1 // Copyright 2015 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/frame.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "components/html_viewer/ax_provider_impl.h"
15 #include "components/html_viewer/blink_input_events_type_converters.h"
16 #include "components/html_viewer/blink_url_request_type_converters.h"
17 #include "components/html_viewer/frame_tree_manager.h"
18 #include "components/html_viewer/global_state.h"
19 #include "components/html_viewer/media_factory.h"
20 #include "components/html_viewer/touch_handler.h"
21 #include "components/html_viewer/web_layer_tree_view_impl.h"
22 #include "components/html_viewer/web_storage_namespace_impl.h"
23 #include "components/html_viewer/web_url_loader_impl.h"
24 #include "components/view_manager/ids.h"
25 #include "components/view_manager/public/cpp/view.h"
26 #include "components/view_manager/public/cpp/view_manager.h"
27 #include "components/view_manager/public/interfaces/surfaces.mojom.h"
28 #include "mandoline/services/navigation/public/interfaces/navigation.mojom.h"
29 #include "mojo/application/public/cpp/application_impl.h"
30 #include "mojo/application/public/cpp/connect.h"
31 #include "mojo/application/public/interfaces/shell.mojom.h"
32 #include "mojo/converters/geometry/geometry_type_converters.h"
33 #include "skia/ext/refptr.h"
34 #include "third_party/WebKit/public/platform/Platform.h"
35 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
36 #include "third_party/WebKit/public/platform/WebSize.h"
37 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
38 #include "third_party/WebKit/public/web/WebDocument.h"
39 #include "third_party/WebKit/public/web/WebElement.h"
40 #include "third_party/WebKit/public/web/WebFrameWidget.h"
41 #include "third_party/WebKit/public/web/WebInputEvent.h"
42 #include "third_party/WebKit/public/web/WebLocalFrame.h"
43 #include "third_party/WebKit/public/web/WebNavigationPolicy.h"
44 #include "third_party/WebKit/public/web/WebRemoteFrame.h"
45 #include "third_party/WebKit/public/web/WebRemoteFrameClient.h"
46 #include "third_party/WebKit/public/web/WebScriptSource.h"
47 #include "third_party/WebKit/public/web/WebSettings.h"
48 #include "third_party/WebKit/public/web/WebView.h"
49 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
50 #include "third_party/skia/include/core/SkCanvas.h"
51 #include "third_party/skia/include/core/SkColor.h"
52 #include "third_party/skia/include/core/SkDevice.h"
53 #include "ui/gfx/geometry/dip_util.h"
54 #include "ui/gfx/geometry/size.h"
56 using mojo::AxProvider
;
58 using mojo::ServiceProviderPtr
;
59 using mojo::URLResponsePtr
;
61 using mojo::WeakBindToRequest
;
63 namespace html_viewer
{
66 void ConfigureSettings(blink::WebSettings
* settings
) {
67 settings
->setCookieEnabled(true);
68 settings
->setDefaultFixedFontSize(13);
69 settings
->setDefaultFontSize(16);
70 settings
->setLoadsImagesAutomatically(true);
71 settings
->setJavaScriptEnabled(true);
74 Frame
* GetPreviousSibling(Frame
* frame
) {
75 DCHECK(frame
->parent());
76 auto iter
= std::find(frame
->parent()->children().begin(),
77 frame
->parent()->children().end(), frame
);
78 return (iter
== frame
->parent()->children().begin()) ? nullptr : *(--iter
);
83 Frame::Frame(const Frame::CreateParams
& params
)
84 : frame_tree_manager_(params
.manager
),
85 parent_(params
.parent
),
90 scope_(blink::WebTreeScopeType::Document
),
93 parent_
->children_
.push_back(this);
96 void Frame::Init(mojo::View
* local_view
) {
97 // TODO(sky): need to plumb through scope, name and other args correctly for
101 // This is the root of the tree (aka the main frame).
102 // Expected order for creating webframes is:
103 // . Create local webframe (first webframe must always be local).
104 // . Set as main frame on WebView.
105 // . Swap to remote (if not local).
106 blink::WebLocalFrame
* local_web_frame
=
107 blink::WebLocalFrame::create(blink::WebTreeScopeType::Document
, this);
108 // We need to set the main frame before creating children so that state is
109 // properly set up in blink.
110 // TODO(sky): I don't like these casts.
111 web_view()->setMainFrame(local_web_frame
);
112 const gfx::Size
size_in_pixels(local_view
->bounds().width
,
113 local_view
->bounds().height
);
114 const gfx::Size size_in_dips
= gfx::ConvertSizeToDIP(
115 local_view
->viewport_metrics().device_pixel_ratio
, size_in_pixels
);
116 web_widget_
->resize(size_in_dips
);
117 web_frame_
= local_web_frame
;
118 web_view()->setDeviceScaleFactor(global_state()->device_pixel_ratio());
119 if (id_
!= local_view
->id()) {
120 blink::WebRemoteFrame
* remote_web_frame
= blink::WebRemoteFrame::create(
121 blink::WebTreeScopeType::Document
, this);
122 local_web_frame
->swap(remote_web_frame
);
123 // local_web_frame->close();
124 web_frame_
= remote_web_frame
;
125 // TODO(sky): This needs to come from FrameTreeData.
126 remote_web_frame
->setReplicatedOrigin(
127 blink::WebSecurityOrigin::createFromString(""));
129 } else if (id_
== local_view
->id()) {
130 // Frame represents the local frame.
131 Frame
* previous_sibling
= GetPreviousSibling(this);
132 blink::WebFrame
* previous_web_frame
=
133 previous_sibling
? previous_sibling
->web_frame() : nullptr;
134 DCHECK(!parent_
->IsLocal());
135 web_frame_
= parent_
->web_frame()->toWebRemoteFrame()->createLocalChild(
136 blink::WebTreeScopeType::Document
, "", blink::WebSandboxFlags::None
,
137 this, previous_web_frame
);
139 } else if (parent_
->web_frame()->isWebLocalFrame()) {
140 blink::WebLocalFrame
* local_web_frame
=
141 blink::WebLocalFrame::create(blink::WebTreeScopeType::Document
, this);
142 parent_
->web_frame()->appendChild(local_web_frame
);
143 blink::WebRemoteFrame
* remote_web_frame
=
144 blink::WebRemoteFrame::create(blink::WebTreeScopeType::Document
, this);
145 // remote_web_frame->swap(local_web_frame);
146 local_web_frame
->close();
147 web_frame_
= remote_web_frame
;
149 web_frame_
= parent_
->web_frame()->toWebRemoteFrame()->createRemoteChild(
150 blink::WebTreeScopeType::Document
, "", blink::WebSandboxFlags::None
,
155 void Frame::Close() {
157 // Closing the widget implicitly detaches the frame.
158 web_widget_
->close();
160 web_frame_
->detach();
164 const Frame
* Frame::FindFrame(uint32_t id
) const {
168 for (const Frame
* child
: children_
) {
169 const Frame
* match
= child
->FindFrame(id
);
176 blink::WebView
* Frame::web_view() {
177 return web_widget_
&& web_widget_
->isWebView()
178 ? static_cast<blink::WebView
*>(web_widget_
)
183 DCHECK(children_
.empty());
187 std::find(parent_
->children_
.begin(), parent_
->children_
.end(), this);
188 parent_
->children_
.erase(iter
);
192 frame_tree_manager_
->OnFrameDestroyed(this);
195 view_
->RemoveObserver(this);
200 bool Frame::IsLocal() const {
201 return web_frame_
->isWebLocalFrame();
204 void Frame::SetView(mojo::View
* view
) {
207 view_
->AddObserver(this);
210 void Frame::CreateWebWidget() {
211 DCHECK(!web_widget_
);
213 // TODO(sky): this isn't quite right. I should only have a WebFrameWidget
214 // for local roots. And the cast to local fram definitely isn't right.
216 blink::WebFrameWidget::create(this, web_frame_
->toWebLocalFrame());
217 } else if (view_
&& view_
->id() == id_
) {
218 web_widget_
= blink::WebView::create(this);
220 web_widget_
= blink::WebView::create(nullptr);
223 // Creating the widget calls initializeLayerTreeView() to create the
224 // |web_layer_tree_view_impl_|. As we haven't yet assigned the |web_widget_|
225 // we have to set it here.
226 if (web_layer_tree_view_impl_
) {
227 web_layer_tree_view_impl_
->set_widget(web_widget_
);
228 web_layer_tree_view_impl_
->set_view(view_
);
229 UpdateWebViewSizeFromViewSize();
233 ConfigureSettings(web_view()->settings());
236 void Frame::UpdateFocus() {
237 if (!web_widget_
|| !view_
)
239 const bool is_focused
= view_
&& view_
->HasFocus();
240 web_widget_
->setFocus(is_focused
);
241 if (web_widget_
->isWebView())
242 static_cast<blink::WebView
*>(web_widget_
)->setIsActive(is_focused
);
245 void Frame::UpdateWebViewSizeFromViewSize() {
246 if (!web_widget_
|| !view_
)
249 const gfx::Size
size_in_pixels(view_
->bounds().width
, view_
->bounds().height
);
250 const gfx::Size size_in_dips
= gfx::ConvertSizeToDIP(
251 view_
->viewport_metrics().device_pixel_ratio
, size_in_pixels
);
253 blink::WebSize(size_in_dips
.width(), size_in_dips
.height()));
254 web_layer_tree_view_impl_
->setViewportSize(size_in_pixels
);
257 void Frame::SwapToRemote(const blink::WebURLRequest
& request
) {
259 mojo::URLRequestPtr url_request
= mojo::URLRequest::From(request
);
260 view_
->EmbedAllowingReembed(url_request
.Pass());
262 // TODO(sky): I tried swapping the frame types here, but that resulted in
263 // the view never getting sized. Figure out why.
264 base::MessageLoop::current()->PostTask(
266 base::Bind(&Frame::FinishSwapToRemote
, weak_factory_
.GetWeakPtr()));
269 void Frame::FinishSwapToRemote() {
270 if (web_frame_
->isWebRemoteFrame())
271 return; // We already did the swap.
273 blink::WebRemoteFrame
* remote_frame
=
274 blink::WebRemoteFrame::create(scope_
, this);
275 remote_frame
->initializeFromFrame(web_frame_
->toWebLocalFrame());
276 // swap() ends up calling us back and we then close the frame.
277 web_frame_
->swap(remote_frame
);
278 web_frame_
= remote_frame
;
281 Frame
* Frame::FindFrameWithWebFrame(blink::WebFrame
* web_frame
) {
282 if (web_frame_
== web_frame
)
284 for (Frame
* child_frame
: children_
) {
285 Frame
* result
= child_frame
->FindFrameWithWebFrame(web_frame
);
292 void Frame::FrameDetachedImpl(blink::WebFrame
* web_frame
) {
293 DCHECK_EQ(web_frame_
, web_frame
);
295 while (!children_
.empty()) {
296 Frame
* child
= children_
.front();
298 DCHECK(children_
.empty() || children_
.front() != child
);
301 if (web_frame
->parent())
302 web_frame
->parent()->removeChild(web_frame
);
307 void Frame::OnViewBoundsChanged(View
* view
,
308 const Rect
& old_bounds
,
309 const Rect
& new_bounds
) {
310 DCHECK_EQ(view
, view_
);
311 UpdateWebViewSizeFromViewSize();
314 void Frame::OnViewDestroyed(View
* view
) {
315 DCHECK_EQ(view
, view_
);
316 view_
->RemoveObserver(this);
321 void Frame::OnViewInputEvent(View
* view
, const mojo::EventPtr
& event
) {
322 if (event
->pointer_data
) {
323 // Blink expects coordintes to be in DIPs.
324 event
->pointer_data
->x
/= global_state()->device_pixel_ratio();
325 event
->pointer_data
->y
/= global_state()->device_pixel_ratio();
326 event
->pointer_data
->screen_x
/= global_state()->device_pixel_ratio();
327 event
->pointer_data
->screen_y
/= global_state()->device_pixel_ratio();
330 if (!touch_handler_
&& web_widget_
)
331 touch_handler_
.reset(new TouchHandler(web_widget_
));
333 if ((event
->action
== mojo::EVENT_TYPE_POINTER_DOWN
||
334 event
->action
== mojo::EVENT_TYPE_POINTER_UP
||
335 event
->action
== mojo::EVENT_TYPE_POINTER_CANCEL
||
336 event
->action
== mojo::EVENT_TYPE_POINTER_MOVE
) &&
337 event
->pointer_data
->kind
== mojo::POINTER_KIND_TOUCH
) {
338 touch_handler_
->OnTouchEvent(*event
);
345 scoped_ptr
<blink::WebInputEvent
> web_event
=
346 event
.To
<scoped_ptr
<blink::WebInputEvent
>>();
348 web_widget_
->handleInputEvent(*web_event
);
351 void Frame::OnViewFocusChanged(mojo::View
* gained_focus
,
352 mojo::View
* lost_focus
) {
356 void Frame::initializeLayerTreeView() {
357 mojo::URLRequestPtr
request(mojo::URLRequest::New());
358 request
->url
= mojo::String::From("mojo:surfaces_service");
359 mojo::SurfacePtr surface
;
360 frame_tree_manager_
->app()->ConnectToService(request
.Pass(), &surface
);
362 // TODO(jamesr): Should be mojo:gpu_service
363 mojo::URLRequestPtr
request2(mojo::URLRequest::New());
364 request2
->url
= mojo::String::From("mojo:view_manager");
365 mojo::GpuPtr gpu_service
;
366 frame_tree_manager_
->app()->ConnectToService(request2
.Pass(), &gpu_service
);
367 web_layer_tree_view_impl_
.reset(new WebLayerTreeViewImpl(
368 global_state()->compositor_thread(),
369 global_state()->gpu_memory_buffer_manager(),
370 global_state()->raster_thread_helper()->task_graph_runner(),
371 surface
.Pass(), gpu_service
.Pass()));
374 blink::WebLayerTreeView
* Frame::layerTreeView() {
375 return web_layer_tree_view_impl_
.get();
378 blink::WebStorageNamespace
* Frame::createSessionStorageNamespace() {
379 return new WebStorageNamespaceImpl();
382 blink::WebMediaPlayer
* Frame::createMediaPlayer(
383 blink::WebLocalFrame
* frame
,
384 const blink::WebURL
& url
,
385 blink::WebMediaPlayerClient
* client
,
386 blink::WebContentDecryptionModule
* initial_cdm
) {
387 return global_state()->media_factory()->CreateMediaPlayer(
388 frame
, url
, client
, initial_cdm
, frame_tree_manager_
->app()->shell());
391 blink::WebFrame
* Frame::createChildFrame(blink::WebLocalFrame
* parent
,
392 blink::WebTreeScopeType scope
,
393 const blink::WebString
& frame_name
,
394 blink::WebSandboxFlags sandbox_flags
) {
395 DCHECK(IsLocal()); // Can't create children of remote frames.
396 DCHECK_EQ(parent
, web_frame_
);
397 DCHECK(view_
); // If we're local we have to have a view.
398 // Create the view that will house the frame now. We embed once we know the
399 // url (see decidePolicyForNavigation()).
400 mojo::View
* child_view
= view_
->view_manager()->CreateView();
401 child_view
->SetVisible(true);
402 view_
->AddChild(child_view
);
404 // TODO(sky): the act of creating needs to notify the browser side, not
406 Frame::CreateParams
params(frame_tree_manager_
, this, child_view
->id());
407 Frame
* child_frame
= new Frame(params
);
408 child_frame
->scope_
= scope
;
410 child_frame
->SetView(child_view
);
412 blink::WebLocalFrame
* child_web_frame
=
413 blink::WebLocalFrame::create(scope
, child_frame
);
414 child_frame
->web_frame_
= child_web_frame
;
415 parent
->appendChild(child_web_frame
);
416 return child_web_frame
;
419 void Frame::frameDetached(blink::WebFrame
* frame
) {
420 frameDetached(frame
, blink::WebFrameClient::DetachType::Remove
);
423 void Frame::frameDetached(blink::WebFrame
* web_frame
,
424 blink::WebFrameClient::DetachType type
) {
425 if (type
== blink::WebFrameClient::DetachType::Swap
) {
430 DCHECK(type
== blink::WebFrameClient::DetachType::Remove
);
431 FrameDetachedImpl(web_frame
);
434 blink::WebCookieJar
* Frame::cookieJar(blink::WebLocalFrame
* frame
) {
435 // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
436 // Either it should, as it once did, or we should find another solution here.
437 return blink::Platform::current()->cookieJar();
440 blink::WebNavigationPolicy
Frame::decidePolicyForNavigation(
441 const NavigationPolicyInfo
& info
) {
442 if (parent_
&& parent_
->IsLocal()) {
443 SwapToRemote(info
.urlRequest
);
444 return blink::WebNavigationPolicyIgnore
;
447 return frame_tree_manager_
->DecidePolicyForNavigation(this, info
);
450 void Frame::didAddMessageToConsole(const blink::WebConsoleMessage
& message
,
451 const blink::WebString
& source_name
,
452 unsigned source_line
,
453 const blink::WebString
& stack_trace
) {
454 VLOG(1) << "[" << source_name
.utf8() << "(" << source_line
<< ")] "
455 << message
.text
.utf8();
458 void Frame::didFinishLoad(blink::WebLocalFrame
* frame
) {
459 frame_tree_manager_
->OnFrameDidFinishLoad(this);
462 void Frame::didNavigateWithinPage(blink::WebLocalFrame
* frame
,
463 const blink::WebHistoryItem
& history_item
,
464 blink::WebHistoryCommitType commit_type
) {
465 frame_tree_manager_
->OnFrameDidNavigateLocally(
466 this, history_item
.urlString().utf8());
469 blink::WebEncryptedMediaClient
* Frame::encryptedMediaClient() {
470 return global_state()->media_factory()->GetEncryptedMediaClient();
473 void Frame::frameDetached(blink::WebRemoteFrameClient::DetachType type
) {
474 if (type
== blink::WebRemoteFrameClient::DetachType::Swap
) {
479 DCHECK(type
== blink::WebRemoteFrameClient::DetachType::Remove
);
480 FrameDetachedImpl(web_frame_
);
483 void Frame::postMessageEvent(blink::WebLocalFrame
* source_frame
,
484 blink::WebRemoteFrame
* target_frame
,
485 blink::WebSecurityOrigin target_origin
,
486 blink::WebDOMMessageEvent event
) {
490 void Frame::initializeChildFrame(const blink::WebRect
& frame_rect
,
491 float scale_factor
) {
492 // TODO(sky): frame_rect is in dips. Need to convert.
494 rect
.x
= frame_rect
.x
;
495 rect
.y
= frame_rect
.y
;
496 rect
.width
= frame_rect
.width
;
497 rect
.height
= frame_rect
.height
;
498 view_
->SetBounds(rect
);
501 void Frame::navigate(const blink::WebURLRequest
& request
,
502 bool should_replace_current_entry
) {
506 void Frame::reload(bool ignore_cache
, bool is_client_redirect
) {
510 void Frame::forwardInputEvent(const blink::WebInputEvent
* event
) {