1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/html_viewer/html_document.h"
7 #include "base/command_line.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "components/html_viewer/blink_url_request_type_converters.h"
14 #include "components/html_viewer/devtools_agent_impl.h"
15 #include "components/html_viewer/document_resource_waiter.h"
16 #include "components/html_viewer/global_state.h"
17 #include "components/html_viewer/html_frame.h"
18 #include "components/html_viewer/html_frame_tree_manager.h"
19 #include "components/html_viewer/test_html_viewer_impl.h"
20 #include "components/html_viewer/web_url_loader_impl.h"
21 #include "components/mus/ids.h"
22 #include "components/mus/public/cpp/view.h"
23 #include "components/mus/public/cpp/view_tree_connection.h"
24 #include "mojo/application/public/cpp/application_impl.h"
25 #include "mojo/application/public/cpp/connect.h"
26 #include "mojo/application/public/interfaces/shell.mojom.h"
27 #include "mojo/converters/geometry/geometry_type_converters.h"
28 #include "third_party/WebKit/public/web/WebLocalFrame.h"
29 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
30 #include "ui/gfx/geometry/dip_util.h"
31 #include "ui/gfx/geometry/size.h"
33 using mojo::AxProvider
;
36 namespace html_viewer
{
39 const char kEnableTestInterface
[] = "enable-html-viewer-test-interface";
41 bool IsTestInterfaceEnabled() {
42 return base::CommandLine::ForCurrentProcess()->HasSwitch(
43 kEnableTestInterface
);
48 // A ViewTreeDelegate implementation that delegates to a (swappable) delegate.
49 // This is used when one HTMLDocument takes over for another delegate
51 class ViewTreeDelegateImpl
: public mus::ViewTreeDelegate
{
53 explicit ViewTreeDelegateImpl(mus::ViewTreeDelegate
* delegate
)
54 : delegate_(delegate
) {}
55 ~ViewTreeDelegateImpl() override
{}
57 void set_delegate(mus::ViewTreeDelegate
* delegate
) { delegate_
= delegate
; }
61 void OnEmbed(mus::View
* root
) override
{ delegate_
->OnEmbed(root
); }
62 void OnUnembed() override
{ delegate_
->OnUnembed(); }
63 void OnConnectionLost(mus::ViewTreeConnection
* connection
) override
{
64 delegate_
->OnConnectionLost(connection
);
67 mus::ViewTreeDelegate
* delegate_
;
69 DISALLOW_COPY_AND_ASSIGN(ViewTreeDelegateImpl
);
72 HTMLDocument::BeforeLoadCache::BeforeLoadCache() {}
74 HTMLDocument::BeforeLoadCache::~BeforeLoadCache() {
75 STLDeleteElements(&ax_provider_requests
);
76 STLDeleteElements(&test_interface_requests
);
79 HTMLDocument::TransferableState::TransferableState()
80 : owns_view_tree_connection(false), root(nullptr) {}
82 HTMLDocument::TransferableState::~TransferableState() {}
84 void HTMLDocument::TransferableState::Move(TransferableState
* other
) {
85 owns_view_tree_connection
= other
->owns_view_tree_connection
;
87 view_tree_delegate_impl
= other
->view_tree_delegate_impl
.Pass();
89 other
->root
= nullptr;
90 other
->owns_view_tree_connection
= false;
93 HTMLDocument::HTMLDocument(mojo::ApplicationImpl
* html_document_app
,
94 mojo::ApplicationConnection
* connection
,
95 mojo::URLResponsePtr response
,
96 GlobalState
* global_state
,
97 const DeleteCallback
& delete_callback
,
99 : app_refcount_(html_document_app
->app_lifetime_helper()
100 ->CreateAppRefCount()),
101 html_document_app_(html_document_app
),
102 connection_(connection
),
103 global_state_(global_state
),
105 delete_callback_(delete_callback
),
107 connection
->AddService
<web_view::mojom::FrameClient
>(this);
108 connection
->AddService
<AxProvider
>(this);
109 connection
->AddService
<mojo::ViewTreeClient
>(this);
110 connection
->AddService
<devtools_service::DevToolsAgent
>(this);
111 if (IsTestInterfaceEnabled())
112 connection
->AddService
<TestHTMLViewer
>(this);
114 resource_waiter_
.reset(
115 new DocumentResourceWaiter(global_state_
, response
.Pass(), this));
118 void HTMLDocument::Destroy() {
119 if (resource_waiter_
) {
120 mus::View
* root
= resource_waiter_
->root();
122 resource_waiter_
.reset();
123 delete root
->connection();
128 // Closing the frame ends up destroying the ViewManager, which triggers
129 // deleting this (OnConnectionLost()).
131 } else if (transferable_state_
.root
) {
132 // This triggers deleting us.
133 if (transferable_state_
.owns_view_tree_connection
)
134 delete transferable_state_
.root
->connection();
142 HTMLDocument::~HTMLDocument() {
143 delete_callback_
.Run(this);
145 STLDeleteElements(&ax_providers_
);
148 void HTMLDocument::Load() {
149 DCHECK(resource_waiter_
&& resource_waiter_
->is_ready());
151 // Note: |view| is null if we're taking over for an existing frame.
152 mus::View
* view
= resource_waiter_
->root();
154 global_state_
->InitIfNecessary(
155 view
->viewport_metrics().size_in_pixels
.To
<gfx::Size
>(),
156 view
->viewport_metrics().device_pixel_ratio
);
159 scoped_ptr
<WebURLRequestExtraData
> extra_data(new WebURLRequestExtraData
);
160 extra_data
->synthetic_response
=
161 resource_waiter_
->ReleaseURLResponse().Pass();
163 frame_
= HTMLFrameTreeManager::CreateFrameAndAttachToTree(
164 global_state_
, view
, resource_waiter_
.Pass(), this);
166 // If the frame wasn't created we can destroy ourself.
172 if (devtools_agent_request_
.is_pending()) {
173 if (frame_
->devtools_agent()) {
174 frame_
->devtools_agent()->BindToRequest(devtools_agent_request_
.Pass());
176 devtools_agent_request_
=
177 mojo::InterfaceRequest
<devtools_service::DevToolsAgent
>();
181 const GURL
url(extra_data
->synthetic_response
->url
);
183 blink::WebURLRequest web_request
;
184 web_request
.initialize();
185 web_request
.setURL(url
);
186 web_request
.setExtraData(extra_data
.release());
188 frame_
->web_frame()->toWebLocalFrame()->loadRequest(web_request
);
191 HTMLDocument::BeforeLoadCache
* HTMLDocument::GetBeforeLoadCache() {
192 CHECK(!did_finish_local_frame_load_
);
193 if (!before_load_cache_
.get())
194 before_load_cache_
.reset(new BeforeLoadCache
);
195 return before_load_cache_
.get();
198 void HTMLDocument::OnEmbed(View
* root
) {
199 transferable_state_
.root
= root
;
200 resource_waiter_
->SetRoot(root
);
203 void HTMLDocument::OnConnectionLost(mus::ViewTreeConnection
* connection
) {
207 void HTMLDocument::OnFrameDidFinishLoad() {
208 did_finish_local_frame_load_
= true;
209 scoped_ptr
<BeforeLoadCache
> before_load_cache
= before_load_cache_
.Pass();
210 if (!before_load_cache
)
213 // Bind any pending AxProvider and TestHTMLViewer interface requests.
214 for (auto it
: before_load_cache
->ax_provider_requests
) {
215 ax_providers_
.insert(new AxProviderImpl(
216 frame_
->frame_tree_manager()->GetWebView(), it
->Pass()));
218 for (auto it
: before_load_cache
->test_interface_requests
) {
219 CHECK(IsTestInterfaceEnabled());
220 test_html_viewers_
.push_back(new TestHTMLViewerImpl(
221 frame_
->web_frame()->toWebLocalFrame(), it
->Pass()));
225 mojo::ApplicationImpl
* HTMLDocument::GetApp() {
226 return html_document_app_
;
229 HTMLFactory
* HTMLDocument::GetHTMLFactory() {
233 void HTMLDocument::OnFrameSwappedToRemote() {
234 // When the frame becomes remote HTMLDocument is no longer needed.
239 void HTMLDocument::OnSwap(HTMLFrame
* frame
, HTMLFrameDelegate
* old_delegate
) {
240 DCHECK(frame
->IsLocal());
241 DCHECK(frame
->view());
243 DCHECK(!transferable_state_
.root
);
245 // We're taking over a child of a local root that isn't associated with a
246 // delegate. In this case the frame's view is not the root of the
247 // ViewTreeConnection.
248 transferable_state_
.owns_view_tree_connection
= false;
249 transferable_state_
.root
= frame
->view();
251 HTMLDocument
* old_document
= static_cast<HTMLDocument
*>(old_delegate
);
252 transferable_state_
.Move(&old_document
->transferable_state_
);
253 if (transferable_state_
.view_tree_delegate_impl
)
254 transferable_state_
.view_tree_delegate_impl
->set_delegate(this);
255 old_document
->frame_
= nullptr;
256 old_document
->Destroy();
260 void HTMLDocument::OnFrameDestroyed() {
261 if (!transferable_state_
.owns_view_tree_connection
)
265 void HTMLDocument::Create(mojo::ApplicationConnection
* connection
,
266 mojo::InterfaceRequest
<AxProvider
> request
) {
267 if (!did_finish_local_frame_load_
) {
268 // Cache AxProvider interface requests until the document finishes loading.
269 auto cached_request
= new mojo::InterfaceRequest
<AxProvider
>();
270 *cached_request
= request
.Pass();
271 GetBeforeLoadCache()->ax_provider_requests
.insert(cached_request
);
273 ax_providers_
.insert(
274 new AxProviderImpl(frame_
->web_view(), request
.Pass()));
278 void HTMLDocument::Create(mojo::ApplicationConnection
* connection
,
279 mojo::InterfaceRequest
<TestHTMLViewer
> request
) {
280 CHECK(IsTestInterfaceEnabled());
281 if (!did_finish_local_frame_load_
) {
282 auto cached_request
= new mojo::InterfaceRequest
<TestHTMLViewer
>();
283 *cached_request
= request
.Pass();
284 GetBeforeLoadCache()->test_interface_requests
.insert(cached_request
);
286 test_html_viewers_
.push_back(new TestHTMLViewerImpl(
287 frame_
->web_frame()->toWebLocalFrame(), request
.Pass()));
291 void HTMLDocument::Create(
292 mojo::ApplicationConnection
* connection
,
293 mojo::InterfaceRequest
<web_view::mojom::FrameClient
> request
) {
295 DVLOG(1) << "Request for FrameClient after one already vended.";
298 resource_waiter_
->Bind(request
.Pass());
301 void HTMLDocument::Create(
302 mojo::ApplicationConnection
* connection
,
303 mojo::InterfaceRequest
<devtools_service::DevToolsAgent
> request
) {
305 if (frame_
->devtools_agent())
306 frame_
->devtools_agent()->BindToRequest(request
.Pass());
308 devtools_agent_request_
= request
.Pass();
312 void HTMLDocument::Create(
313 mojo::ApplicationConnection
* connection
,
314 mojo::InterfaceRequest
<mojo::ViewTreeClient
> request
) {
315 DCHECK(!transferable_state_
.view_tree_delegate_impl
);
316 transferable_state_
.view_tree_delegate_impl
.reset(
317 new ViewTreeDelegateImpl(this));
318 transferable_state_
.owns_view_tree_connection
= true;
319 mus::ViewTreeConnection::Create(
320 transferable_state_
.view_tree_delegate_impl
.get(), request
.Pass());
323 } // namespace html_viewer