summaryrefslogtreecommitdiffstats
path: root/components/html_viewer/html_document.cc
diff options
context:
space:
mode:
Diffstat (limited to 'components/html_viewer/html_document.cc')
-rw-r--r--components/html_viewer/html_document.cc385
1 files changed, 385 insertions, 0 deletions
diff --git a/components/html_viewer/html_document.cc b/components/html_viewer/html_document.cc
new file mode 100644
index 0000000..e1b57cf
--- /dev/null
+++ b/components/html_viewer/html_document.cc
@@ -0,0 +1,385 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/html_viewer/html_document.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/html_viewer/blink_input_events_type_converters.h"
+#include "components/html_viewer/blink_url_request_type_converters.h"
+#include "components/html_viewer/web_layer_tree_view_impl.h"
+#include "components/html_viewer/web_media_player_factory.h"
+#include "components/html_viewer/web_storage_namespace_impl.h"
+#include "components/html_viewer/web_url_loader_impl.h"
+#include "media/blink/webencryptedmediaclient_impl.h"
+#include "media/cdm/default_cdm_factory.h"
+#include "media/filters/default_media_permission.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/public/platform/WebSize.h"
+#include "third_party/WebKit/public/web/WebConsoleMessage.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+#include "third_party/WebKit/public/web/WebSettings.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/mojo/src/mojo/public/cpp/application/application_impl.h"
+#include "third_party/mojo/src/mojo/public/cpp/application/connect.h"
+#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
+#include "third_party/mojo/src/mojo/public/interfaces/application/shell.mojom.h"
+#include "third_party/mojo_services/src/surfaces/public/interfaces/surfaces.mojom.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "ui/gfx/geometry/dip_util.h"
+
+using mojo::AxProvider;
+using mojo::Rect;
+using mojo::ServiceProviderPtr;
+using mojo::URLResponsePtr;
+using mojo::View;
+using mojo::ViewManager;
+using mojo::WeakBindToRequest;
+
+namespace html_viewer {
+namespace {
+
+void ConfigureSettings(blink::WebSettings* settings) {
+ settings->setCookieEnabled(true);
+ settings->setDefaultFixedFontSize(13);
+ settings->setDefaultFontSize(16);
+ settings->setLoadsImagesAutomatically(true);
+ settings->setJavaScriptEnabled(true);
+}
+
+mojo::Target WebNavigationPolicyToNavigationTarget(
+ blink::WebNavigationPolicy policy) {
+ switch (policy) {
+ case blink::WebNavigationPolicyCurrentTab:
+ return mojo::TARGET_SOURCE_NODE;
+ case blink::WebNavigationPolicyNewBackgroundTab:
+ case blink::WebNavigationPolicyNewForegroundTab:
+ case blink::WebNavigationPolicyNewWindow:
+ case blink::WebNavigationPolicyNewPopup:
+ return mojo::TARGET_NEW_NODE;
+ default:
+ return mojo::TARGET_DEFAULT;
+ }
+}
+
+bool CanNavigateLocally(blink::WebFrame* frame,
+ const blink::WebURLRequest& request) {
+ // For now, we just load child frames locally.
+ // TODO(aa): In the future, this should use embedding to connect to a
+ // different instance of Blink if the frame is cross-origin.
+ if (frame->parent())
+ return true;
+
+ // If we have extraData() it means we already have the url response
+ // (presumably because we are being called via Navigate()). In that case we
+ // can go ahead and navigate locally.
+ if (request.extraData())
+ return true;
+
+ // mojo::NavigatorHost doesn't accept POSTs, so for now reuse this instance.
+ // TODO(jam): improve this (and copy logic from RenderFrameImpl's version)
+ // when we have multi-process.
+ if (EqualsASCII(request.httpMethod(), "POST"))
+ return true;
+
+ // Logging into Gmail fails when the referrer isn't sent with a request.
+ // TODO(jam): pass referrer and other HTTP data to NavigatorHost so we can
+ // use a new process in this case.
+ if (!request.httpHeaderField(blink::WebString::fromUTF8("Referer")).isEmpty())
+ return true;
+
+ // Otherwise we don't know if we're the right app to handle this request. Ask
+ // host to do the navigation for us.
+ return false;
+}
+
+} // namespace
+
+HTMLDocument::HTMLDocument(
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ URLResponsePtr response,
+ mojo::Shell* shell,
+ scoped_refptr<base::MessageLoopProxy> compositor_thread,
+ WebMediaPlayerFactory* web_media_player_factory,
+ bool is_headless)
+ : response_(response.Pass()),
+ shell_(shell),
+ web_view_(nullptr),
+ root_(nullptr),
+ view_manager_client_factory_(shell_, this),
+ compositor_thread_(compositor_thread),
+ web_media_player_factory_(web_media_player_factory),
+ is_headless_(is_headless),
+ device_pixel_ratio_(1.0) {
+ exported_services_.AddService(this);
+ exported_services_.AddService(&view_manager_client_factory_);
+ exported_services_.Bind(services.Pass());
+ Load(response_.Pass());
+}
+
+HTMLDocument::~HTMLDocument() {
+ STLDeleteElements(&ax_providers_);
+ STLDeleteElements(&ax_provider_requests_);
+
+ if (web_view_)
+ web_view_->close();
+ if (root_)
+ root_->RemoveObserver(this);
+}
+
+void HTMLDocument::OnEmbed(
+ View* root,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) {
+ DCHECK(!is_headless_);
+ root_ = root;
+ embedder_service_provider_ = exposed_services.Pass();
+ navigator_host_.set_service_provider(embedder_service_provider_.get());
+ UpdateWebviewSizeFromViewSize();
+ web_layer_tree_view_impl_->set_view(root_);
+ root_->AddObserver(this);
+}
+
+void HTMLDocument::OnViewManagerDisconnected(ViewManager* view_manager) {
+ // TODO(aa): Need to figure out how shutdown works.
+}
+
+void HTMLDocument::Create(mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<AxProvider> request) {
+ if (!did_finish_load_) {
+ // Cache AxProvider interface requests until the document finishes loading.
+ auto cached_request = new mojo::InterfaceRequest<AxProvider>();
+ *cached_request = request.Pass();
+ ax_provider_requests_.insert(cached_request);
+ } else {
+ ax_providers_.insert(
+ WeakBindToRequest(new AxProviderImpl(web_view_), &request));
+ }
+}
+
+void HTMLDocument::Load(URLResponsePtr response) {
+ DCHECK(!web_view_);
+ web_view_ = blink::WebView::create(this);
+ touch_handler_.reset(new TouchHandler(web_view_));
+ web_layer_tree_view_impl_->set_widget(web_view_);
+ ConfigureSettings(web_view_->settings());
+ web_view_->setMainFrame(blink::WebLocalFrame::create(this));
+
+ GURL url(response->url);
+
+ WebURLRequestExtraData* extra_data = new WebURLRequestExtraData;
+ extra_data->synthetic_response = response.Pass();
+
+ blink::WebURLRequest web_request;
+ web_request.initialize();
+ web_request.setURL(url);
+ web_request.setExtraData(extra_data);
+
+ web_view_->mainFrame()->loadRequest(web_request);
+}
+
+void HTMLDocument::UpdateWebviewSizeFromViewSize() {
+ device_pixel_ratio_ = root_->viewport_metrics().device_pixel_ratio;
+ web_view_->setDeviceScaleFactor(device_pixel_ratio_);
+ const gfx::Size size_in_pixels(root_->bounds().width, root_->bounds().height);
+ const gfx::Size size_in_dips = gfx::ConvertSizeToDIP(
+ root_->viewport_metrics().device_pixel_ratio, size_in_pixels);
+ web_view_->resize(
+ blink::WebSize(size_in_dips.width(), size_in_dips.height()));
+ web_layer_tree_view_impl_->setViewportSize(size_in_pixels);
+}
+
+blink::WebStorageNamespace* HTMLDocument::createSessionStorageNamespace() {
+ return new WebStorageNamespaceImpl();
+}
+
+void HTMLDocument::initializeLayerTreeView() {
+ if (is_headless_) {
+ web_layer_tree_view_impl_.reset(
+ new WebLayerTreeViewImpl(compositor_thread_, nullptr, nullptr));
+ return;
+ }
+
+ ServiceProviderPtr surfaces_service_provider;
+ shell_->ConnectToApplication("mojo:surfaces_service",
+ GetProxy(&surfaces_service_provider), nullptr);
+ mojo::SurfacePtr surface;
+ ConnectToService(surfaces_service_provider.get(), &surface);
+
+ ServiceProviderPtr gpu_service_provider;
+ // TODO(jamesr): Should be mojo:gpu_service
+ shell_->ConnectToApplication("mojo:native_viewport_service",
+ GetProxy(&gpu_service_provider), nullptr);
+ mojo::GpuPtr gpu_service;
+ ConnectToService(gpu_service_provider.get(), &gpu_service);
+ web_layer_tree_view_impl_.reset(new WebLayerTreeViewImpl(
+ compositor_thread_, surface.Pass(), gpu_service.Pass()));
+}
+
+blink::WebLayerTreeView* HTMLDocument::layerTreeView() {
+ return web_layer_tree_view_impl_.get();
+}
+
+blink::WebMediaPlayer* HTMLDocument::createMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client) {
+ return createMediaPlayer(frame, url, client, nullptr);
+}
+
+blink::WebMediaPlayer* HTMLDocument::createMediaPlayer(
+ blink::WebLocalFrame* frame,
+ const blink::WebURL& url,
+ blink::WebMediaPlayerClient* client,
+ blink::WebContentDecryptionModule* initial_cdm) {
+ blink::WebMediaPlayer* player =
+ web_media_player_factory_
+ ? web_media_player_factory_->CreateMediaPlayer(
+ frame, url, client, GetMediaPermission(), GetCdmFactory(),
+ initial_cdm, shell_)
+ : nullptr;
+ return player;
+}
+
+blink::WebFrame* HTMLDocument::createChildFrame(
+ blink::WebLocalFrame* parent,
+ const blink::WebString& frameName,
+ blink::WebSandboxFlags sandboxFlags) {
+ blink::WebLocalFrame* web_frame = blink::WebLocalFrame::create(this);
+ parent->appendChild(web_frame);
+ return web_frame;
+}
+
+void HTMLDocument::frameDetached(blink::WebFrame* frame) {
+ if (frame->parent())
+ frame->parent()->removeChild(frame);
+
+ // |frame| is invalid after here.
+ frame->close();
+}
+
+blink::WebCookieJar* HTMLDocument::cookieJar(blink::WebLocalFrame* frame) {
+ // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar.
+ // Either it should, as it once did, or we should find another solution here.
+ return blink::Platform::current()->cookieJar();
+}
+
+blink::WebNavigationPolicy HTMLDocument::decidePolicyForNavigation(
+ blink::WebLocalFrame* frame,
+ blink::WebDataSource::ExtraData* data,
+ const blink::WebURLRequest& request,
+ blink::WebNavigationType nav_type,
+ blink::WebNavigationPolicy default_policy,
+ bool is_redirect) {
+ if (CanNavigateLocally(frame, request))
+ return default_policy;
+
+ if (navigator_host_.get()) {
+ navigator_host_->RequestNavigate(
+ WebNavigationPolicyToNavigationTarget(default_policy),
+ mojo::URLRequest::From(request).Pass());
+ }
+
+ return blink::WebNavigationPolicyIgnore;
+}
+
+void HTMLDocument::didAddMessageToConsole(
+ const blink::WebConsoleMessage& message,
+ const blink::WebString& source_name,
+ unsigned source_line,
+ const blink::WebString& stack_trace) {
+ VLOG(1) << "[" << source_name.utf8() << "(" << source_line << ")] "
+ << message.text.utf8();
+}
+
+void HTMLDocument::didFinishLoad(blink::WebLocalFrame* frame) {
+ // TODO(msw): Notify AxProvider clients of updates on child frame loads.
+ did_finish_load_ = true;
+ // Bind any pending AxProviderImpl interface requests.
+ for (auto it : ax_provider_requests_)
+ ax_providers_.insert(WeakBindToRequest(new AxProviderImpl(web_view_), it));
+ STLDeleteElements(&ax_provider_requests_);
+}
+
+void HTMLDocument::didNavigateWithinPage(
+ blink::WebLocalFrame* frame,
+ const blink::WebHistoryItem& history_item,
+ blink::WebHistoryCommitType commit_type) {
+ if (navigator_host_.get())
+ navigator_host_->DidNavigateLocally(history_item.urlString().utf8());
+}
+
+blink::WebEncryptedMediaClient* HTMLDocument::encryptedMediaClient() {
+ if (!web_encrypted_media_client_) {
+ web_encrypted_media_client_.reset(new media::WebEncryptedMediaClientImpl(
+ GetCdmFactory(), GetMediaPermission()));
+ }
+ return web_encrypted_media_client_.get();
+}
+
+void HTMLDocument::OnViewBoundsChanged(View* view,
+ const Rect& old_bounds,
+ const Rect& new_bounds) {
+ DCHECK_EQ(view, root_);
+ UpdateWebviewSizeFromViewSize();
+}
+
+void HTMLDocument::OnViewDestroyed(View* view) {
+ DCHECK_EQ(view, root_);
+ root_ = nullptr;
+ delete this;
+ mojo::ApplicationImpl::Terminate();
+}
+
+void HTMLDocument::OnViewInputEvent(View* view, const mojo::EventPtr& event) {
+ if (event->pointer_data) {
+ // Blink expects coordintes to be in DIPs.
+ event->pointer_data->x /= device_pixel_ratio_;
+ event->pointer_data->y /= device_pixel_ratio_;
+ event->pointer_data->screen_x /= device_pixel_ratio_;
+ event->pointer_data->screen_y /= device_pixel_ratio_;
+ }
+
+ if ((event->action == mojo::EVENT_TYPE_POINTER_DOWN ||
+ event->action == mojo::EVENT_TYPE_POINTER_UP ||
+ event->action == mojo::EVENT_TYPE_POINTER_CANCEL ||
+ event->action == mojo::EVENT_TYPE_POINTER_MOVE) &&
+ event->pointer_data->kind == mojo::POINTER_KIND_TOUCH) {
+ touch_handler_->OnTouchEvent(*event);
+ return;
+ }
+ scoped_ptr<blink::WebInputEvent> web_event =
+ event.To<scoped_ptr<blink::WebInputEvent>>();
+ if (web_event)
+ web_view_->handleInputEvent(*web_event);
+}
+
+media::MediaPermission* HTMLDocument::GetMediaPermission() {
+ if (!media_permission_)
+ media_permission_.reset(new media::DefaultMediaPermission(true));
+ return media_permission_.get();
+}
+
+media::CdmFactory* HTMLDocument::GetCdmFactory() {
+ if (!cdm_factory_)
+ cdm_factory_.reset(new media::DefaultCdmFactory());
+ return cdm_factory_.get();
+}
+
+} // namespace html_viewer