OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "mojo/services/html_viewer/html_document.h" | |
6 | |
7 #include "base/bind.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 "media/blink/webencryptedmediaclient_impl.h" | |
16 #include "media/cdm/default_cdm_factory.h" | |
17 #include "media/filters/default_media_permission.h" | |
18 #include "mojo/services/html_viewer/blink_input_events_type_converters.h" | |
19 #include "mojo/services/html_viewer/blink_url_request_type_converters.h" | |
20 #include "mojo/services/html_viewer/web_layer_tree_view_impl.h" | |
21 #include "mojo/services/html_viewer/web_media_player_factory.h" | |
22 #include "mojo/services/html_viewer/web_storage_namespace_impl.h" | |
23 #include "mojo/services/html_viewer/web_url_loader_impl.h" | |
24 #include "skia/ext/refptr.h" | |
25 #include "third_party/WebKit/public/platform/Platform.h" | |
26 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" | |
27 #include "third_party/WebKit/public/platform/WebSize.h" | |
28 #include "third_party/WebKit/public/web/WebConsoleMessage.h" | |
29 #include "third_party/WebKit/public/web/WebDocument.h" | |
30 #include "third_party/WebKit/public/web/WebElement.h" | |
31 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
32 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
33 #include "third_party/WebKit/public/web/WebScriptSource.h" | |
34 #include "third_party/WebKit/public/web/WebSettings.h" | |
35 #include "third_party/WebKit/public/web/WebView.h" | |
36 #include "third_party/mojo/src/mojo/public/cpp/application/application_impl.h" | |
37 #include "third_party/mojo/src/mojo/public/cpp/application/connect.h" | |
38 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" | |
39 #include "third_party/mojo/src/mojo/public/interfaces/application/shell.mojom.h" | |
40 #include "third_party/mojo_services/src/surfaces/public/interfaces/surfaces.mojo
m.h" | |
41 #include "third_party/mojo_services/src/view_manager/public/cpp/view.h" | |
42 #include "third_party/skia/include/core/SkCanvas.h" | |
43 #include "third_party/skia/include/core/SkColor.h" | |
44 #include "third_party/skia/include/core/SkDevice.h" | |
45 #include "ui/gfx/geometry/dip_util.h" | |
46 | |
47 using mojo::AxProvider; | |
48 using mojo::Rect; | |
49 using mojo::ServiceProviderPtr; | |
50 using mojo::URLResponsePtr; | |
51 using mojo::View; | |
52 using mojo::ViewManager; | |
53 using mojo::WeakBindToRequest; | |
54 | |
55 namespace html_viewer { | |
56 namespace { | |
57 | |
58 void ConfigureSettings(blink::WebSettings* settings) { | |
59 settings->setCookieEnabled(true); | |
60 settings->setDefaultFixedFontSize(13); | |
61 settings->setDefaultFontSize(16); | |
62 settings->setLoadsImagesAutomatically(true); | |
63 settings->setJavaScriptEnabled(true); | |
64 } | |
65 | |
66 mojo::Target WebNavigationPolicyToNavigationTarget( | |
67 blink::WebNavigationPolicy policy) { | |
68 switch (policy) { | |
69 case blink::WebNavigationPolicyCurrentTab: | |
70 return mojo::TARGET_SOURCE_NODE; | |
71 case blink::WebNavigationPolicyNewBackgroundTab: | |
72 case blink::WebNavigationPolicyNewForegroundTab: | |
73 case blink::WebNavigationPolicyNewWindow: | |
74 case blink::WebNavigationPolicyNewPopup: | |
75 return mojo::TARGET_NEW_NODE; | |
76 default: | |
77 return mojo::TARGET_DEFAULT; | |
78 } | |
79 } | |
80 | |
81 bool CanNavigateLocally(blink::WebFrame* frame, | |
82 const blink::WebURLRequest& request) { | |
83 // For now, we just load child frames locally. | |
84 // TODO(aa): In the future, this should use embedding to connect to a | |
85 // different instance of Blink if the frame is cross-origin. | |
86 if (frame->parent()) | |
87 return true; | |
88 | |
89 // If we have extraData() it means we already have the url response | |
90 // (presumably because we are being called via Navigate()). In that case we | |
91 // can go ahead and navigate locally. | |
92 if (request.extraData()) | |
93 return true; | |
94 | |
95 // mojo::NavigatorHost doesn't accept POSTs, so for now reuse this instance. | |
96 // TODO(jam): improve this (and copy logic from RenderFrameImpl's version) | |
97 // when we have multi-process. | |
98 if (EqualsASCII(request.httpMethod(), "POST")) | |
99 return true; | |
100 | |
101 // Logging into Gmail fails when the referrer isn't sent with a request. | |
102 // TODO(jam): pass referrer and other HTTP data to NavigatorHost so we can | |
103 // use a new process in this case. | |
104 if (!request.httpHeaderField(blink::WebString::fromUTF8("Referer")).isEmpty()) | |
105 return true; | |
106 | |
107 // Otherwise we don't know if we're the right app to handle this request. Ask | |
108 // host to do the navigation for us. | |
109 return false; | |
110 } | |
111 | |
112 } // namespace | |
113 | |
114 HTMLDocument::HTMLDocument( | |
115 mojo::InterfaceRequest<mojo::ServiceProvider> services, | |
116 URLResponsePtr response, | |
117 mojo::Shell* shell, | |
118 scoped_refptr<base::MessageLoopProxy> compositor_thread, | |
119 WebMediaPlayerFactory* web_media_player_factory, | |
120 bool is_headless) | |
121 : response_(response.Pass()), | |
122 shell_(shell), | |
123 web_view_(nullptr), | |
124 root_(nullptr), | |
125 view_manager_client_factory_(shell_, this), | |
126 compositor_thread_(compositor_thread), | |
127 web_media_player_factory_(web_media_player_factory), | |
128 is_headless_(is_headless), | |
129 device_pixel_ratio_(1.0) { | |
130 exported_services_.AddService(this); | |
131 exported_services_.AddService(&view_manager_client_factory_); | |
132 exported_services_.Bind(services.Pass()); | |
133 Load(response_.Pass()); | |
134 } | |
135 | |
136 HTMLDocument::~HTMLDocument() { | |
137 STLDeleteElements(&ax_providers_); | |
138 STLDeleteElements(&ax_provider_requests_); | |
139 | |
140 if (web_view_) | |
141 web_view_->close(); | |
142 if (root_) | |
143 root_->RemoveObserver(this); | |
144 } | |
145 | |
146 void HTMLDocument::OnEmbed( | |
147 View* root, | |
148 mojo::InterfaceRequest<mojo::ServiceProvider> services, | |
149 mojo::ServiceProviderPtr exposed_services) { | |
150 DCHECK(!is_headless_); | |
151 root_ = root; | |
152 embedder_service_provider_ = exposed_services.Pass(); | |
153 navigator_host_.set_service_provider(embedder_service_provider_.get()); | |
154 UpdateWebviewSizeFromViewSize(); | |
155 web_layer_tree_view_impl_->set_view(root_); | |
156 root_->AddObserver(this); | |
157 } | |
158 | |
159 void HTMLDocument::OnViewManagerDisconnected(ViewManager* view_manager) { | |
160 // TODO(aa): Need to figure out how shutdown works. | |
161 } | |
162 | |
163 void HTMLDocument::Create(mojo::ApplicationConnection* connection, | |
164 mojo::InterfaceRequest<AxProvider> request) { | |
165 if (!did_finish_load_) { | |
166 // Cache AxProvider interface requests until the document finishes loading. | |
167 auto cached_request = new mojo::InterfaceRequest<AxProvider>(); | |
168 *cached_request = request.Pass(); | |
169 ax_provider_requests_.insert(cached_request); | |
170 } else { | |
171 ax_providers_.insert( | |
172 WeakBindToRequest(new AxProviderImpl(web_view_), &request)); | |
173 } | |
174 } | |
175 | |
176 void HTMLDocument::Load(URLResponsePtr response) { | |
177 DCHECK(!web_view_); | |
178 web_view_ = blink::WebView::create(this); | |
179 touch_handler_.reset(new TouchHandler(web_view_)); | |
180 web_layer_tree_view_impl_->set_widget(web_view_); | |
181 ConfigureSettings(web_view_->settings()); | |
182 web_view_->setMainFrame(blink::WebLocalFrame::create(this)); | |
183 | |
184 GURL url(response->url); | |
185 | |
186 WebURLRequestExtraData* extra_data = new WebURLRequestExtraData; | |
187 extra_data->synthetic_response = response.Pass(); | |
188 | |
189 blink::WebURLRequest web_request; | |
190 web_request.initialize(); | |
191 web_request.setURL(url); | |
192 web_request.setExtraData(extra_data); | |
193 | |
194 web_view_->mainFrame()->loadRequest(web_request); | |
195 } | |
196 | |
197 void HTMLDocument::UpdateWebviewSizeFromViewSize() { | |
198 device_pixel_ratio_ = root_->viewport_metrics().device_pixel_ratio; | |
199 web_view_->setDeviceScaleFactor(device_pixel_ratio_); | |
200 const gfx::Size size_in_pixels(root_->bounds().width, root_->bounds().height); | |
201 const gfx::Size size_in_dips = gfx::ConvertSizeToDIP( | |
202 root_->viewport_metrics().device_pixel_ratio, size_in_pixels); | |
203 web_view_->resize( | |
204 blink::WebSize(size_in_dips.width(), size_in_dips.height())); | |
205 web_layer_tree_view_impl_->setViewportSize(size_in_pixels); | |
206 } | |
207 | |
208 blink::WebStorageNamespace* HTMLDocument::createSessionStorageNamespace() { | |
209 return new WebStorageNamespaceImpl(); | |
210 } | |
211 | |
212 void HTMLDocument::initializeLayerTreeView() { | |
213 if (is_headless_) { | |
214 web_layer_tree_view_impl_.reset( | |
215 new WebLayerTreeViewImpl(compositor_thread_, nullptr, nullptr)); | |
216 return; | |
217 } | |
218 | |
219 ServiceProviderPtr surfaces_service_provider; | |
220 shell_->ConnectToApplication("mojo:surfaces_service", | |
221 GetProxy(&surfaces_service_provider), nullptr); | |
222 mojo::SurfacePtr surface; | |
223 ConnectToService(surfaces_service_provider.get(), &surface); | |
224 | |
225 ServiceProviderPtr gpu_service_provider; | |
226 // TODO(jamesr): Should be mojo:gpu_service | |
227 shell_->ConnectToApplication("mojo:native_viewport_service", | |
228 GetProxy(&gpu_service_provider), nullptr); | |
229 mojo::GpuPtr gpu_service; | |
230 ConnectToService(gpu_service_provider.get(), &gpu_service); | |
231 web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl( | |
232 compositor_thread_, surface.Pass(), gpu_service.Pass())); | |
233 } | |
234 | |
235 blink::WebLayerTreeView* HTMLDocument::layerTreeView() { | |
236 return web_layer_tree_view_impl_.get(); | |
237 } | |
238 | |
239 blink::WebMediaPlayer* HTMLDocument::createMediaPlayer( | |
240 blink::WebLocalFrame* frame, | |
241 const blink::WebURL& url, | |
242 blink::WebMediaPlayerClient* client) { | |
243 return createMediaPlayer(frame, url, client, nullptr); | |
244 } | |
245 | |
246 blink::WebMediaPlayer* HTMLDocument::createMediaPlayer( | |
247 blink::WebLocalFrame* frame, | |
248 const blink::WebURL& url, | |
249 blink::WebMediaPlayerClient* client, | |
250 blink::WebContentDecryptionModule* initial_cdm) { | |
251 blink::WebMediaPlayer* player = | |
252 web_media_player_factory_ | |
253 ? web_media_player_factory_->CreateMediaPlayer( | |
254 frame, url, client, GetMediaPermission(), GetCdmFactory(), | |
255 initial_cdm, shell_) | |
256 : nullptr; | |
257 return player; | |
258 } | |
259 | |
260 blink::WebFrame* HTMLDocument::createChildFrame( | |
261 blink::WebLocalFrame* parent, | |
262 const blink::WebString& frameName, | |
263 blink::WebSandboxFlags sandboxFlags) { | |
264 blink::WebLocalFrame* web_frame = blink::WebLocalFrame::create(this); | |
265 parent->appendChild(web_frame); | |
266 return web_frame; | |
267 } | |
268 | |
269 void HTMLDocument::frameDetached(blink::WebFrame* frame) { | |
270 if (frame->parent()) | |
271 frame->parent()->removeChild(frame); | |
272 | |
273 // |frame| is invalid after here. | |
274 frame->close(); | |
275 } | |
276 | |
277 blink::WebCookieJar* HTMLDocument::cookieJar(blink::WebLocalFrame* frame) { | |
278 // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar. | |
279 // Either it should, as it once did, or we should find another solution here. | |
280 return blink::Platform::current()->cookieJar(); | |
281 } | |
282 | |
283 blink::WebNavigationPolicy HTMLDocument::decidePolicyForNavigation( | |
284 blink::WebLocalFrame* frame, | |
285 blink::WebDataSource::ExtraData* data, | |
286 const blink::WebURLRequest& request, | |
287 blink::WebNavigationType nav_type, | |
288 blink::WebNavigationPolicy default_policy, | |
289 bool is_redirect) { | |
290 if (CanNavigateLocally(frame, request)) | |
291 return default_policy; | |
292 | |
293 if (navigator_host_.get()) { | |
294 navigator_host_->RequestNavigate( | |
295 WebNavigationPolicyToNavigationTarget(default_policy), | |
296 mojo::URLRequest::From(request).Pass()); | |
297 } | |
298 | |
299 return blink::WebNavigationPolicyIgnore; | |
300 } | |
301 | |
302 void HTMLDocument::didAddMessageToConsole( | |
303 const blink::WebConsoleMessage& message, | |
304 const blink::WebString& source_name, | |
305 unsigned source_line, | |
306 const blink::WebString& stack_trace) { | |
307 VLOG(1) << "[" << source_name.utf8() << "(" << source_line << ")] " | |
308 << message.text.utf8(); | |
309 } | |
310 | |
311 void HTMLDocument::didFinishLoad(blink::WebLocalFrame* frame) { | |
312 // TODO(msw): Notify AxProvider clients of updates on child frame loads. | |
313 did_finish_load_ = true; | |
314 // Bind any pending AxProviderImpl interface requests. | |
315 for (auto it : ax_provider_requests_) | |
316 ax_providers_.insert(WeakBindToRequest(new AxProviderImpl(web_view_), it)); | |
317 STLDeleteElements(&ax_provider_requests_); | |
318 } | |
319 | |
320 void HTMLDocument::didNavigateWithinPage( | |
321 blink::WebLocalFrame* frame, | |
322 const blink::WebHistoryItem& history_item, | |
323 blink::WebHistoryCommitType commit_type) { | |
324 if (navigator_host_.get()) | |
325 navigator_host_->DidNavigateLocally(history_item.urlString().utf8()); | |
326 } | |
327 | |
328 blink::WebEncryptedMediaClient* HTMLDocument::encryptedMediaClient() { | |
329 if (!web_encrypted_media_client_) { | |
330 web_encrypted_media_client_.reset(new media::WebEncryptedMediaClientImpl( | |
331 GetCdmFactory(), GetMediaPermission())); | |
332 } | |
333 return web_encrypted_media_client_.get(); | |
334 } | |
335 | |
336 void HTMLDocument::OnViewBoundsChanged(View* view, | |
337 const Rect& old_bounds, | |
338 const Rect& new_bounds) { | |
339 DCHECK_EQ(view, root_); | |
340 UpdateWebviewSizeFromViewSize(); | |
341 } | |
342 | |
343 void HTMLDocument::OnViewDestroyed(View* view) { | |
344 DCHECK_EQ(view, root_); | |
345 root_ = nullptr; | |
346 delete this; | |
347 mojo::ApplicationImpl::Terminate(); | |
348 } | |
349 | |
350 void HTMLDocument::OnViewInputEvent(View* view, const mojo::EventPtr& event) { | |
351 if (event->pointer_data) { | |
352 // Blink expects coordintes to be in DIPs. | |
353 event->pointer_data->x /= device_pixel_ratio_; | |
354 event->pointer_data->y /= device_pixel_ratio_; | |
355 event->pointer_data->screen_x /= device_pixel_ratio_; | |
356 event->pointer_data->screen_y /= device_pixel_ratio_; | |
357 } | |
358 | |
359 if ((event->action == mojo::EVENT_TYPE_POINTER_DOWN || | |
360 event->action == mojo::EVENT_TYPE_POINTER_UP || | |
361 event->action == mojo::EVENT_TYPE_POINTER_CANCEL || | |
362 event->action == mojo::EVENT_TYPE_POINTER_MOVE) && | |
363 event->pointer_data->kind == mojo::POINTER_KIND_TOUCH) { | |
364 touch_handler_->OnTouchEvent(*event); | |
365 return; | |
366 } | |
367 scoped_ptr<blink::WebInputEvent> web_event = | |
368 event.To<scoped_ptr<blink::WebInputEvent>>(); | |
369 if (web_event) | |
370 web_view_->handleInputEvent(*web_event); | |
371 } | |
372 | |
373 media::MediaPermission* HTMLDocument::GetMediaPermission() { | |
374 if (!media_permission_) | |
375 media_permission_.reset(new media::DefaultMediaPermission(true)); | |
376 return media_permission_.get(); | |
377 } | |
378 | |
379 media::CdmFactory* HTMLDocument::GetCdmFactory() { | |
380 if (!cdm_factory_) | |
381 cdm_factory_.reset(new media::DefaultCdmFactory()); | |
382 return cdm_factory_.get(); | |
383 } | |
384 | |
385 } // namespace html_viewer | |
OLD | NEW |