diff options
author | aberent@chromium.org <aberent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-11 19:08:37 +0000 |
---|---|---|
committer | aberent@chromium.org <aberent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-11 19:08:37 +0000 |
commit | fe54cf1993710bfb47548f1aecb3bd5e65122c03 (patch) | |
tree | 3edb58882e508d4d51eea2c7ff4ed6ed0e27651c /components/plugins | |
parent | 5a0d0066f349bb4e52421cce97460477b137154f (diff) | |
download | chromium_src-fe54cf1993710bfb47548f1aecb3bd5e65122c03.zip chromium_src-fe54cf1993710bfb47548f1aecb3bd5e65122c03.tar.gz chromium_src-fe54cf1993710bfb47548f1aecb3bd5e65122c03.tar.bz2 |
Move renderer plugin code into a new component.
Android Webview needs to use some of the renderer plugin code, however
this code is currently in chrome. This commit moves the parts of this
code that don't depend on the rest of chrome into a new component.
BUG=283713
Review URL: https://codereview.chromium.org/23606022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228211 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/plugins')
-rw-r--r-- | components/plugins/OWNERS | 1 | ||||
-rw-r--r-- | components/plugins/renderer/DEPS | 13 | ||||
-rw-r--r-- | components/plugins/renderer/mobile_youtube_plugin.cc | 113 | ||||
-rw-r--r-- | components/plugins/renderer/mobile_youtube_plugin.h | 41 | ||||
-rw-r--r-- | components/plugins/renderer/plugin_placeholder.cc | 247 | ||||
-rw-r--r-- | components/plugins/renderer/plugin_placeholder.h | 115 | ||||
-rw-r--r-- | components/plugins/renderer/webview_plugin.cc | 224 | ||||
-rw-r--r-- | components/plugins/renderer/webview_plugin.h | 155 |
8 files changed, 909 insertions, 0 deletions
diff --git a/components/plugins/OWNERS b/components/plugins/OWNERS new file mode 100644 index 0000000..00985a8 --- /dev/null +++ b/components/plugins/OWNERS @@ -0,0 +1 @@ +bauerb@chromium.org diff --git a/components/plugins/renderer/DEPS b/components/plugins/renderer/DEPS new file mode 100644 index 0000000..40b689d --- /dev/null +++ b/components/plugins/renderer/DEPS @@ -0,0 +1,13 @@ +include_rules = [ + "+net", + + "+content/public/renderer", + "+content/public/common", + "+content/public/test", + "+third_party/WebKit/public", + "+third_party/re2", + "+skia", + "+webkit/common", + "+webkit/renderer", + "+ui/webui", +] diff --git a/components/plugins/renderer/mobile_youtube_plugin.cc b/components/plugins/renderer/mobile_youtube_plugin.cc new file mode 100644 index 0000000..3312312 --- /dev/null +++ b/components/plugins/renderer/mobile_youtube_plugin.cc @@ -0,0 +1,113 @@ +// Copyright 2013 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/plugins/renderer/mobile_youtube_plugin.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "content/public/common/content_constants.h" +#include "content/public/renderer/render_view.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "ui/webui/jstemplate_builder.h" + +using WebKit::WebFrame; +using WebKit::WebPlugin; +using WebKit::WebURLRequest; + +const char* const kSlashVSlash = "/v/"; +const char* const kSlashESlash = "/e/"; + +namespace { + +std::string GetYoutubeVideoId(const WebKit::WebPluginParams& params) { + GURL url(params.url); + std::string video_id = url.path().substr(strlen(kSlashVSlash)); + + // Extract just the video id + size_t video_id_end = video_id.find('&'); + if (video_id_end != std::string::npos) + video_id = video_id.substr(0, video_id_end); + return video_id; +} + +std::string HtmlData(const WebKit::WebPluginParams& params, + base::StringPiece template_html) { + base::DictionaryValue values; + values.SetString("video_id", GetYoutubeVideoId(params)); + return webui::GetI18nTemplateHtml(template_html, &values); +} + +bool IsValidYouTubeVideo(const std::string& path) { + unsigned len = strlen(kSlashVSlash); + + // check for more than just /v/ or /e/. + if (path.length() <= len) + return false; + + std::string str = StringToLowerASCII(path); + // Youtube flash url can start with /v/ or /e/. + if (strncmp(str.data(), kSlashVSlash, len) != 0 && + strncmp(str.data(), kSlashESlash, len) != 0) + return false; + + // Start after /v/ + for (unsigned i = len; i < path.length(); i++) { + char c = str[i]; + if (isalpha(c) || isdigit(c) || c == '_' || c == '-') + continue; + // The url can have more parameters such as &hl=en after the video id. + // Once we start seeing extra parameters we can return true. + return c == '&' && i > len; + } + return true; +} + +} // namespace + +namespace plugins { + +MobileYouTubePlugin::MobileYouTubePlugin(content::RenderView* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params, + base::StringPiece& template_html, + GURL placeholderDataUrl) + : PluginPlaceholder(render_view, + frame, + params, + HtmlData(params, template_html), + placeholderDataUrl) {} + +// static +bool MobileYouTubePlugin::IsYouTubeURL(const GURL& url, + const std::string& mime_type) { + std::string host = url.host(); + bool is_youtube = EndsWith(host, "youtube.com", true) || + EndsWith(host, "youtube-nocookie.com", true); + + return is_youtube && IsValidYouTubeVideo(url.path()) && + LowerCaseEqualsASCII(mime_type, content::kFlashPluginSwfMimeType); +} + +void MobileYouTubePlugin::OpenYoutubeUrlCallback( + const webkit_glue::CppArgumentList& args, + webkit_glue::CppVariant* result) { + std::string youtube("vnd.youtube:"); + GURL url(youtube.append(GetYoutubeVideoId(GetPluginParams()))); + WebURLRequest request; + request.initialize(); + request.setURL(url); + render_view()->LoadURLExternally( + GetFrame(), request, WebKit::WebNavigationPolicyNewForegroundTab); +} +void MobileYouTubePlugin::BindWebFrame(WebFrame* frame) { + PluginPlaceholder::BindWebFrame(frame); + BindCallback("openYoutubeURL", + base::Bind(&MobileYouTubePlugin::OpenYoutubeUrlCallback, + base::Unretained(this))); +} + +} // namespace plugins diff --git a/components/plugins/renderer/mobile_youtube_plugin.h b/components/plugins/renderer/mobile_youtube_plugin.h new file mode 100644 index 0000000..9a88ce0 --- /dev/null +++ b/components/plugins/renderer/mobile_youtube_plugin.h @@ -0,0 +1,41 @@ +// Copyright 2013 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. + +#ifndef COMPONENTS_PLUGINS_RENDERER_MOBILE_YOUTUBE_PLUGIN_H_ +#define COMPONENTS_PLUGINS_RENDERER_MOBILE_YOUTUBE_PLUGIN_H_ + +#include "components/plugins/renderer/plugin_placeholder.h" + +namespace plugins { + +// Class representing placeholders for old style embedded youtube video on +// mobile device. For old style embedded youtube video, it has a url in the form +// of http://www.youtube.com/v/VIDEO_ID. This placeholder replaces the url with +// a simple html page and clicking the play image redirects the user to the +// mobile youtube app. +class MobileYouTubePlugin : public PluginPlaceholder { + public: + MobileYouTubePlugin(content::RenderView* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params, + base::StringPiece& template_html, + GURL placeholderDataUrl); + + // Whether this is a youtube url. + static bool IsYouTubeURL(const GURL& url, const std::string& mime_type); + + private: + // Opens a youtube app in the current tab. + void OpenYoutubeUrlCallback(const webkit_glue::CppArgumentList& args, + webkit_glue::CppVariant* result); + + // WebViewPlugin::Delegate (via PluginPlaceholder) method + virtual void BindWebFrame(WebKit::WebFrame* frame) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(MobileYouTubePlugin); +}; + +} // namespace plugins + +#endif // COMPONENTS_PLUGINS_RENDERER_MOBILE_YOUTUBE_PLUGIN_H_ diff --git a/components/plugins/renderer/plugin_placeholder.cc b/components/plugins/renderer/plugin_placeholder.cc new file mode 100644 index 0000000..03d13b1 --- /dev/null +++ b/components/plugins/renderer/plugin_placeholder.cc @@ -0,0 +1,247 @@ +// Copyright 2013 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/plugins/renderer/plugin_placeholder.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/json/string_escape.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "content/public/common/content_constants.h" +#include "content/public/common/context_menu_params.h" +#include "content/public/renderer/render_thread.h" +#include "content/public/renderer/render_view.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebScriptSource.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "third_party/re2/re2/re2.h" + +using content::RenderThread; +using WebKit::WebElement; +using WebKit::WebFrame; +using WebKit::WebMouseEvent; +using WebKit::WebNode; +using WebKit::WebPlugin; +using WebKit::WebPluginContainer; +using WebKit::WebPluginParams; +using WebKit::WebScriptSource; +using WebKit::WebURLRequest; +using webkit_glue::CppArgumentList; +using webkit_glue::CppVariant; + +namespace plugins { + +PluginPlaceholder::PluginPlaceholder(content::RenderView* render_view, + WebFrame* frame, + const WebPluginParams& params, + const std::string& html_data, + GURL placeholderDataUrl) + : content::RenderViewObserver(render_view), + frame_(frame), + plugin_params_(params), + plugin_(WebViewPlugin::Create(this, + render_view->GetWebkitPreferences(), + html_data, + placeholderDataUrl)), + is_blocked_for_prerendering_(false), + allow_loading_(false), + hidden_(false), + finished_loading_(false) {} + +PluginPlaceholder::~PluginPlaceholder() {} + +void PluginPlaceholder::BindWebFrame(WebFrame* frame) { + BindToJavascript(frame, "plugin"); + BindCallback( + "load", + base::Bind(&PluginPlaceholder::LoadCallback, base::Unretained(this))); + BindCallback( + "hide", + base::Bind(&PluginPlaceholder::HideCallback, base::Unretained(this))); + BindCallback("didFinishLoading", + base::Bind(&PluginPlaceholder::DidFinishLoadingCallback, + base::Unretained(this))); +} + +void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) { + CHECK(plugin_); + if (!new_plugin) return; + WebPluginContainer* container = plugin_->container(); + // Set the new plug-in on the container before initializing it. + container->setPlugin(new_plugin); + // Save the element in case the plug-in is removed from the page during + // initialization. + WebElement element = container->element(); + if (!new_plugin->initialize(container)) { + // We couldn't initialize the new plug-in. Restore the old one and abort. + container->setPlugin(plugin_); + return; + } + + // The plug-in has been removed from the page. Destroy the old plug-in + // (which will destroy us). + if (!element.pluginContainer()) { + plugin_->destroy(); + return; + } + + // During initialization, the new plug-in might have replaced itself in turn + // with another plug-in. Make sure not to use the passed in |new_plugin| after + // this point. + new_plugin = container->plugin(); + + plugin_->RestoreTitleText(); + container->invalidate(); + container->reportGeometry(); + plugin_->ReplayReceivedData(new_plugin); + plugin_->destroy(); +} + +void PluginPlaceholder::HidePlugin() { + hidden_ = true; + WebPluginContainer* container = plugin_->container(); + WebElement element = container->element(); + element.setAttribute("style", "display: none;"); + // If we have a width and height, search for a parent (often <div>) with the + // same dimensions. If we find such a parent, hide that as well. + // This makes much more uncovered page content usable (including clickable) + // as opposed to merely visible. + // TODO(cevans) -- it's a foul heurisitc but we're going to tolerate it for + // now for these reasons: + // 1) Makes the user experience better. + // 2) Foulness is encapsulated within this single function. + // 3) Confidence in no fasle positives. + // 4) Seems to have a good / low false negative rate at this time. + if (element.hasAttribute("width") && element.hasAttribute("height")) { + std::string width_str("width:[\\s]*"); + width_str += element.getAttribute("width").utf8().data(); + if (EndsWith(width_str, "px", false)) { + width_str = width_str.substr(0, width_str.length() - 2); + } + TrimWhitespace(width_str, TRIM_TRAILING, &width_str); + width_str += "[\\s]*px"; + std::string height_str("height:[\\s]*"); + height_str += element.getAttribute("height").utf8().data(); + if (EndsWith(height_str, "px", false)) { + height_str = height_str.substr(0, height_str.length() - 2); + } + TrimWhitespace(height_str, TRIM_TRAILING, &height_str); + height_str += "[\\s]*px"; + WebNode parent = element; + while (!parent.parentNode().isNull()) { + parent = parent.parentNode(); + if (!parent.isElementNode()) + continue; + element = parent.toConst<WebElement>(); + if (element.hasAttribute("style")) { + std::string style_str = element.getAttribute("style").utf8(); + if (RE2::PartialMatch(style_str, width_str) && + RE2::PartialMatch(style_str, height_str)) + element.setAttribute("style", "display: none;"); + } + } + } +} + +void PluginPlaceholder::WillDestroyPlugin() { delete this; } + +void PluginPlaceholder::SetMessage(const string16& message) { + message_ = message; + if (finished_loading_) + UpdateMessage(); +} + +void PluginPlaceholder::UpdateMessage() { + std::string script = + "window.setMessage(" + base::GetDoubleQuotedJson(message_) + ")"; + plugin_->web_view()->mainFrame()->executeScript( + WebScriptSource(ASCIIToUTF16(script))); +} + +void PluginPlaceholder::ShowContextMenu(const WebMouseEvent& event) { + // Does nothing by default. Will be overridden if a specific browser wants + // a context menu. + return; +} + +void PluginPlaceholder::OnLoadBlockedPlugins(const std::string& identifier) { + if (!identifier.empty() && identifier != identifier_) + return; + + RenderThread::Get()->RecordUserMetrics("Plugin_Load_UI"); + LoadPlugin(); +} + +void PluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { + // Prerendering can only be enabled prior to a RenderView's first navigation, + // so no BlockedPlugin should see the notification that enables prerendering. + DCHECK(!is_prerendering); + if (is_blocked_for_prerendering_ && !is_prerendering) + LoadPlugin(); +} + +void PluginPlaceholder::LoadPlugin() { + // This is not strictly necessary but is an important defense in case the + // event propagation changes between "close" vs. "click-to-play". + if (hidden_) + return; + if (!allow_loading_) { + NOTREACHED(); + return; + } + + // TODO(mmenke): In the case of prerendering, feed into + // ChromeContentRendererClient::CreatePlugin instead, to + // reduce the chance of future regressions. + WebPlugin* plugin = + render_view()->CreatePlugin(frame_, plugin_info_, plugin_params_); + ReplacePlugin(plugin); +} + +void PluginPlaceholder::LoadCallback(const CppArgumentList& args, + CppVariant* result) { + RenderThread::Get()->RecordUserMetrics("Plugin_Load_Click"); + LoadPlugin(); +} + +void PluginPlaceholder::HideCallback(const CppArgumentList& args, + CppVariant* result) { + RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Click"); + HidePlugin(); +} + +void PluginPlaceholder::DidFinishLoadingCallback(const CppArgumentList& args, + CppVariant* result) { + finished_loading_ = true; + if (message_.length() > 0) + UpdateMessage(); +} + +void PluginPlaceholder::SetPluginInfo( + const content::WebPluginInfo& plugin_info) { + plugin_info_ = plugin_info; +} + +const content::WebPluginInfo& PluginPlaceholder::GetPluginInfo() const { + return plugin_info_; +} + +void PluginPlaceholder::SetIdentifier(const std::string& identifier) { + identifier_ = identifier; +} + +WebKit::WebFrame* PluginPlaceholder::GetFrame() { return frame_; } + +const WebKit::WebPluginParams& PluginPlaceholder::GetPluginParams() const { + return plugin_params_; +} + +} // namespace plugins diff --git a/components/plugins/renderer/plugin_placeholder.h b/components/plugins/renderer/plugin_placeholder.h new file mode 100644 index 0000000..83a13e6 --- /dev/null +++ b/components/plugins/renderer/plugin_placeholder.h @@ -0,0 +1,115 @@ +// Copyright 2013 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. + +#ifndef COMPONENTS_PLUGINS_RENDERER_PLUGIN_PLACEHOLDER_H_ +#define COMPONENTS_PLUGINS_RENDERER_PLUGIN_PLACEHOLDER_H_ + +#include "components/plugins/renderer/webview_plugin.h" +#include "content/public/common/webplugininfo.h" +#include "content/public/renderer/context_menu_client.h" +#include "content/public/renderer/render_process_observer.h" +#include "content/public/renderer/render_view_observer.h" +#include "third_party/WebKit/public/web/WebPluginParams.h" +#include "webkit/renderer/cpp_bound_class.h" + +namespace content { +struct WebPluginInfo; +} + +namespace plugins { +// Placeholders can be used if a plug-in is missing or not available +// (blocked or disabled). +class PluginPlaceholder : public content::RenderViewObserver, + public webkit_glue::CppBoundClass, + public WebViewPlugin::Delegate { + public: + + WebViewPlugin* plugin() { return plugin_; } + + void set_blocked_for_prerendering(bool blocked_for_prerendering) { + is_blocked_for_prerendering_ = blocked_for_prerendering; + } + + void set_allow_loading(bool allow_loading) { allow_loading_ = allow_loading; } + + protected: + // |render_view| and |frame| are weak pointers. If either one is going away, + // our |plugin_| will be destroyed as well and will notify us. + PluginPlaceholder(content::RenderView* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params, + const std::string& html_data, + GURL placeholderDataUrl); + + virtual ~PluginPlaceholder(); + + void OnLoadBlockedPlugins(const std::string& identifier); + void OnSetIsPrerendering(bool is_prerendering); + + void SetMessage(const string16& message); + void SetPluginInfo(const content::WebPluginInfo& plugin_info); + const content::WebPluginInfo& GetPluginInfo() const; + void SetIdentifier(const std::string& identifier); + WebKit::WebFrame* GetFrame(); + const WebKit::WebPluginParams& GetPluginParams() const; + bool LoadingAllowed() const { return allow_loading_; } + + // Replace this placeholder with a different plugin (which could be + // a placeholder again). + void ReplacePlugin(WebKit::WebPlugin* new_plugin); + + // Hide this placeholder. + void HidePlugin(); + + // Load the blocked plugin. + void LoadPlugin(); + + // WebViewPlugin::Delegate method: + virtual void BindWebFrame(WebKit::WebFrame* frame) OVERRIDE; + + private: + // WebViewPlugin::Delegate methods: + virtual void WillDestroyPlugin() OVERRIDE; + virtual void ShowContextMenu(const WebKit::WebMouseEvent&) OVERRIDE; + + // Javascript callbacks: + // All ignore arguments (which are, however, required by caller) and return + // nothing. + + // Load the blocked plugin by calling LoadPlugin(). + void LoadCallback(const webkit_glue::CppArgumentList& args, + webkit_glue::CppVariant* result); + + // Hide the blocked plugin by calling HidePlugin(). + void HideCallback(const webkit_glue::CppArgumentList& args, + webkit_glue::CppVariant* result); + + void DidFinishLoadingCallback(const webkit_glue::CppArgumentList& args, + webkit_glue::CppVariant* result); + + void UpdateMessage(); + + WebKit::WebFrame* frame_; + WebKit::WebPluginParams plugin_params_; + WebViewPlugin* plugin_; + + content::WebPluginInfo plugin_info_; + + string16 message_; + + // True iff the plugin was blocked because the page was being prerendered. + // Plugin will automatically be loaded when the page is displayed. + bool is_blocked_for_prerendering_; + bool allow_loading_; + + bool hidden_; + bool finished_loading_; + std::string identifier_; + + DISALLOW_COPY_AND_ASSIGN(PluginPlaceholder); +}; + +} // namespace plugins + +#endif // COMPONENTS_PLUGINS_RENDERER_PLUGIN_PLACEHOLDER_H_ diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc new file mode 100644 index 0000000..20969d5 --- /dev/null +++ b/components/plugins/renderer/webview_plugin.cc @@ -0,0 +1,224 @@ +// Copyright 2013 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/plugins/renderer/webview_plugin.h" + +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" +#include "base/safe_numerics.h" +#include "content/public/renderer/web_preferences.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/platform/WebURLResponse.h" +#include "third_party/WebKit/public/web/WebCursorInfo.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "webkit/common/webpreferences.h" + +using WebKit::WebCanvas; +using WebKit::WebCursorInfo; +using WebKit::WebDragData; +using WebKit::WebDragOperationsMask; +using WebKit::WebFrame; +using WebKit::WebImage; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebPlugin; +using WebKit::WebPluginContainer; +using WebKit::WebPoint; +using WebKit::WebRect; +using WebKit::WebSize; +using WebKit::WebString; +using WebKit::WebURLError; +using WebKit::WebURLRequest; +using WebKit::WebURLResponse; +using WebKit::WebVector; +using WebKit::WebView; + +WebViewPlugin::WebViewPlugin(WebViewPlugin::Delegate* delegate) + : delegate_(delegate), container_(NULL), finished_loading_(false) { + web_view_ = WebView::create(this); + web_view_->initializeMainFrame(this); +} + +// static +WebViewPlugin* WebViewPlugin::Create(WebViewPlugin::Delegate* delegate, + const WebPreferences& preferences, + const std::string& html_data, + const GURL& url) { + WebViewPlugin* plugin = new WebViewPlugin(delegate); + WebView* web_view = plugin->web_view(); + content::ApplyWebPreferences(preferences, web_view); + web_view->mainFrame()->loadHTMLString(html_data, url); + return plugin; +} + +WebViewPlugin::~WebViewPlugin() { web_view_->close(); } + +void WebViewPlugin::ReplayReceivedData(WebPlugin* plugin) { + if (!response_.isNull()) { + plugin->didReceiveResponse(response_); + size_t total_bytes = 0; + for (std::list<std::string>::iterator it = data_.begin(); it != data_.end(); + ++it) { + plugin->didReceiveData( + it->c_str(), base::checked_numeric_cast<int, size_t>(it->length())); + total_bytes += it->length(); + } + UMA_HISTOGRAM_MEMORY_KB( + "PluginDocument.Memory", + (base::checked_numeric_cast<int, size_t>(total_bytes / 1024))); + UMA_HISTOGRAM_COUNTS( + "PluginDocument.NumChunks", + (base::checked_numeric_cast<int, size_t>(data_.size()))); + } + if (finished_loading_) { + plugin->didFinishLoading(); + } + if (error_) { + plugin->didFailLoading(*error_); + } +} + +void WebViewPlugin::RestoreTitleText() { + if (container_) + container_->element().setAttribute("title", old_title_); +} + +WebPluginContainer* WebViewPlugin::container() const { return container_; } + +bool WebViewPlugin::initialize(WebPluginContainer* container) { + container_ = container; + if (container_) + old_title_ = container_->element().getAttribute("title"); + return true; +} + +void WebViewPlugin::destroy() { + if (delegate_) { + delegate_->WillDestroyPlugin(); + delegate_ = NULL; + } + container_ = NULL; + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); +} + +NPObject* WebViewPlugin::scriptableObject() { return NULL; } + +struct _NPP* WebViewPlugin::pluginNPP() { return NULL; } + +bool WebViewPlugin::getFormValue(WebString& value) { return false; } + +void WebViewPlugin::paint(WebCanvas* canvas, const WebRect& rect) { + gfx::Rect paint_rect = gfx::IntersectRects(rect_, rect); + if (paint_rect.IsEmpty()) + return; + + paint_rect.Offset(-rect_.x(), -rect_.y()); + + canvas->translate(SkIntToScalar(rect_.x()), SkIntToScalar(rect_.y())); + canvas->save(); + + web_view_->layout(); + web_view_->paint(canvas, paint_rect); + + canvas->restore(); +} + +// Coordinates are relative to the containing window. +void WebViewPlugin::updateGeometry(const WebRect& frame_rect, + const WebRect& clip_rect, + const WebVector<WebRect>& cut_out_rects, + bool is_visible) { + if (static_cast<gfx::Rect>(frame_rect) != rect_) { + rect_ = frame_rect; + web_view_->resize(WebSize(frame_rect.width, frame_rect.height)); + } +} + +bool WebViewPlugin::acceptsInputEvents() { return true; } + +bool WebViewPlugin::handleInputEvent(const WebInputEvent& event, + WebCursorInfo& cursor) { + // For tap events, don't handle them. They will be converted to + // mouse events later and passed to here. + if (event.type == WebInputEvent::GestureTap) + return false; + + if (event.type == WebInputEvent::ContextMenu) { + if (delegate_) { + const WebMouseEvent& mouse_event = + reinterpret_cast<const WebMouseEvent&>(event); + delegate_->ShowContextMenu(mouse_event); + } + return true; + } + current_cursor_ = cursor; + bool handled = web_view_->handleInputEvent(event); + cursor = current_cursor_; + + return handled; +} + +void WebViewPlugin::didReceiveResponse(const WebURLResponse& response) { + DCHECK(response_.isNull()); + response_ = response; +} + +void WebViewPlugin::didReceiveData(const char* data, int data_length) { + data_.push_back(std::string(data, data_length)); +} + +void WebViewPlugin::didFinishLoading() { + DCHECK(!finished_loading_); + finished_loading_ = true; +} + +void WebViewPlugin::didFailLoading(const WebURLError& error) { + DCHECK(!error_.get()); + error_.reset(new WebURLError(error)); +} + +bool WebViewPlugin::acceptsLoadDrops() { return false; } + +void WebViewPlugin::setToolTipText(const WebString& text, + WebKit::WebTextDirection hint) { + if (container_) + container_->element().setAttribute("title", text); +} + +void WebViewPlugin::startDragging(WebFrame*, + const WebDragData&, + WebDragOperationsMask, + const WebImage&, + const WebPoint&) { + // Immediately stop dragging. + web_view_->dragSourceSystemDragEnded(); +} + +void WebViewPlugin::didInvalidateRect(const WebRect& rect) { + if (container_) + container_->invalidateRect(rect); +} + +void WebViewPlugin::didChangeCursor(const WebCursorInfo& cursor) { + current_cursor_ = cursor; +} + +void WebViewPlugin::didClearWindowObject(WebFrame* frame) { + if (delegate_) + delegate_->BindWebFrame(frame); +} + +void WebViewPlugin::didReceiveResponse(WebFrame* frame, + unsigned identifier, + const WebURLResponse& response) { + WebFrameClient::didReceiveResponse(frame, identifier, response); +} diff --git a/components/plugins/renderer/webview_plugin.h b/components/plugins/renderer/webview_plugin.h new file mode 100644 index 0000000..13e480c --- /dev/null +++ b/components/plugins/renderer/webview_plugin.h @@ -0,0 +1,155 @@ +// Copyright 2013 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. + +#ifndef COMPONENTS_PLUGINS_RENDERER_WEBVIEW_PLUGIN_H_ +#define COMPONENTS_PLUGINS_RENDERER_WEBVIEW_PLUGIN_H_ + +#include <list> + +#include "base/memory/scoped_ptr.h" +#include "base/sequenced_task_runner_helpers.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURLResponse.h" +#include "third_party/WebKit/public/web/WebCursorInfo.h" +#include "third_party/WebKit/public/web/WebFrameClient.h" +#include "third_party/WebKit/public/web/WebPlugin.h" +#include "third_party/WebKit/public/web/WebViewClient.h" + +struct WebPreferences; + +namespace WebKit { +class WebMouseEvent; +} + +// This class implements the WebPlugin interface by forwarding drawing and +// handling input events to a WebView. +// It can be used as a placeholder for an actual plugin, using HTML for the UI. +// To show HTML data inside the WebViewPlugin, +// call web_view->mainFrame()->loadHTMLString() with the HTML data and a fake +// chrome:// URL as origin. + +class WebViewPlugin : public WebKit::WebPlugin, + public WebKit::WebViewClient, + public WebKit::WebFrameClient { + public: + class Delegate { + public: + // Bind |frame| to a Javascript object, enabling the delegate to receive + // callback methods from Javascript inside the WebFrame. + // This method is called from WebFrameClient::didClearWindowObject. + virtual void BindWebFrame(WebKit::WebFrame* frame) = 0; + + // Called before the WebViewPlugin is destroyed. The delegate should delete + // itself here. + virtual void WillDestroyPlugin() = 0; + + // Called upon a context menu event. + virtual void ShowContextMenu(const WebKit::WebMouseEvent&) = 0; + }; + + explicit WebViewPlugin(Delegate* delegate); + + // Convenience method to set up a new WebViewPlugin using |preferences| + // and displaying |html_data|. |url| should be a (fake) chrome:// URL; it is + // only used for navigation and never actually resolved. + static WebViewPlugin* Create(Delegate* delegate, + const WebPreferences& preferences, + const std::string& html_data, + const GURL& url); + + WebKit::WebView* web_view() { return web_view_; } + + // When loading a plug-in document (i.e. a full page plug-in not embedded in + // another page), we save all data that has been received, and replay it with + // this method on the actual plug-in. + void ReplayReceivedData(WebKit::WebPlugin* plugin); + + void RestoreTitleText(); + + // WebPlugin methods: + virtual WebKit::WebPluginContainer* container() const; + virtual bool initialize(WebKit::WebPluginContainer*); + virtual void destroy(); + + virtual NPObject* scriptableObject(); + virtual struct _NPP* pluginNPP(); + + virtual bool getFormValue(WebKit::WebString& value); + + virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect); + + // Coordinates are relative to the containing window. + virtual void updateGeometry( + const WebKit::WebRect& frame_rect, + const WebKit::WebRect& clip_rect, + const WebKit::WebVector<WebKit::WebRect>& cut_out_rects, + bool is_visible); + + virtual void updateFocus(bool) {} + virtual void updateVisibility(bool) {} + + virtual bool acceptsInputEvents(); + virtual bool handleInputEvent(const WebKit::WebInputEvent& event, + WebKit::WebCursorInfo& cursor_info); + + virtual void didReceiveResponse(const WebKit::WebURLResponse& response); + virtual void didReceiveData(const char* data, int data_length); + virtual void didFinishLoading(); + virtual void didFailLoading(const WebKit::WebURLError& error); + + // Called in response to WebPluginContainer::loadFrameRequest + virtual void didFinishLoadingFrameRequest(const WebKit::WebURL& url, + void* notifyData) {} + virtual void didFailLoadingFrameRequest(const WebKit::WebURL& url, + void* notify_data, + const WebKit::WebURLError& error) {} + + // WebViewClient methods: + virtual bool acceptsLoadDrops(); + + virtual void setToolTipText(const WebKit::WebString&, + WebKit::WebTextDirection); + + virtual void startDragging(WebKit::WebFrame* frame, + const WebKit::WebDragData& drag_data, + WebKit::WebDragOperationsMask mask, + const WebKit::WebImage& image, + const WebKit::WebPoint& point); + + // WebWidgetClient methods: + virtual void didInvalidateRect(const WebKit::WebRect&); + virtual void didChangeCursor(const WebKit::WebCursorInfo& cursor); + + // WebFrameClient methods: + virtual void didClearWindowObject(WebKit::WebFrame* frame); + + // This method is defined in WebPlugin as well as in WebFrameClient, but with + // different parameters. We only care about implementing the WebPlugin + // version, so we implement this method and call the default in WebFrameClient + // (which does nothing) to correctly overload it. + virtual void didReceiveResponse(WebKit::WebFrame* frame, + unsigned identifier, + const WebKit::WebURLResponse& response); + + private: + friend class base::DeleteHelper<WebViewPlugin>; + virtual ~WebViewPlugin(); + + Delegate* delegate_; + // Destroys itself. + WebKit::WebCursorInfo current_cursor_; + // Owns us. + WebKit::WebPluginContainer* container_; + // Owned by us, deleted via |close()|. + WebKit::WebView* web_view_; + gfx::Rect rect_; + + WebKit::WebURLResponse response_; + std::list<std::string> data_; + bool finished_loading_; + scoped_ptr<WebKit::WebURLError> error_; + WebKit::WebString old_title_; +}; + +#endif // COMPONENTS_PLUGINS_RENDERER_WEBVIEW_PLUGIN_H_ |