Don't preload rarely seen large images
[chromium-blink-merge.git] / components / html_viewer / frame.cc
blobc5ee687a0d7cc12761bf4173257c6ff0ec126d41
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"
7 #include <algorithm>
9 #include "base/bind.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;
57 using mojo::Rect;
58 using mojo::ServiceProviderPtr;
59 using mojo::URLResponsePtr;
60 using mojo::View;
61 using mojo::WeakBindToRequest;
63 namespace html_viewer {
64 namespace {
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);
81 } // namespace
83 Frame::Frame(const Frame::CreateParams& params)
84 : frame_tree_manager_(params.manager),
85 parent_(params.parent),
86 view_(nullptr),
87 id_(params.id),
88 web_frame_(nullptr),
89 web_widget_(nullptr),
90 scope_(blink::WebTreeScopeType::Document),
91 weak_factory_(this) {
92 if (parent_)
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
98 // frame creation.
99 if (!parent_) {
100 CreateWebWidget();
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);
138 CreateWebWidget();
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;
148 } else {
149 web_frame_ = parent_->web_frame()->toWebRemoteFrame()->createRemoteChild(
150 blink::WebTreeScopeType::Document, "", blink::WebSandboxFlags::None,
151 this);
155 void Frame::Close() {
156 if (web_widget_) {
157 // Closing the widget implicitly detaches the frame.
158 web_widget_->close();
159 } else {
160 web_frame_->detach();
164 const Frame* Frame::FindFrame(uint32_t id) const {
165 if (id == id_)
166 return this;
168 for (const Frame* child : children_) {
169 const Frame* match = child->FindFrame(id);
170 if (match)
171 return match;
173 return nullptr;
176 blink::WebView* Frame::web_view() {
177 return web_widget_ && web_widget_->isWebView()
178 ? static_cast<blink::WebView*>(web_widget_)
179 : nullptr;
182 Frame::~Frame() {
183 DCHECK(children_.empty());
185 if (parent_) {
186 auto iter =
187 std::find(parent_->children_.begin(), parent_->children_.end(), this);
188 parent_->children_.erase(iter);
190 parent_ = nullptr;
192 frame_tree_manager_->OnFrameDestroyed(this);
194 if (view_) {
195 view_->RemoveObserver(this);
196 view_->Destroy();
200 bool Frame::IsLocal() const {
201 return web_frame_->isWebLocalFrame();
204 void Frame::SetView(mojo::View* view) {
205 DCHECK(!view_);
206 view_ = view;
207 view_->AddObserver(this);
210 void Frame::CreateWebWidget() {
211 DCHECK(!web_widget_);
212 if (parent_) {
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.
215 web_widget_ =
216 blink::WebFrameWidget::create(this, web_frame_->toWebLocalFrame());
217 } else if (view_ && view_->id() == id_) {
218 web_widget_ = blink::WebView::create(this);
219 } else {
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();
232 if (web_view())
233 ConfigureSettings(web_view()->settings());
236 void Frame::UpdateFocus() {
237 if (!web_widget_ || !view_)
238 return;
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_)
247 return;
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);
252 web_widget_->resize(
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) {
258 DCHECK(IsLocal());
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(
265 FROM_HERE,
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)
283 return this;
284 for (Frame* child_frame : children_) {
285 Frame* result = child_frame->FindFrameWithWebFrame(web_frame);
286 if (result)
287 return result;
289 return nullptr;
292 void Frame::FrameDetachedImpl(blink::WebFrame* web_frame) {
293 DCHECK_EQ(web_frame_, web_frame);
295 while (!children_.empty()) {
296 Frame* child = children_.front();
297 child->Close();
298 DCHECK(children_.empty() || children_.front() != child);
301 if (web_frame->parent())
302 web_frame->parent()->removeChild(web_frame);
304 delete this;
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);
317 view_ = nullptr;
318 Close();
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);
339 return;
342 if (!web_widget_)
343 return;
345 scoped_ptr<blink::WebInputEvent> web_event =
346 event.To<scoped_ptr<blink::WebInputEvent>>();
347 if (web_event)
348 web_widget_->handleInputEvent(*web_event);
351 void Frame::OnViewFocusChanged(mojo::View* gained_focus,
352 mojo::View* lost_focus) {
353 UpdateFocus();
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
405 // navigation.
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) {
426 web_frame->close();
427 return;
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) {
475 web_frame_->close();
476 return;
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) {
487 NOTIMPLEMENTED();
490 void Frame::initializeChildFrame(const blink::WebRect& frame_rect,
491 float scale_factor) {
492 // TODO(sky): frame_rect is in dips. Need to convert.
493 mojo::Rect rect;
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) {
503 NOTIMPLEMENTED();
506 void Frame::reload(bool ignore_cache, bool is_client_redirect) {
507 NOTIMPLEMENTED();
510 void Frame::forwardInputEvent(const blink::WebInputEvent* event) {
511 NOTIMPLEMENTED();
514 } // namespace mojo