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_view.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "base/single_thread_task_runner.h"
11#include "base/strings/string_util.h"
12#include "base/thread_task_runner_handle.h"
13#include "mojo/public/cpp/application/connect.h"
14#include "mojo/public/cpp/application/service_provider_impl.h"
15#include "mojo/public/cpp/system/data_pipe.h"
16#include "mojo/public/interfaces/application/shell.mojom.h"
17#include "mojo/services/html_viewer/blink_input_events_type_converters.h"
18#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
19#include "mojo/services/html_viewer/weblayertreeview_impl.h"
20#include "mojo/services/html_viewer/webmediaplayer_factory.h"
21#include "mojo/services/html_viewer/webstoragenamespace_impl.h"
22#include "mojo/services/html_viewer/weburlloader_impl.h"
23#include "mojo/services/public/cpp/view_manager/view.h"
24#include "mojo/services/public/interfaces/surfaces/surfaces_service.mojom.h"
25#include "skia/ext/refptr.h"
26#include "third_party/WebKit/public/platform/Platform.h"
27#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.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/skia/include/core/SkCanvas.h"
37#include "third_party/skia/include/core/SkColor.h"
38#include "third_party/skia/include/core/SkDevice.h"
39
40namespace mojo {
41namespace {
42
43void ConfigureSettings(blink::WebSettings* settings) {
44  settings->setCookieEnabled(true);
45  settings->setDefaultFixedFontSize(13);
46  settings->setDefaultFontSize(16);
47  settings->setLoadsImagesAutomatically(true);
48  settings->setJavaScriptEnabled(true);
49}
50
51Target WebNavigationPolicyToNavigationTarget(
52    blink::WebNavigationPolicy policy) {
53  switch (policy) {
54    case blink::WebNavigationPolicyCurrentTab:
55      return TARGET_SOURCE_NODE;
56    case blink::WebNavigationPolicyNewBackgroundTab:
57    case blink::WebNavigationPolicyNewForegroundTab:
58    case blink::WebNavigationPolicyNewWindow:
59    case blink::WebNavigationPolicyNewPopup:
60      return TARGET_NEW_NODE;
61    default:
62      return TARGET_DEFAULT;
63  }
64}
65
66bool CanNavigateLocally(blink::WebFrame* frame,
67                        const blink::WebURLRequest& request) {
68  // For now, we just load child frames locally.
69  // TODO(aa): In the future, this should use embedding to connect to a
70  // different instance of Blink if the frame is cross-origin.
71  if (frame->parent())
72    return true;
73
74  // If we have extraData() it means we already have the url response
75  // (presumably because we are being called via Navigate()). In that case we
76  // can go ahead and navigate locally.
77  if (request.extraData())
78    return true;
79
80  // Otherwise we don't know if we're the right app to handle this request. Ask
81  // host to do the navigation for us.
82  return false;
83}
84
85}  // namespace
86
87HTMLDocumentView::HTMLDocumentView(
88    URLResponsePtr response,
89    InterfaceRequest<ServiceProvider> service_provider_request,
90    Shell* shell,
91    scoped_refptr<base::MessageLoopProxy> compositor_thread,
92    WebMediaPlayerFactory* web_media_player_factory)
93    : shell_(shell),
94      web_view_(NULL),
95      root_(NULL),
96      view_manager_client_factory_(shell, this),
97      compositor_thread_(compositor_thread),
98      web_media_player_factory_(web_media_player_factory),
99      weak_factory_(this) {
100  ServiceProviderImpl* exported_services = new ServiceProviderImpl();
101  exported_services->AddService(&view_manager_client_factory_);
102  BindToRequest(exported_services, &service_provider_request);
103  Load(response.Pass());
104}
105
106HTMLDocumentView::~HTMLDocumentView() {
107  if (web_view_)
108    web_view_->close();
109  if (root_)
110    root_->RemoveObserver(this);
111}
112
113void HTMLDocumentView::OnEmbed(
114    ViewManager* view_manager,
115    View* root,
116    ServiceProviderImpl* embedee_service_provider_impl,
117    scoped_ptr<ServiceProvider> embedder_service_provider) {
118  root_ = root;
119  embedder_service_provider_ = embedder_service_provider.Pass();
120  navigator_host_.set_service_provider(embedder_service_provider_.get());
121
122  web_view_->resize(root_->bounds().size());
123  web_layer_tree_view_impl_->setViewportSize(root_->bounds().size());
124  web_layer_tree_view_impl_->set_view(root_);
125  root_->AddObserver(this);
126}
127
128void HTMLDocumentView::OnViewManagerDisconnected(ViewManager* view_manager) {
129  // TODO(aa): Need to figure out how shutdown works.
130}
131
132void HTMLDocumentView::Load(URLResponsePtr response) {
133  web_view_ = blink::WebView::create(this);
134  web_layer_tree_view_impl_->set_widget(web_view_);
135  ConfigureSettings(web_view_->settings());
136  web_view_->setMainFrame(blink::WebLocalFrame::create(this));
137
138  GURL url(response->url);
139
140  WebURLRequestExtraData* extra_data = new WebURLRequestExtraData;
141  extra_data->synthetic_response = response.Pass();
142
143  blink::WebURLRequest web_request;
144  web_request.initialize();
145  web_request.setURL(url);
146  web_request.setExtraData(extra_data);
147
148  web_view_->mainFrame()->loadRequest(web_request);
149}
150
151blink::WebStorageNamespace* HTMLDocumentView::createSessionStorageNamespace() {
152  return new WebStorageNamespaceImpl();
153}
154
155void HTMLDocumentView::initializeLayerTreeView() {
156  ServiceProviderPtr surfaces_service_provider;
157  shell_->ConnectToApplication("mojo:mojo_surfaces_service",
158                               Get(&surfaces_service_provider));
159  InterfacePtr<SurfacesService> surfaces_service;
160  ConnectToService(surfaces_service_provider.get(), &surfaces_service);
161
162  ServiceProviderPtr gpu_service_provider;
163  // TODO(jamesr): Should be mojo:mojo_gpu_service
164  shell_->ConnectToApplication("mojo:mojo_native_viewport_service",
165                               Get(&gpu_service_provider));
166  InterfacePtr<Gpu> gpu_service;
167  ConnectToService(gpu_service_provider.get(), &gpu_service);
168  web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
169      compositor_thread_, surfaces_service.Pass(), gpu_service.Pass()));
170}
171
172blink::WebLayerTreeView* HTMLDocumentView::layerTreeView() {
173  return web_layer_tree_view_impl_.get();
174}
175
176blink::WebMediaPlayer* HTMLDocumentView::createMediaPlayer(
177    blink::WebLocalFrame* frame,
178    const blink::WebURL& url,
179    blink::WebMediaPlayerClient* client) {
180  return web_media_player_factory_->CreateMediaPlayer(frame, url, client);
181}
182
183blink::WebMediaPlayer* HTMLDocumentView::createMediaPlayer(
184    blink::WebLocalFrame* frame,
185    const blink::WebURL& url,
186    blink::WebMediaPlayerClient* client,
187    blink::WebContentDecryptionModule* initial_cdm) {
188  return createMediaPlayer(frame, url, client);
189}
190
191blink::WebFrame* HTMLDocumentView::createChildFrame(
192    blink::WebLocalFrame* parent,
193    const blink::WebString& frameName) {
194  blink::WebLocalFrame* web_frame = blink::WebLocalFrame::create(this);
195  parent->appendChild(web_frame);
196  return web_frame;
197}
198
199void HTMLDocumentView::frameDetached(blink::WebFrame* frame) {
200  if (frame->parent())
201    frame->parent()->removeChild(frame);
202
203  // |frame| is invalid after here.
204  frame->close();
205}
206
207blink::WebCookieJar* HTMLDocumentView::cookieJar(blink::WebLocalFrame* frame) {
208  // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
209  // Either it should, as it once did, or we should find another solution here.
210  return blink::Platform::current()->cookieJar();
211}
212
213blink::WebNavigationPolicy HTMLDocumentView::decidePolicyForNavigation(
214    blink::WebLocalFrame* frame, blink::WebDataSource::ExtraData* data,
215    const blink::WebURLRequest& request, blink::WebNavigationType nav_type,
216    blink::WebNavigationPolicy default_policy, bool is_redirect) {
217  if (CanNavigateLocally(frame, request))
218    return default_policy;
219
220  navigator_host_->RequestNavigate(
221      WebNavigationPolicyToNavigationTarget(default_policy),
222      URLRequest::From(request).Pass());
223
224  return blink::WebNavigationPolicyIgnore;
225}
226
227void HTMLDocumentView::didAddMessageToConsole(
228    const blink::WebConsoleMessage& message,
229    const blink::WebString& source_name,
230    unsigned source_line,
231    const blink::WebString& stack_trace) {
232}
233
234void HTMLDocumentView::didNavigateWithinPage(
235    blink::WebLocalFrame* frame, const blink::WebHistoryItem& history_item,
236    blink::WebHistoryCommitType commit_type) {
237  navigator_host_->DidNavigateLocally(history_item.urlString().utf8());
238}
239
240void HTMLDocumentView::OnViewBoundsChanged(View* view,
241                                           const gfx::Rect& old_bounds,
242                                           const gfx::Rect& new_bounds) {
243  DCHECK_EQ(view, root_);
244  web_view_->resize(view->bounds().size());
245}
246
247void HTMLDocumentView::OnViewDestroyed(View* view) {
248  DCHECK_EQ(view, root_);
249  view->RemoveObserver(this);
250  root_ = NULL;
251}
252
253void HTMLDocumentView::OnViewInputEvent(View* view, const EventPtr& event) {
254  scoped_ptr<blink::WebInputEvent> web_event =
255      event.To<scoped_ptr<blink::WebInputEvent> >();
256  if (web_event)
257    web_view_->handleInputEvent(*web_event);
258}
259
260}  // namespace mojo
261