Clean up extension confirmation prompts and make them consistent between Views and...
[chromium-blink-merge.git] / components / html_viewer / html_document.cc
blobcbd06c97cc3ac71f69b3541de7f5811e36670acf
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/bind.h"
8 #include "base/command_line.h"
9 #include "base/location.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "components/devtools_service/public/cpp/switches.h"
16 #include "components/html_viewer/blink_input_events_type_converters.h"
17 #include "components/html_viewer/blink_url_request_type_converters.h"
18 #include "components/html_viewer/devtools_agent_impl.h"
19 #include "components/html_viewer/media_factory.h"
20 #include "components/html_viewer/setup.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/public/cpp/view.h"
25 #include "components/view_manager/public/cpp/view_manager.h"
26 #include "components/view_manager/public/cpp/view_property.h"
27 #include "components/view_manager/public/interfaces/surfaces.mojom.h"
28 #include "mojo/application/public/cpp/application_impl.h"
29 #include "mojo/application/public/cpp/connect.h"
30 #include "mojo/application/public/interfaces/shell.mojom.h"
31 #include "mojo/converters/geometry/geometry_type_converters.h"
32 #include "skia/ext/refptr.h"
33 #include "third_party/WebKit/public/platform/Platform.h"
34 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
35 #include "third_party/WebKit/public/platform/WebSize.h"
36 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
37 #include "third_party/WebKit/public/web/WebDocument.h"
38 #include "third_party/WebKit/public/web/WebElement.h"
39 #include "third_party/WebKit/public/web/WebInputEvent.h"
40 #include "third_party/WebKit/public/web/WebLocalFrame.h"
41 #include "third_party/WebKit/public/web/WebRemoteFrame.h"
42 #include "third_party/WebKit/public/web/WebRemoteFrameClient.h"
43 #include "third_party/WebKit/public/web/WebScriptSource.h"
44 #include "third_party/WebKit/public/web/WebSettings.h"
45 #include "third_party/WebKit/public/web/WebView.h"
46 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
47 #include "third_party/skia/include/core/SkCanvas.h"
48 #include "third_party/skia/include/core/SkColor.h"
49 #include "third_party/skia/include/core/SkDevice.h"
50 #include "ui/gfx/geometry/dip_util.h"
51 #include "ui/gfx/geometry/size.h"
53 using blink::WebString;
54 using mojo::AxProvider;
55 using mojo::Rect;
56 using mojo::ServiceProviderPtr;
57 using mojo::URLResponsePtr;
58 using mojo::View;
59 using mojo::ViewManager;
60 using mojo::WeakBindToRequest;
62 namespace html_viewer {
63 namespace {
65 // Switch to enable out of process iframes.
66 const char kOOPIF[] = "oopifs";
68 bool EnableOOPIFs() {
69 return base::CommandLine::ForCurrentProcess()->HasSwitch(kOOPIF);
72 bool EnableRemoteDebugging() {
73 return base::CommandLine::ForCurrentProcess()->HasSwitch(
74 devtools_service::kRemoteDebuggingPort);
77 // WebRemoteFrameClient implementation used for OOPIFs.
78 // TODO(sky): this needs to talk to browser by way of an interface.
79 class RemoteFrameClientImpl : public blink::WebRemoteFrameClient {
80 public:
81 explicit RemoteFrameClientImpl(mojo::View* view) : view_(view) {}
82 ~RemoteFrameClientImpl() {}
84 // WebRemoteFrameClient methods:
85 virtual void postMessageEvent(blink::WebLocalFrame* source_frame,
86 blink::WebRemoteFrame* target_frame,
87 blink::WebSecurityOrigin target_origin,
88 blink::WebDOMMessageEvent event) {}
89 virtual void initializeChildFrame(const blink::WebRect& frame_rect,
90 float scale_factor) {
91 mojo::Rect rect;
92 rect.x = frame_rect.x;
93 rect.y = frame_rect.y;
94 rect.width = frame_rect.width;
95 rect.height = frame_rect.height;
96 view_->SetBounds(rect);
98 virtual void navigate(const blink::WebURLRequest& request,
99 bool should_replace_current_entry) {}
100 virtual void reload(bool ignore_cache, bool is_client_redirect) {}
102 virtual void forwardInputEvent(const blink::WebInputEvent* event) {}
104 private:
105 mojo::View* const view_;
107 DISALLOW_COPY_AND_ASSIGN(RemoteFrameClientImpl);
110 void ConfigureSettings(blink::WebSettings* settings) {
111 settings->setCookieEnabled(true);
112 settings->setDefaultFixedFontSize(13);
113 settings->setDefaultFontSize(16);
114 settings->setLoadsImagesAutomatically(true);
115 settings->setJavaScriptEnabled(true);
118 mojo::Target WebNavigationPolicyToNavigationTarget(
119 blink::WebNavigationPolicy policy) {
120 switch (policy) {
121 case blink::WebNavigationPolicyCurrentTab:
122 return mojo::TARGET_SOURCE_NODE;
123 case blink::WebNavigationPolicyNewBackgroundTab:
124 case blink::WebNavigationPolicyNewForegroundTab:
125 case blink::WebNavigationPolicyNewWindow:
126 case blink::WebNavigationPolicyNewPopup:
127 return mojo::TARGET_NEW_NODE;
128 default:
129 return mojo::TARGET_DEFAULT;
133 bool CanNavigateLocally(blink::WebFrame* frame,
134 const blink::WebURLRequest& request) {
135 // For now, we just load child frames locally.
136 // TODO(sky): this can be removed once we transition to oopifs.
137 if (!EnableOOPIFs() && frame->parent())
138 return true;
140 // If we have extraData() it means we already have the url response
141 // (presumably because we are being called via Navigate()). In that case we
142 // can go ahead and navigate locally.
143 if (request.extraData())
144 return true;
146 // Otherwise we don't know if we're the right app to handle this request. Ask
147 // host to do the navigation for us.
148 return false;
151 } // namespace
153 HTMLDocument::HTMLDocument(mojo::ApplicationImpl* html_document_app,
154 mojo::ApplicationConnection* connection,
155 URLResponsePtr response,
156 Setup* setup)
157 : app_refcount_(
158 html_document_app->app_lifetime_helper()->CreateAppRefCount()),
159 html_document_app_(html_document_app),
160 response_(response.Pass()),
161 navigator_host_(connection->GetServiceProvider()),
162 web_view_(nullptr),
163 root_(nullptr),
164 view_manager_client_factory_(html_document_app->shell(), this),
165 setup_(setup),
166 frame_tree_manager_binding_(&frame_tree_manager_) {
167 connection->AddService(
168 static_cast<mojo::InterfaceFactory<mandoline::FrameTreeClient>*>(this));
169 connection->AddService(
170 static_cast<InterfaceFactory<mojo::AxProvider>*>(this));
171 connection->AddService(&view_manager_client_factory_);
173 if (setup_->did_init())
174 Load(response_.Pass());
177 HTMLDocument::~HTMLDocument() {
178 STLDeleteElements(&ax_providers_);
179 STLDeleteElements(&ax_provider_requests_);
181 if (web_view_)
182 web_view_->close();
183 if (root_)
184 root_->RemoveObserver(this);
187 void HTMLDocument::OnEmbed(View* root) {
188 DCHECK(!setup_->is_headless());
189 root_ = root;
190 root_->AddObserver(this);
191 UpdateFocus();
193 InitSetupAndLoadIfNecessary();
196 void HTMLDocument::OnViewManagerDestroyed(ViewManager* view_manager) {
197 delete this;
200 void HTMLDocument::Create(mojo::ApplicationConnection* connection,
201 mojo::InterfaceRequest<AxProvider> request) {
202 if (!did_finish_load_) {
203 // Cache AxProvider interface requests until the document finishes loading.
204 auto cached_request = new mojo::InterfaceRequest<AxProvider>();
205 *cached_request = request.Pass();
206 ax_provider_requests_.insert(cached_request);
207 } else {
208 ax_providers_.insert(
209 new AxProviderImpl(web_view_, request.Pass()));
213 void HTMLDocument::Create(
214 mojo::ApplicationConnection* connection,
215 mojo::InterfaceRequest<mandoline::FrameTreeClient> request) {
216 frame_tree_manager_binding_.Bind(request.Pass());
219 void HTMLDocument::Load(URLResponsePtr response) {
220 DCHECK(!web_view_);
221 web_view_ = blink::WebView::create(this);
222 touch_handler_.reset(new TouchHandler(web_view_));
223 web_layer_tree_view_impl_->set_widget(web_view_);
224 ConfigureSettings(web_view_->settings());
226 blink::WebLocalFrame* main_frame =
227 blink::WebLocalFrame::create(blink::WebTreeScopeType::Document, this);
228 web_view_->setMainFrame(main_frame);
230 // TODO(yzshen): http://crbug.com/498986 Creating DevToolsAgentImpl instances
231 // causes html_viewer_apptests flakiness currently. Before we fix that we
232 // cannot enable remote debugging (which is required by Telemetry tests) on
233 // the bots.
234 if (EnableRemoteDebugging()) {
235 devtools_agent_.reset(
236 new DevToolsAgentImpl(main_frame, html_document_app_->shell()));
239 GURL url(response->url);
241 WebURLRequestExtraData* extra_data = new WebURLRequestExtraData;
242 extra_data->synthetic_response = response.Pass();
244 blink::WebURLRequest web_request;
245 web_request.initialize();
246 web_request.setURL(url);
247 web_request.setExtraData(extra_data);
249 web_view_->mainFrame()->loadRequest(web_request);
250 UpdateFocus();
253 void HTMLDocument::ConvertLocalFrameToRemoteFrame(blink::WebLocalFrame* frame) {
254 mojo::View* view = frame_to_view_[frame].view;
255 // TODO(sky): this leaks. Fix it.
256 blink::WebRemoteFrame* remote_frame = blink::WebRemoteFrame::create(
257 frame_to_view_[frame].scope, new RemoteFrameClientImpl(view));
258 remote_frame->initializeFromFrame(frame);
259 frame->swap(remote_frame);
262 void HTMLDocument::UpdateWebviewSizeFromViewSize() {
263 web_view_->setDeviceScaleFactor(setup_->device_pixel_ratio());
264 const gfx::Size size_in_pixels(root_->bounds().width, root_->bounds().height);
265 const gfx::Size size_in_dips = gfx::ConvertSizeToDIP(
266 root_->viewport_metrics().device_pixel_ratio, size_in_pixels);
267 web_view_->resize(
268 blink::WebSize(size_in_dips.width(), size_in_dips.height()));
269 web_layer_tree_view_impl_->setViewportSize(size_in_pixels);
272 void HTMLDocument::InitSetupAndLoadIfNecessary() {
273 DCHECK(root_);
274 if (root_->viewport_metrics().device_pixel_ratio == 0.f)
275 return;
277 if (!web_view_) {
278 setup_->InitIfNecessary(
279 root_->viewport_metrics().size_in_pixels.To<gfx::Size>(),
280 root_->viewport_metrics().device_pixel_ratio);
281 Load(response_.Pass());
284 UpdateWebviewSizeFromViewSize();
285 web_layer_tree_view_impl_->set_view(root_);
288 blink::WebStorageNamespace* HTMLDocument::createSessionStorageNamespace() {
289 return new WebStorageNamespaceImpl();
292 void HTMLDocument::initializeLayerTreeView() {
293 if (setup_->is_headless()) {
294 web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
295 setup_->compositor_thread(), nullptr, nullptr));
296 return;
299 mojo::URLRequestPtr request(mojo::URLRequest::New());
300 request->url = mojo::String::From("mojo:surfaces_service");
301 mojo::SurfacePtr surface;
302 html_document_app_->ConnectToService(request.Pass(), &surface);
304 // TODO(jamesr): Should be mojo:gpu_service
305 mojo::URLRequestPtr request2(mojo::URLRequest::New());
306 request2->url = mojo::String::From("mojo:view_manager");
307 mojo::GpuPtr gpu_service;
308 html_document_app_->ConnectToService(request2.Pass(), &gpu_service);
309 web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
310 setup_->compositor_thread(), surface.Pass(), gpu_service.Pass()));
313 blink::WebLayerTreeView* HTMLDocument::layerTreeView() {
314 return web_layer_tree_view_impl_.get();
317 blink::WebMediaPlayer* HTMLDocument::createMediaPlayer(
318 blink::WebLocalFrame* frame,
319 const blink::WebURL& url,
320 blink::WebMediaPlayerClient* client,
321 blink::WebContentDecryptionModule* initial_cdm) {
322 return setup_->media_factory()->CreateMediaPlayer(
323 frame, url, client, initial_cdm, html_document_app_->shell());
326 blink::WebFrame* HTMLDocument::createChildFrame(
327 blink::WebLocalFrame* parent,
328 blink::WebTreeScopeType scope,
329 const blink::WebString& frameName,
330 blink::WebSandboxFlags sandboxFlags) {
331 blink::WebLocalFrame* child_frame = blink::WebLocalFrame::create(scope, this);
332 parent->appendChild(child_frame);
333 if (EnableOOPIFs()) {
334 // Create the view that will house the frame now. We embed only once we know
335 // the url.
336 mojo::View* child_frame_view = root_->view_manager()->CreateView();
337 child_frame_view->SetVisible(true);
338 root_->AddChild(child_frame_view);
340 ChildFrameData child_frame_data;
341 child_frame_data.view = child_frame_view;
342 child_frame_data.scope = scope;
343 frame_to_view_[child_frame] = child_frame_data;
345 return child_frame;
348 void HTMLDocument::frameDetached(blink::WebFrame* frame) {
349 frameDetached(frame, DetachType::Remove);
352 void HTMLDocument::frameDetached(blink::WebFrame* frame, DetachType type) {
353 DCHECK(type == DetachType::Remove);
354 if (frame->parent())
355 frame->parent()->removeChild(frame);
357 if (devtools_agent_ && frame == devtools_agent_->frame())
358 devtools_agent_.reset();
360 // |frame| is invalid after here.
361 frame->close();
364 blink::WebCookieJar* HTMLDocument::cookieJar(blink::WebLocalFrame* frame) {
365 // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
366 // Either it should, as it once did, or we should find another solution here.
367 return blink::Platform::current()->cookieJar();
370 blink::WebNavigationPolicy HTMLDocument::decidePolicyForNavigation(
371 const NavigationPolicyInfo& info) {
372 std::string frame_name = info.frame ? info.frame->assignedName().utf8() : "";
373 if (info.frame->parent() && EnableOOPIFs()) {
374 mojo::View* view = frame_to_view_[info.frame].view;
375 mojo::URLRequestPtr url_request = mojo::URLRequest::From(info.urlRequest);
376 view->EmbedAllowingReembed(url_request.Pass());
377 // TODO(sky): I tried swapping the frame types here, but that resulted in
378 // the view never getting sized. Figure out why.
379 // TODO(sky): there are timing conditions here, and we should only do this
380 // once.
381 base::MessageLoop::current()->PostTask(
382 FROM_HERE, base::Bind(&HTMLDocument::ConvertLocalFrameToRemoteFrame,
383 base::Unretained(this), info.frame));
384 return blink::WebNavigationPolicyIgnore;
387 if (CanNavigateLocally(info.frame, info.urlRequest))
388 return info.defaultPolicy;
390 if (navigator_host_.get()) {
391 mojo::URLRequestPtr url_request = mojo::URLRequest::From(info.urlRequest);
392 navigator_host_->RequestNavigate(
393 WebNavigationPolicyToNavigationTarget(info.defaultPolicy),
394 url_request.Pass());
397 return blink::WebNavigationPolicyIgnore;
400 void HTMLDocument::didAddMessageToConsole(
401 const blink::WebConsoleMessage& message,
402 const blink::WebString& source_name,
403 unsigned source_line,
404 const blink::WebString& stack_trace) {
405 VLOG(1) << "[" << source_name.utf8() << "(" << source_line << ")] "
406 << message.text.utf8();
409 void HTMLDocument::didFinishLoad(blink::WebLocalFrame* frame) {
410 // TODO(msw): Notify AxProvider clients of updates on child frame loads.
411 did_finish_load_ = true;
412 // Bind any pending AxProviderImpl interface requests.
413 for (auto it : ax_provider_requests_)
414 ax_providers_.insert(new AxProviderImpl(web_view_, it->Pass()));
415 STLDeleteElements(&ax_provider_requests_);
418 void HTMLDocument::didNavigateWithinPage(
419 blink::WebLocalFrame* frame,
420 const blink::WebHistoryItem& history_item,
421 blink::WebHistoryCommitType commit_type) {
422 if (navigator_host_.get())
423 navigator_host_->DidNavigateLocally(history_item.urlString().utf8());
426 blink::WebEncryptedMediaClient* HTMLDocument::encryptedMediaClient() {
427 return setup_->media_factory()->GetEncryptedMediaClient();
430 void HTMLDocument::OnViewBoundsChanged(View* view,
431 const Rect& old_bounds,
432 const Rect& new_bounds) {
433 DCHECK_EQ(view, root_);
434 UpdateWebviewSizeFromViewSize();
437 void HTMLDocument::OnViewViewportMetricsChanged(
438 mojo::View* view,
439 const mojo::ViewportMetrics& old_metrics,
440 const mojo::ViewportMetrics& new_metrics) {
441 InitSetupAndLoadIfNecessary();
444 void HTMLDocument::OnViewDestroyed(View* view) {
445 DCHECK_EQ(view, root_);
446 root_ = nullptr;
449 void HTMLDocument::OnViewInputEvent(View* view, const mojo::EventPtr& event) {
450 if (event->pointer_data) {
451 // Blink expects coordintes to be in DIPs.
452 event->pointer_data->x /= setup_->device_pixel_ratio();
453 event->pointer_data->y /= setup_->device_pixel_ratio();
454 event->pointer_data->screen_x /= setup_->device_pixel_ratio();
455 event->pointer_data->screen_y /= setup_->device_pixel_ratio();
458 if ((event->action == mojo::EVENT_TYPE_POINTER_DOWN ||
459 event->action == mojo::EVENT_TYPE_POINTER_UP ||
460 event->action == mojo::EVENT_TYPE_POINTER_CANCEL ||
461 event->action == mojo::EVENT_TYPE_POINTER_MOVE) &&
462 event->pointer_data->kind == mojo::POINTER_KIND_TOUCH) {
463 touch_handler_->OnTouchEvent(*event);
464 return;
466 scoped_ptr<blink::WebInputEvent> web_event =
467 event.To<scoped_ptr<blink::WebInputEvent>>();
468 if (web_event)
469 web_view_->handleInputEvent(*web_event);
472 void HTMLDocument::OnViewFocusChanged(mojo::View* gained_focus,
473 mojo::View* lost_focus) {
474 UpdateFocus();
477 void HTMLDocument::UpdateFocus() {
478 if (!web_view_)
479 return;
480 bool is_focused = root_ && root_->HasFocus();
481 web_view_->setFocus(is_focused);
482 web_view_->setIsActive(is_focused);
485 } // namespace html_viewer