Stack sampling profiler: add fire-and-forget interface
[chromium-blink-merge.git] / components / html_viewer / html_document.cc
blob5c2f00b381b328cd3dc44d70cb07029e5b5a4f00
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/geolocation_client_impl.h"
20 #include "components/html_viewer/global_state.h"
21 #include "components/html_viewer/media_factory.h"
22 #include "components/html_viewer/web_layer_tree_view_impl.h"
23 #include "components/html_viewer/web_storage_namespace_impl.h"
24 #include "components/html_viewer/web_url_loader_impl.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/cpp/view_property.h"
28 #include "components/view_manager/public/interfaces/surfaces.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/WebInputEvent.h"
41 #include "third_party/WebKit/public/web/WebLocalFrame.h"
42 #include "third_party/WebKit/public/web/WebRemoteFrame.h"
43 #include "third_party/WebKit/public/web/WebRemoteFrameClient.h"
44 #include "third_party/WebKit/public/web/WebScriptSource.h"
45 #include "third_party/WebKit/public/web/WebSettings.h"
46 #include "third_party/WebKit/public/web/WebView.h"
47 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
48 #include "third_party/skia/include/core/SkCanvas.h"
49 #include "third_party/skia/include/core/SkColor.h"
50 #include "third_party/skia/include/core/SkDevice.h"
51 #include "ui/gfx/geometry/dip_util.h"
52 #include "ui/gfx/geometry/size.h"
54 using blink::WebString;
55 using mojo::AxProvider;
56 using mojo::Rect;
57 using mojo::ServiceProviderPtr;
58 using mojo::URLResponsePtr;
59 using mojo::View;
60 using mojo::ViewManager;
61 using mojo::WeakBindToRequest;
63 namespace html_viewer {
64 namespace {
66 bool EnableRemoteDebugging() {
67 return base::CommandLine::ForCurrentProcess()->HasSwitch(
68 devtools_service::kRemoteDebuggingPort);
71 // WebRemoteFrameClient implementation used for OOPIFs.
72 // TODO(sky): this needs to talk to browser by way of an interface.
73 class RemoteFrameClientImpl : public blink::WebRemoteFrameClient {
74 public:
75 explicit RemoteFrameClientImpl(mojo::View* view) : view_(view) {}
76 ~RemoteFrameClientImpl() {}
78 // WebRemoteFrameClient methods:
79 virtual void postMessageEvent(blink::WebLocalFrame* source_frame,
80 blink::WebRemoteFrame* target_frame,
81 blink::WebSecurityOrigin target_origin,
82 blink::WebDOMMessageEvent event) {}
83 virtual void initializeChildFrame(const blink::WebRect& frame_rect,
84 float scale_factor) {
85 mojo::Rect rect;
86 rect.x = frame_rect.x;
87 rect.y = frame_rect.y;
88 rect.width = frame_rect.width;
89 rect.height = frame_rect.height;
90 view_->SetBounds(rect);
92 virtual void navigate(const blink::WebURLRequest& request,
93 bool should_replace_current_entry) {}
94 virtual void reload(bool ignore_cache, bool is_client_redirect) {}
96 virtual void forwardInputEvent(const blink::WebInputEvent* event) {}
98 private:
99 mojo::View* const view_;
101 DISALLOW_COPY_AND_ASSIGN(RemoteFrameClientImpl);
104 void ConfigureSettings(blink::WebSettings* settings) {
105 settings->setCookieEnabled(true);
106 settings->setDefaultFixedFontSize(13);
107 settings->setDefaultFontSize(16);
108 settings->setLoadsImagesAutomatically(true);
109 settings->setJavaScriptEnabled(true);
112 mojo::Target WebNavigationPolicyToNavigationTarget(
113 blink::WebNavigationPolicy policy) {
114 switch (policy) {
115 case blink::WebNavigationPolicyCurrentTab:
116 return mojo::TARGET_SOURCE_NODE;
117 case blink::WebNavigationPolicyNewBackgroundTab:
118 case blink::WebNavigationPolicyNewForegroundTab:
119 case blink::WebNavigationPolicyNewWindow:
120 case blink::WebNavigationPolicyNewPopup:
121 return mojo::TARGET_NEW_NODE;
122 default:
123 return mojo::TARGET_DEFAULT;
127 bool CanNavigateLocally(blink::WebFrame* frame,
128 const blink::WebURLRequest& request) {
129 // For now, we just load child frames locally.
130 if (frame->parent())
131 return true;
133 // If we have extraData() it means we already have the url response
134 // (presumably because we are being called via Navigate()). In that case we
135 // can go ahead and navigate locally.
136 if (request.extraData())
137 return true;
139 // Otherwise we don't know if we're the right app to handle this request. Ask
140 // host to do the navigation for us.
141 return false;
144 } // namespace
146 HTMLDocument::CreateParams::CreateParams(
147 mojo::ApplicationImpl* html_document_app,
148 mojo::ApplicationConnection* connection,
149 mojo::URLResponsePtr response,
150 GlobalState* global_state,
151 const DeleteCallback& delete_callback)
152 : html_document_app(html_document_app),
153 connection(connection),
154 response(response.Pass()),
155 global_state(global_state),
156 delete_callback(delete_callback) {
159 HTMLDocument::CreateParams::~CreateParams() {
162 HTMLDocument::HTMLDocument(HTMLDocument::CreateParams* params)
163 : app_refcount_(params->html_document_app->app_lifetime_helper()
164 ->CreateAppRefCount()),
165 html_document_app_(params->html_document_app),
166 response_(params->response.Pass()),
167 navigator_host_(params->connection->GetServiceProvider()),
168 web_view_(nullptr),
169 root_(nullptr),
170 view_manager_client_factory_(params->html_document_app->shell(), this),
171 global_state_(params->global_state),
172 delete_callback_(params->delete_callback) {
173 params->connection->AddService(
174 static_cast<InterfaceFactory<mojo::AxProvider>*>(this));
175 params->connection->AddService(&view_manager_client_factory_);
177 if (global_state_->did_init())
178 Load(response_.Pass());
181 void HTMLDocument::Destroy() {
182 // See comment in header for a description of lifetime.
183 if (root_) {
184 // Deleting the ViewManager calls back to OnViewManagerDestroyed() and
185 // triggers deletion.
186 delete root_->view_manager();
187 } else {
188 delete this;
192 HTMLDocument::~HTMLDocument() {
193 delete_callback_.Run(this);
195 STLDeleteElements(&ax_providers_);
196 STLDeleteElements(&ax_provider_requests_);
198 if (web_view_)
199 web_view_->close();
200 if (root_)
201 root_->RemoveObserver(this);
204 void HTMLDocument::OnEmbed(View* root) {
205 DCHECK(!global_state_->is_headless());
206 root_ = root;
207 root_->AddObserver(this);
208 UpdateFocus();
210 InitGlobalStateAndLoadIfNecessary();
213 void HTMLDocument::OnViewManagerDestroyed(ViewManager* view_manager) {
214 delete this;
217 void HTMLDocument::Create(mojo::ApplicationConnection* connection,
218 mojo::InterfaceRequest<AxProvider> request) {
219 if (!did_finish_load_) {
220 // Cache AxProvider interface requests until the document finishes loading.
221 auto cached_request = new mojo::InterfaceRequest<AxProvider>();
222 *cached_request = request.Pass();
223 ax_provider_requests_.insert(cached_request);
224 } else {
225 ax_providers_.insert(
226 new AxProviderImpl(web_view_, request.Pass()));
230 void HTMLDocument::Load(URLResponsePtr response) {
231 DCHECK(!web_view_);
232 web_view_ = blink::WebView::create(this);
233 touch_handler_.reset(new TouchHandler(web_view_));
234 web_layer_tree_view_impl_->set_widget(web_view_);
235 ConfigureSettings(web_view_->settings());
237 blink::WebLocalFrame* main_frame =
238 blink::WebLocalFrame::create(blink::WebTreeScopeType::Document, this);
239 web_view_->setMainFrame(main_frame);
241 // TODO(yzshen): http://crbug.com/498986 Creating DevToolsAgentImpl instances
242 // causes html_viewer_apptests flakiness currently. Before we fix that we
243 // cannot enable remote debugging (which is required by Telemetry tests) on
244 // the bots.
245 if (EnableRemoteDebugging()) {
246 devtools_agent_.reset(
247 new DevToolsAgentImpl(main_frame, html_document_app_->shell()));
250 GURL url(response->url);
252 WebURLRequestExtraData* extra_data = new WebURLRequestExtraData;
253 extra_data->synthetic_response = response.Pass();
255 blink::WebURLRequest web_request;
256 web_request.initialize();
257 web_request.setURL(url);
258 web_request.setExtraData(extra_data);
260 web_view_->mainFrame()->loadRequest(web_request);
261 UpdateFocus();
264 void HTMLDocument::UpdateWebviewSizeFromViewSize() {
265 web_view_->setDeviceScaleFactor(global_state_->device_pixel_ratio());
266 const gfx::Size size_in_pixels(root_->bounds().width, root_->bounds().height);
267 const gfx::Size size_in_dips = gfx::ConvertSizeToDIP(
268 root_->viewport_metrics().device_pixel_ratio, size_in_pixels);
269 web_view_->resize(
270 blink::WebSize(size_in_dips.width(), size_in_dips.height()));
271 web_layer_tree_view_impl_->setViewportSize(size_in_pixels);
274 void HTMLDocument::InitGlobalStateAndLoadIfNecessary() {
275 DCHECK(root_);
276 if (root_->viewport_metrics().device_pixel_ratio == 0.f)
277 return;
279 if (!web_view_) {
280 global_state_->InitIfNecessary(
281 root_->viewport_metrics().size_in_pixels.To<gfx::Size>(),
282 root_->viewport_metrics().device_pixel_ratio);
283 Load(response_.Pass());
286 UpdateWebviewSizeFromViewSize();
287 web_layer_tree_view_impl_->set_view(root_);
290 blink::WebStorageNamespace* HTMLDocument::createSessionStorageNamespace() {
291 return new WebStorageNamespaceImpl();
294 void HTMLDocument::initializeLayerTreeView() {
295 if (global_state_->is_headless()) {
296 web_layer_tree_view_impl_.reset(
297 new WebLayerTreeViewImpl(global_state_->compositor_thread(), nullptr,
298 nullptr, nullptr, nullptr));
299 return;
302 mojo::URLRequestPtr request(mojo::URLRequest::New());
303 request->url = mojo::String::From("mojo:surfaces_service");
304 mojo::SurfacePtr surface;
305 html_document_app_->ConnectToService(request.Pass(), &surface);
307 // TODO(jamesr): Should be mojo:gpu_service
308 mojo::URLRequestPtr request2(mojo::URLRequest::New());
309 request2->url = mojo::String::From("mojo:view_manager");
310 mojo::GpuPtr gpu_service;
311 html_document_app_->ConnectToService(request2.Pass(), &gpu_service);
312 web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
313 global_state_->compositor_thread(),
314 global_state_->gpu_memory_buffer_manager(),
315 global_state_->raster_thread_helper()->task_graph_runner(),
316 surface.Pass(), gpu_service.Pass()));
319 blink::WebLayerTreeView* HTMLDocument::layerTreeView() {
320 return web_layer_tree_view_impl_.get();
323 blink::WebMediaPlayer* HTMLDocument::createMediaPlayer(
324 blink::WebLocalFrame* frame,
325 const blink::WebURL& url,
326 blink::WebMediaPlayerClient* client,
327 blink::WebMediaPlayerEncryptedMediaClient* encrypted_client,
328 blink::WebContentDecryptionModule* initial_cdm) {
329 return global_state_->media_factory()->CreateMediaPlayer(
330 frame, url, client, encrypted_client, initial_cdm,
331 html_document_app_->shell());
334 blink::WebFrame* HTMLDocument::createChildFrame(
335 blink::WebLocalFrame* parent,
336 blink::WebTreeScopeType scope,
337 const blink::WebString& frameName,
338 blink::WebSandboxFlags sandboxFlags) {
339 blink::WebLocalFrame* child_frame = blink::WebLocalFrame::create(scope, this);
340 parent->appendChild(child_frame);
341 return child_frame;
344 void HTMLDocument::frameDetached(blink::WebFrame* frame, DetachType type) {
345 DCHECK(type == DetachType::Remove);
346 if (frame->parent())
347 frame->parent()->removeChild(frame);
349 if (devtools_agent_ && frame == devtools_agent_->frame())
350 devtools_agent_.reset();
352 // |frame| is invalid after here.
353 frame->close();
356 blink::WebCookieJar* HTMLDocument::cookieJar(blink::WebLocalFrame* frame) {
357 // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
358 // Either it should, as it once did, or we should find another solution here.
359 return blink::Platform::current()->cookieJar();
362 blink::WebNavigationPolicy HTMLDocument::decidePolicyForNavigation(
363 const NavigationPolicyInfo& info) {
364 // TODO(yzshen): Remove this check once the browser is able to navigate an
365 // existing html_viewer instance and about:blank page support is ready.
366 if (devtools_agent_ && devtools_agent_->frame() == info.frame &&
367 devtools_agent_->handling_page_navigate_request()) {
368 return info.defaultPolicy;
371 std::string frame_name = info.frame ? info.frame->assignedName().utf8() : "";
373 if (CanNavigateLocally(info.frame, info.urlRequest))
374 return info.defaultPolicy;
376 if (navigator_host_.get()) {
377 mojo::URLRequestPtr url_request = mojo::URLRequest::From(info.urlRequest);
378 navigator_host_->RequestNavigate(
379 WebNavigationPolicyToNavigationTarget(info.defaultPolicy),
380 url_request.Pass());
383 return blink::WebNavigationPolicyIgnore;
386 blink::WebGeolocationClient* HTMLDocument::geolocationClient() {
387 if (!geolocation_client_impl_)
388 geolocation_client_impl_.reset(new GeolocationClientImpl);
389 return geolocation_client_impl_.get();
392 void HTMLDocument::didAddMessageToConsole(
393 const blink::WebConsoleMessage& message,
394 const blink::WebString& source_name,
395 unsigned source_line,
396 const blink::WebString& stack_trace) {
397 VLOG(1) << "[" << source_name.utf8() << "(" << source_line << ")] "
398 << message.text.utf8();
401 void HTMLDocument::didFinishLoad(blink::WebLocalFrame* frame) {
402 // TODO(msw): Notify AxProvider clients of updates on child frame loads.
403 did_finish_load_ = true;
404 // Bind any pending AxProviderImpl interface requests.
405 for (auto it : ax_provider_requests_)
406 ax_providers_.insert(new AxProviderImpl(web_view_, it->Pass()));
407 STLDeleteElements(&ax_provider_requests_);
410 void HTMLDocument::didNavigateWithinPage(
411 blink::WebLocalFrame* frame,
412 const blink::WebHistoryItem& history_item,
413 blink::WebHistoryCommitType commit_type) {
414 if (navigator_host_.get())
415 navigator_host_->DidNavigateLocally(history_item.urlString().utf8());
418 blink::WebEncryptedMediaClient* HTMLDocument::encryptedMediaClient() {
419 return global_state_->media_factory()->GetEncryptedMediaClient();
422 void HTMLDocument::OnViewBoundsChanged(View* view,
423 const Rect& old_bounds,
424 const Rect& new_bounds) {
425 DCHECK_EQ(view, root_);
426 UpdateWebviewSizeFromViewSize();
429 void HTMLDocument::OnViewViewportMetricsChanged(
430 mojo::View* view,
431 const mojo::ViewportMetrics& old_metrics,
432 const mojo::ViewportMetrics& new_metrics) {
433 InitGlobalStateAndLoadIfNecessary();
436 void HTMLDocument::OnViewDestroyed(View* view) {
437 DCHECK_EQ(view, root_);
438 root_ = nullptr;
441 void HTMLDocument::OnViewInputEvent(View* view, const mojo::EventPtr& event) {
442 if (event->pointer_data) {
443 // Blink expects coordintes to be in DIPs.
444 event->pointer_data->x /= global_state_->device_pixel_ratio();
445 event->pointer_data->y /= global_state_->device_pixel_ratio();
446 event->pointer_data->screen_x /= global_state_->device_pixel_ratio();
447 event->pointer_data->screen_y /= global_state_->device_pixel_ratio();
450 if ((event->action == mojo::EVENT_TYPE_POINTER_DOWN ||
451 event->action == mojo::EVENT_TYPE_POINTER_UP ||
452 event->action == mojo::EVENT_TYPE_POINTER_CANCEL ||
453 event->action == mojo::EVENT_TYPE_POINTER_MOVE) &&
454 event->pointer_data->kind == mojo::POINTER_KIND_TOUCH) {
455 touch_handler_->OnTouchEvent(*event);
456 return;
458 scoped_ptr<blink::WebInputEvent> web_event =
459 event.To<scoped_ptr<blink::WebInputEvent>>();
460 if (web_event)
461 web_view_->handleInputEvent(*web_event);
464 void HTMLDocument::OnViewFocusChanged(mojo::View* gained_focus,
465 mojo::View* lost_focus) {
466 UpdateFocus();
469 void HTMLDocument::UpdateFocus() {
470 if (!web_view_)
471 return;
472 bool is_focused = root_ && root_->HasFocus();
473 web_view_->setFocus(is_focused);
474 web_view_->setIsActive(is_focused);
477 } // namespace html_viewer