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"
8 #include "base/location.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop_proxy.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/html_viewer/blink_input_events_type_converters.h"
16 #include "components/html_viewer/blink_url_request_type_converters.h"
17 #include "components/html_viewer/setup.h"
18 #include "components/html_viewer/web_layer_tree_view_impl.h"
19 #include "components/html_viewer/web_media_player_factory.h"
20 #include "components/html_viewer/web_storage_namespace_impl.h"
21 #include "components/html_viewer/web_url_loader_impl.h"
22 #include "components/surfaces/public/interfaces/surfaces.mojom.h"
23 #include "components/view_manager/public/cpp/view.h"
24 #include "media/blink/webencryptedmediaclient_impl.h"
25 #include "media/cdm/default_cdm_factory.h"
26 #include "media/filters/default_media_permission.h"
27 #include "mojo/application/public/cpp/application_impl.h"
28 #include "mojo/application/public/cpp/connect.h"
29 #include "mojo/application/public/interfaces/shell.mojom.h"
30 #include "skia/ext/refptr.h"
31 #include "third_party/WebKit/public/platform/Platform.h"
32 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
33 #include "third_party/WebKit/public/platform/WebSize.h"
34 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
35 #include "third_party/WebKit/public/web/WebDocument.h"
36 #include "third_party/WebKit/public/web/WebElement.h"
37 #include "third_party/WebKit/public/web/WebInputEvent.h"
38 #include "third_party/WebKit/public/web/WebLocalFrame.h"
39 #include "third_party/WebKit/public/web/WebScriptSource.h"
40 #include "third_party/WebKit/public/web/WebSettings.h"
41 #include "third_party/WebKit/public/web/WebView.h"
42 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
43 #include "third_party/skia/include/core/SkCanvas.h"
44 #include "third_party/skia/include/core/SkColor.h"
45 #include "third_party/skia/include/core/SkDevice.h"
46 #include "ui/gfx/geometry/dip_util.h"
47 #include "ui/gfx/geometry/size.h"
49 using mojo::AxProvider
;
51 using mojo::ServiceProviderPtr
;
52 using mojo::URLResponsePtr
;
54 using mojo::ViewManager
;
55 using mojo::WeakBindToRequest
;
57 namespace html_viewer
{
60 void ConfigureSettings(blink::WebSettings
* settings
) {
61 settings
->setCookieEnabled(true);
62 settings
->setDefaultFixedFontSize(13);
63 settings
->setDefaultFontSize(16);
64 settings
->setLoadsImagesAutomatically(true);
65 settings
->setJavaScriptEnabled(true);
68 mojo::Target
WebNavigationPolicyToNavigationTarget(
69 blink::WebNavigationPolicy policy
) {
71 case blink::WebNavigationPolicyCurrentTab
:
72 return mojo::TARGET_SOURCE_NODE
;
73 case blink::WebNavigationPolicyNewBackgroundTab
:
74 case blink::WebNavigationPolicyNewForegroundTab
:
75 case blink::WebNavigationPolicyNewWindow
:
76 case blink::WebNavigationPolicyNewPopup
:
77 return mojo::TARGET_NEW_NODE
;
79 return mojo::TARGET_DEFAULT
;
83 bool CanNavigateLocally(blink::WebFrame
* frame
,
84 const blink::WebURLRequest
& request
) {
85 // For now, we just load child frames locally.
86 // TODO(aa): In the future, this should use embedding to connect to a
87 // different instance of Blink if the frame is cross-origin.
91 // If we have extraData() it means we already have the url response
92 // (presumably because we are being called via Navigate()). In that case we
93 // can go ahead and navigate locally.
94 if (request
.extraData())
97 // mojo::NavigatorHost doesn't accept POSTs, so for now reuse this instance.
98 // TODO(jam): improve this (and copy logic from RenderFrameImpl's version)
99 // when we have multi-process.
100 if (EqualsASCII(request
.httpMethod(), "POST"))
103 // Logging into Gmail fails when the referrer isn't sent with a request.
104 // TODO(jam): pass referrer and other HTTP data to NavigatorHost so we can
105 // use a new process in this case.
106 if (!request
.httpHeaderField(blink::WebString::fromUTF8("Referer")).isEmpty())
109 // Otherwise we don't know if we're the right app to handle this request. Ask
110 // host to do the navigation for us.
114 bool AreSecureCodecsSupported() {
115 // Hardware-secure codecs are not currently supported by HTML Viewer on any
122 HTMLDocument::HTMLDocument(
123 mojo::InterfaceRequest
<mojo::ServiceProvider
> services
,
124 URLResponsePtr response
,
127 : response_(response
.Pass()),
131 view_manager_client_factory_(shell_
, this),
133 exported_services_
.AddService(this);
134 exported_services_
.AddService(&view_manager_client_factory_
);
135 exported_services_
.Bind(services
.Pass());
136 if (setup_
->did_init())
137 Load(response_
.Pass());
140 HTMLDocument::~HTMLDocument() {
141 STLDeleteElements(&ax_providers_
);
142 STLDeleteElements(&ax_provider_requests_
);
147 root_
->RemoveObserver(this);
150 void HTMLDocument::OnEmbed(
152 mojo::InterfaceRequest
<mojo::ServiceProvider
> services
,
153 mojo::ServiceProviderPtr exposed_services
) {
154 DCHECK(!setup_
->is_headless());
156 root_
->AddObserver(this);
157 embedder_service_provider_
= exposed_services
.Pass();
158 navigator_host_
.set_service_provider(embedder_service_provider_
.get());
160 InitSetupAndLoadIfNecessary();
163 void HTMLDocument::OnViewManagerDisconnected(ViewManager
* view_manager
) {
164 // TODO(aa): Need to figure out how shutdown works.
167 void HTMLDocument::Create(mojo::ApplicationConnection
* connection
,
168 mojo::InterfaceRequest
<AxProvider
> request
) {
169 if (!did_finish_load_
) {
170 // Cache AxProvider interface requests until the document finishes loading.
171 auto cached_request
= new mojo::InterfaceRequest
<AxProvider
>();
172 *cached_request
= request
.Pass();
173 ax_provider_requests_
.insert(cached_request
);
175 ax_providers_
.insert(
176 WeakBindToRequest(new AxProviderImpl(web_view_
), &request
));
180 void HTMLDocument::Load(URLResponsePtr response
) {
182 web_view_
= blink::WebView::create(this);
183 touch_handler_
.reset(new TouchHandler(web_view_
));
184 web_layer_tree_view_impl_
->set_widget(web_view_
);
185 ConfigureSettings(web_view_
->settings());
186 web_view_
->setMainFrame(blink::WebLocalFrame::create(this));
188 GURL
url(response
->url
);
190 WebURLRequestExtraData
* extra_data
= new WebURLRequestExtraData
;
191 extra_data
->synthetic_response
= response
.Pass();
193 blink::WebURLRequest web_request
;
194 web_request
.initialize();
195 web_request
.setURL(url
);
196 web_request
.setExtraData(extra_data
);
198 web_view_
->mainFrame()->loadRequest(web_request
);
201 void HTMLDocument::UpdateWebviewSizeFromViewSize() {
202 web_view_
->setDeviceScaleFactor(setup_
->device_pixel_ratio());
203 const gfx::Size
size_in_pixels(root_
->bounds().width
, root_
->bounds().height
);
204 const gfx::Size size_in_dips
= gfx::ConvertSizeToDIP(
205 root_
->viewport_metrics().device_pixel_ratio
, size_in_pixels
);
207 blink::WebSize(size_in_dips
.width(), size_in_dips
.height()));
208 web_layer_tree_view_impl_
->setViewportSize(size_in_pixels
);
211 void HTMLDocument::InitSetupAndLoadIfNecessary() {
213 if (web_view_
|| root_
->viewport_metrics().device_pixel_ratio
== 0.f
)
216 setup_
->InitIfNecessary(gfx::Size(root_
->viewport_metrics().size
->width
,
217 root_
->viewport_metrics().size
->height
),
218 root_
->viewport_metrics().device_pixel_ratio
);
219 Load(response_
.Pass());
221 UpdateWebviewSizeFromViewSize();
222 web_layer_tree_view_impl_
->set_view(root_
);
225 blink::WebStorageNamespace
* HTMLDocument::createSessionStorageNamespace() {
226 return new WebStorageNamespaceImpl();
229 void HTMLDocument::initializeLayerTreeView() {
230 if (setup_
->is_headless()) {
231 web_layer_tree_view_impl_
.reset(new WebLayerTreeViewImpl(
232 setup_
->compositor_thread(), nullptr, nullptr));
236 ServiceProviderPtr surfaces_service_provider
;
237 shell_
->ConnectToApplication("mojo:surfaces_service",
238 GetProxy(&surfaces_service_provider
), nullptr);
239 mojo::SurfacePtr surface
;
240 ConnectToService(surfaces_service_provider
.get(), &surface
);
242 ServiceProviderPtr gpu_service_provider
;
243 // TODO(jamesr): Should be mojo:gpu_service
244 shell_
->ConnectToApplication("mojo:native_viewport_service",
245 GetProxy(&gpu_service_provider
), nullptr);
246 mojo::GpuPtr gpu_service
;
247 ConnectToService(gpu_service_provider
.get(), &gpu_service
);
248 web_layer_tree_view_impl_
.reset(new WebLayerTreeViewImpl(
249 setup_
->compositor_thread(), surface
.Pass(), gpu_service
.Pass()));
252 blink::WebLayerTreeView
* HTMLDocument::layerTreeView() {
253 return web_layer_tree_view_impl_
.get();
256 blink::WebMediaPlayer
* HTMLDocument::createMediaPlayer(
257 blink::WebLocalFrame
* frame
,
258 const blink::WebURL
& url
,
259 blink::WebMediaPlayerClient
* client
) {
260 return createMediaPlayer(frame
, url
, client
, nullptr);
263 blink::WebMediaPlayer
* HTMLDocument::createMediaPlayer(
264 blink::WebLocalFrame
* frame
,
265 const blink::WebURL
& url
,
266 blink::WebMediaPlayerClient
* client
,
267 blink::WebContentDecryptionModule
* initial_cdm
) {
268 blink::WebMediaPlayer
* player
=
269 setup_
->web_media_player_factory()
270 ? setup_
->web_media_player_factory()->CreateMediaPlayer(
271 frame
, url
, client
, GetMediaPermission(), GetCdmFactory(),
277 blink::WebFrame
* HTMLDocument::createChildFrame(
278 blink::WebLocalFrame
* parent
,
279 const blink::WebString
& frameName
,
280 blink::WebSandboxFlags sandboxFlags
) {
281 blink::WebLocalFrame
* web_frame
= blink::WebLocalFrame::create(this);
282 parent
->appendChild(web_frame
);
286 void HTMLDocument::frameDetached(blink::WebFrame
* frame
) {
288 frame
->parent()->removeChild(frame
);
290 // |frame| is invalid after here.
294 blink::WebCookieJar
* HTMLDocument::cookieJar(blink::WebLocalFrame
* frame
) {
295 // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
296 // Either it should, as it once did, or we should find another solution here.
297 return blink::Platform::current()->cookieJar();
300 blink::WebNavigationPolicy
HTMLDocument::decidePolicyForNavigation(
301 blink::WebLocalFrame
* frame
,
302 blink::WebDataSource::ExtraData
* data
,
303 const blink::WebURLRequest
& request
,
304 blink::WebNavigationType nav_type
,
305 blink::WebNavigationPolicy default_policy
,
307 if (CanNavigateLocally(frame
, request
))
308 return default_policy
;
310 if (navigator_host_
.get()) {
311 navigator_host_
->RequestNavigate(
312 WebNavigationPolicyToNavigationTarget(default_policy
),
313 mojo::URLRequest::From(request
).Pass());
316 return blink::WebNavigationPolicyIgnore
;
319 void HTMLDocument::didAddMessageToConsole(
320 const blink::WebConsoleMessage
& message
,
321 const blink::WebString
& source_name
,
322 unsigned source_line
,
323 const blink::WebString
& stack_trace
) {
324 VLOG(1) << "[" << source_name
.utf8() << "(" << source_line
<< ")] "
325 << message
.text
.utf8();
328 void HTMLDocument::didFinishLoad(blink::WebLocalFrame
* frame
) {
329 // TODO(msw): Notify AxProvider clients of updates on child frame loads.
330 did_finish_load_
= true;
331 // Bind any pending AxProviderImpl interface requests.
332 for (auto it
: ax_provider_requests_
)
333 ax_providers_
.insert(WeakBindToRequest(new AxProviderImpl(web_view_
), it
));
334 STLDeleteElements(&ax_provider_requests_
);
337 void HTMLDocument::didNavigateWithinPage(
338 blink::WebLocalFrame
* frame
,
339 const blink::WebHistoryItem
& history_item
,
340 blink::WebHistoryCommitType commit_type
) {
341 if (navigator_host_
.get())
342 navigator_host_
->DidNavigateLocally(history_item
.urlString().utf8());
345 blink::WebEncryptedMediaClient
* HTMLDocument::encryptedMediaClient() {
346 if (!web_encrypted_media_client_
) {
347 web_encrypted_media_client_
.reset(new media::WebEncryptedMediaClientImpl(
348 base::Bind(&AreSecureCodecsSupported
), GetCdmFactory(),
349 GetMediaPermission()));
351 return web_encrypted_media_client_
.get();
354 void HTMLDocument::OnViewBoundsChanged(View
* view
,
355 const Rect
& old_bounds
,
356 const Rect
& new_bounds
) {
357 DCHECK_EQ(view
, root_
);
358 UpdateWebviewSizeFromViewSize();
361 void HTMLDocument::OnViewViewportMetricsChanged(
363 const mojo::ViewportMetrics
& old_metrics
,
364 const mojo::ViewportMetrics
& new_metrics
) {
365 InitSetupAndLoadIfNecessary();
368 void HTMLDocument::OnViewDestroyed(View
* view
) {
369 DCHECK_EQ(view
, root_
);
372 mojo::ApplicationImpl::Terminate();
375 void HTMLDocument::OnViewInputEvent(View
* view
, const mojo::EventPtr
& event
) {
376 if (event
->pointer_data
) {
377 // Blink expects coordintes to be in DIPs.
378 event
->pointer_data
->x
/= setup_
->device_pixel_ratio();
379 event
->pointer_data
->y
/= setup_
->device_pixel_ratio();
380 event
->pointer_data
->screen_x
/= setup_
->device_pixel_ratio();
381 event
->pointer_data
->screen_y
/= setup_
->device_pixel_ratio();
384 if ((event
->action
== mojo::EVENT_TYPE_POINTER_DOWN
||
385 event
->action
== mojo::EVENT_TYPE_POINTER_UP
||
386 event
->action
== mojo::EVENT_TYPE_POINTER_CANCEL
||
387 event
->action
== mojo::EVENT_TYPE_POINTER_MOVE
) &&
388 event
->pointer_data
->kind
== mojo::POINTER_KIND_TOUCH
) {
389 touch_handler_
->OnTouchEvent(*event
);
392 scoped_ptr
<blink::WebInputEvent
> web_event
=
393 event
.To
<scoped_ptr
<blink::WebInputEvent
>>();
395 web_view_
->handleInputEvent(*web_event
);
398 media::MediaPermission
* HTMLDocument::GetMediaPermission() {
399 if (!media_permission_
)
400 media_permission_
.reset(new media::DefaultMediaPermission(true));
401 return media_permission_
.get();
404 media::CdmFactory
* HTMLDocument::GetCdmFactory() {
406 cdm_factory_
.reset(new media::DefaultCdmFactory());
407 return cdm_factory_
.get();
410 } // namespace html_viewer