// Copyright (c) 2012 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 "chrome/renderer/plugins/plugin_placeholder.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/json/string_escape.h" #include "base/string_piece.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/render_messages.h" #include "chrome/common/prerender_messages.h" #include "chrome/renderer/chrome_content_renderer_client.h" #include "chrome/renderer/custom_menu_commands.h" #include "chrome/renderer/plugins/plugin_uma.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "grit/generated_resources.h" #include "grit/renderer_resources.h" #include "grit/webkit_strings.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebData.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebMenuItemInfo.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebRegularExpression.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextCaseSensitivity.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "webkit/glue/webpreferences.h" #include "webkit/plugins/npapi/plugin_group.h" #include "webkit/plugins/npapi/plugin_list.h" #include "webkit/plugins/webview_plugin.h" using content::RenderThread; using content::RenderView; using WebKit::WebContextMenuData; using WebKit::WebDocument; using WebKit::WebElement; using WebKit::WebFrame; using WebKit::WebMenuItemInfo; using WebKit::WebMouseEvent; using WebKit::WebNode; using WebKit::WebPlugin; using WebKit::WebPluginContainer; using webkit::WebPluginInfo; using WebKit::WebPluginParams; using WebKit::WebPoint; using WebKit::WebRegularExpression; using WebKit::WebScriptSource; using WebKit::WebString; using WebKit::WebURLRequest; using WebKit::WebVector; using webkit::WebViewPlugin; using webkit_glue::CppArgumentList; using webkit_glue::CppVariant; const char* const kPluginPlaceholderDataURL = "chrome://pluginplaceholderdata/"; #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) // Strings we used to parse the youtube plugin url. const char* const kSlashVSlash = "/v/"; const char* const kSlashESlash = "/e/"; #endif namespace { const PluginPlaceholder* g_last_active_menu = NULL; #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) // Helper function to get the youtube id from plugin params for old style // embedded youtube video. std::string GetYoutubeVideoId(const 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; } #endif } // static PluginPlaceholder* PluginPlaceholder::CreateMissingPlugin( RenderView* render_view, WebFrame* frame, const WebPluginParams& params) { const base::StringPiece template_html( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_BLOCKED_PLUGIN_HTML)); DictionaryValue values; values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); std::string html_data = jstemplate_builder::GetI18nTemplateHtml(template_html, &values); // |missing_plugin| will destroy itself when its WebViewPlugin is going away. PluginPlaceholder* missing_plugin = new PluginPlaceholder( render_view, frame, params, html_data, params.mimeType); missing_plugin->set_allow_loading(true); #if defined(ENABLE_PLUGIN_INSTALLATION) RenderThread::Get()->Send(new ChromeViewHostMsg_FindMissingPlugin( missing_plugin->routing_id(), missing_plugin->CreateRoutingId(), params.mimeType.utf8())); #else missing_plugin->OnDidNotFindMissingPlugin(); #endif return missing_plugin; } PluginPlaceholder* PluginPlaceholder::CreateErrorPlugin( RenderView* render_view, const FilePath& file_path) { DictionaryValue values; values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR)); const base::StringPiece template_html( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_BLOCKED_PLUGIN_HTML)); std::string html_data = jstemplate_builder::GetI18nTemplateHtml(template_html, &values); WebPluginParams params; // |missing_plugin| will destroy itself when its WebViewPlugin is going away. PluginPlaceholder* plugin = new PluginPlaceholder( render_view, NULL, params, html_data, params.mimeType); return plugin; } // static PluginPlaceholder* PluginPlaceholder::CreateBlockedPlugin( RenderView* render_view, WebFrame* frame, const WebPluginParams& params, const WebPluginInfo& plugin, const std::string& identifier, const string16& name, int template_id, int message_id) { string16 message = l10n_util::GetStringFUTF16(message_id, name); DictionaryValue values; values.SetString("message", message); values.SetString("name", name); values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); const base::StringPiece template_html( ResourceBundle::GetSharedInstance().GetRawDataResource(template_id)); DCHECK(!template_html.empty()) << "unable to load template. ID: " << template_id; std::string html_data = jstemplate_builder::GetI18nTemplateHtml( template_html, &values); // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. PluginPlaceholder* blocked_plugin = new PluginPlaceholder( render_view, frame, params, html_data, name); blocked_plugin->plugin_info_ = plugin; blocked_plugin->identifier_ = identifier; return blocked_plugin; } #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) // static PluginPlaceholder* PluginPlaceholder::CreateMobileYoutubePlugin( content::RenderView* render_view, WebFrame* frame, const WebPluginParams& params) { const base::StringPiece template_html( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_YOUTUBE_PLUGIN_HTML)); DictionaryValue values; values.SetString("video_id", GetYoutubeVideoId(params)); std::string html_data = jstemplate_builder::GetI18nTemplateHtml( template_html, &values); // |youtube_plugin| will destroy itself when its WebViewPlugin is going away. PluginPlaceholder* youtube_plugin = new PluginPlaceholder( render_view, frame, params, html_data, params.mimeType); return youtube_plugin; } #endif PluginPlaceholder::PluginPlaceholder(content::RenderView* render_view, WebFrame* frame, const WebPluginParams& params, const std::string& html_data, const string16& title) : content::RenderViewObserver(render_view), frame_(frame), plugin_params_(params), plugin_(WebViewPlugin::Create( this, render_view->GetWebkitPreferences(), html_data, GURL(kPluginPlaceholderDataURL))), title_(title), status_(new ChromeViewHostMsg_GetPluginInfo_Status), is_blocked_for_prerendering_(false), allow_loading_(false), #if defined(ENABLE_PLUGIN_INSTALLATION) placeholder_routing_id_(MSG_ROUTING_NONE), #endif hidden_(false), has_host_(false), finished_loading_(false) { RenderThread::Get()->AddObserver(this); } PluginPlaceholder::~PluginPlaceholder() { RenderThread::Get()->RemoveObserver(this); #if defined(ENABLE_PLUGIN_INSTALLATION) if (placeholder_routing_id_ == MSG_ROUTING_NONE) return; RenderThread::Get()->RemoveRoute(placeholder_routing_id_); if (has_host_) { RenderThread::Get()->Send( new ChromeViewHostMsg_RemovePluginPlaceholderHost( routing_id(), placeholder_routing_id_)); } #endif } #if defined(ENABLE_PLUGIN_INSTALLATION) int32 PluginPlaceholder::CreateRoutingId() { placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); RenderThread::Get()->AddRoute(placeholder_routing_id_, this); return placeholder_routing_id_; } #endif void PluginPlaceholder::SetStatus( const ChromeViewHostMsg_GetPluginInfo_Status& status) { status_->value = status.value; } 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("openAboutPlugins", base::Bind(&PluginPlaceholder::OpenAboutPluginsCallback, base::Unretained(this))); BindCallback("didFinishLoading", base::Bind(&PluginPlaceholder::DidFinishLoadingCallback, base::Unretained(this))); #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) BindCallback("openYoutubeURL", base::Bind(&PluginPlaceholder::OpenYoutubeUrlCallback, base::Unretained(this))); #endif } bool PluginPlaceholder::OnMessageReceived(const IPC::Message& message) { #if defined(ENABLE_PLUGIN_INSTALLATION) bool handled = true; IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, OnFoundMissingPlugin) IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, OnDidNotFindMissingPlugin) IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, OnStartedDownloadingPlugin) IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, OnFinishedDownloadingPlugin) IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, OnErrorDownloadingPlugin) IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, OnCancelledDownloadingPlugin) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() if (handled) return true; #endif // We don't swallow these messages because multiple blocked plugins have an // interest in them. IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) IPC_END_MESSAGE_MAP() return false; } void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) { CHECK(plugin_); if (!new_plugin) { MissingPluginReporter::GetInstance()->ReportPluginMissing( plugin_params_.mimeType.utf8(), plugin_params_.url); return; } // Set the new plug-in on the container before initializing it. WebPluginContainer* container = plugin_->container(); container->setPlugin(new_plugin); if (!new_plugin->initialize(container)) { // We couldn't initialize the new plug-in. Restore the old one and abort. container->setPlugin(plugin_); 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