| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/renderer/plugins/plugin_placeholder.h" | 5 #include "chrome/renderer/plugins/chrome_plugin_placeholder.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/json/string_escape.h" | |
| 10 #include "base/strings/string_piece.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "base/strings/utf_string_conversions.h" | 7 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/values.h" | 8 #include "base/values.h" |
| 14 #include "chrome/common/prerender_messages.h" | 9 #include "chrome/common/prerender_messages.h" |
| 15 #include "chrome/common/render_messages.h" | 10 #include "chrome/common/render_messages.h" |
| 16 #include "chrome/renderer/chrome_content_renderer_client.h" | 11 #include "chrome/renderer/chrome_content_renderer_client.h" |
| 17 #include "chrome/renderer/custom_menu_commands.h" | 12 #include "chrome/renderer/custom_menu_commands.h" |
| 18 #include "chrome/renderer/plugins/plugin_uma.h" | |
| 19 #include "content/public/common/content_constants.h" | |
| 20 #include "content/public/common/context_menu_params.h" | 13 #include "content/public/common/context_menu_params.h" |
| 21 #include "content/public/renderer/render_thread.h" | 14 #include "content/public/renderer/render_thread.h" |
| 22 #include "content/public/renderer/render_view.h" | 15 #include "content/public/renderer/render_view.h" |
| 23 #include "grit/generated_resources.h" | 16 #include "grit/generated_resources.h" |
| 24 #include "grit/renderer_resources.h" | 17 #include "grit/renderer_resources.h" |
| 25 #include "grit/webkit_strings.h" | 18 #include "grit/webkit_strings.h" |
| 26 #include "third_party/WebKit/public/platform/WebData.h" | |
| 27 #include "third_party/WebKit/public/platform/WebPoint.h" | |
| 28 #include "third_party/WebKit/public/platform/WebURLRequest.h" | |
| 29 #include "third_party/WebKit/public/platform/WebVector.h" | |
| 30 #include "third_party/WebKit/public/web/WebContextMenuData.h" | |
| 31 #include "third_party/WebKit/public/web/WebDocument.h" | 19 #include "third_party/WebKit/public/web/WebDocument.h" |
| 32 #include "third_party/WebKit/public/web/WebElement.h" | |
| 33 #include "third_party/WebKit/public/web/WebFrame.h" | 20 #include "third_party/WebKit/public/web/WebFrame.h" |
| 34 #include "third_party/WebKit/public/web/WebInputEvent.h" | 21 #include "third_party/WebKit/public/web/WebInputEvent.h" |
| 35 #include "third_party/WebKit/public/web/WebPluginContainer.h" | |
| 36 #include "third_party/WebKit/public/web/WebScriptSource.h" | 22 #include "third_party/WebKit/public/web/WebScriptSource.h" |
| 37 #include "third_party/WebKit/public/web/WebView.h" | |
| 38 #include "third_party/re2/re2/re2.h" | |
| 39 #include "ui/base/l10n/l10n_util.h" | 23 #include "ui/base/l10n/l10n_util.h" |
| 40 #include "ui/base/resource/resource_bundle.h" | 24 #include "ui/base/resource/resource_bundle.h" |
| 41 #include "ui/webui/jstemplate_builder.h" | 25 #include "ui/webui/jstemplate_builder.h" |
| 42 | 26 |
| 43 using content::RenderThread; | 27 using content::RenderThread; |
| 44 using content::RenderView; | 28 using content::RenderView; |
| 45 using WebKit::WebContextMenuData; | |
| 46 using WebKit::WebDocument; | 29 using WebKit::WebDocument; |
| 47 using WebKit::WebElement; | 30 using WebKit::WebElement; |
| 48 using WebKit::WebFrame; | 31 using WebKit::WebFrame; |
| 49 using WebKit::WebMouseEvent; | 32 using WebKit::WebMouseEvent; |
| 50 using WebKit::WebNode; | 33 using WebKit::WebNode; |
| 51 using WebKit::WebPlugin; | 34 using WebKit::WebPlugin; |
| 52 using WebKit::WebPluginContainer; | 35 using WebKit::WebPluginContainer; |
| 53 using WebKit::WebPluginParams; | 36 using WebKit::WebPluginParams; |
| 54 using WebKit::WebPoint; | |
| 55 using WebKit::WebScriptSource; | |
| 56 using WebKit::WebString; | |
| 57 using WebKit::WebURLRequest; | |
| 58 using WebKit::WebVector; | |
| 59 using webkit_glue::CppArgumentList; | 37 using webkit_glue::CppArgumentList; |
| 60 using webkit_glue::CppVariant; | 38 using webkit_glue::CppVariant; |
| 61 | 39 |
| 62 const char* const kPluginPlaceholderDataURL = | |
| 63 "chrome://pluginplaceholderdata/"; | |
| 64 | |
| 65 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
| 66 // Strings we used to parse the youtube plugin url. | |
| 67 const char* const kSlashVSlash = "/v/"; | |
| 68 const char* const kSlashESlash = "/e/"; | |
| 69 #endif | |
| 70 | |
| 71 namespace { | 40 namespace { |
| 72 const PluginPlaceholder* g_last_active_menu = NULL; | 41 const PluginPlaceholder* g_last_active_menu = NULL; |
| 73 | |
| 74 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
| 75 // Helper function to get the youtube id from plugin params for old style | |
| 76 // embedded youtube video. | |
| 77 std::string GetYoutubeVideoId(const WebPluginParams& params) { | |
| 78 GURL url(params.url); | |
| 79 std::string video_id = url.path().substr(strlen(kSlashVSlash)); | |
| 80 | |
| 81 // Extract just the video id | |
| 82 size_t video_id_end = video_id.find('&'); | |
| 83 if (video_id_end != std::string::npos) | |
| 84 video_id = video_id.substr(0, video_id_end); | |
| 85 return video_id; | |
| 86 } | |
| 87 #endif | |
| 88 } // namespace | 42 } // namespace |
| 89 | 43 |
| 44 ChromePluginPlaceholder::ChromePluginPlaceholder( |
| 45 content::RenderView* render_view, |
| 46 WebKit::WebFrame* frame, |
| 47 const WebKit::WebPluginParams& params, |
| 48 const std::string& html_data, |
| 49 const string16& title) |
| 50 : PluginPlaceholder(render_view, frame, params, html_data), |
| 51 status_(new ChromeViewHostMsg_GetPluginInfo_Status), |
| 52 title_(title), |
| 53 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 54 placeholder_routing_id_(MSG_ROUTING_NONE), |
| 55 #endif |
| 56 has_host_(false), |
| 57 context_menu_request_id_(0) { |
| 58 RenderThread::Get()->AddObserver(this); |
| 59 } |
| 60 |
| 61 ChromePluginPlaceholder::~ChromePluginPlaceholder() { |
| 62 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 63 if (placeholder_routing_id_ == MSG_ROUTING_NONE) |
| 64 return; |
| 65 RenderThread::Get()->RemoveRoute(placeholder_routing_id_); |
| 66 if (has_host_) { |
| 67 RenderThread::Get()->Send(new ChromeViewHostMsg_RemovePluginPlaceholderHost( |
| 68 routing_id(), placeholder_routing_id_)); |
| 69 } |
| 70 #endif |
| 71 RenderThread::Get()->RemoveObserver(this); |
| 72 if (context_menu_request_id_) |
| 73 render_view()->CancelContextMenu(context_menu_request_id_); |
| 74 } |
| 75 |
| 90 // static | 76 // static |
| 91 PluginPlaceholder* PluginPlaceholder::CreateMissingPlugin( | 77 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateMissingPlugin( |
| 92 RenderView* render_view, | 78 RenderView* render_view, |
| 93 WebFrame* frame, | 79 WebFrame* frame, |
| 94 const WebPluginParams& params) { | 80 const WebPluginParams& params) { |
| 95 const base::StringPiece template_html( | 81 const base::StringPiece template_html( |
| 96 ResourceBundle::GetSharedInstance().GetRawDataResource( | 82 ResourceBundle::GetSharedInstance() |
| 97 IDR_BLOCKED_PLUGIN_HTML)); | 83 .GetRawDataResource(IDR_BLOCKED_PLUGIN_HTML)); |
| 98 | 84 |
| 99 base::DictionaryValue values; | 85 base::DictionaryValue values; |
| 100 #if defined(ENABLE_PLUGIN_INSTALLATION) | 86 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 101 values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); | 87 values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); |
| 102 #else | 88 #else |
| 103 values.SetString("message", | 89 values.SetString("message", |
| 104 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); | 90 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); |
| 105 #endif | 91 #endif |
| 106 | 92 |
| 107 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | 93 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); |
| 108 | 94 |
| 109 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. | 95 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. |
| 110 PluginPlaceholder* missing_plugin = new PluginPlaceholder( | 96 ChromePluginPlaceholder* missing_plugin = new ChromePluginPlaceholder( |
| 111 render_view, frame, params, html_data, params.mimeType); | 97 render_view, frame, params, html_data, params.mimeType); |
| 112 missing_plugin->set_allow_loading(true); | 98 missing_plugin->set_allow_loading(true); |
| 113 #if defined(ENABLE_PLUGIN_INSTALLATION) | 99 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 114 RenderThread::Get()->Send(new ChromeViewHostMsg_FindMissingPlugin( | 100 RenderThread::Get()->Send( |
| 115 missing_plugin->routing_id(), missing_plugin->CreateRoutingId(), | 101 new ChromeViewHostMsg_FindMissingPlugin(missing_plugin->routing_id(), |
| 116 params.mimeType.utf8())); | 102 missing_plugin->CreateRoutingId(), |
| 103 params.mimeType.utf8())); |
| 117 #endif | 104 #endif |
| 118 return missing_plugin; | 105 return missing_plugin; |
| 119 } | 106 } |
| 120 | 107 |
| 121 PluginPlaceholder* PluginPlaceholder::CreateErrorPlugin( | 108 // static |
| 109 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateErrorPlugin( |
| 122 RenderView* render_view, | 110 RenderView* render_view, |
| 123 const base::FilePath& file_path) { | 111 const base::FilePath& file_path) { |
| 124 base::DictionaryValue values; | 112 base::DictionaryValue values; |
| 125 values.SetString("message", | 113 values.SetString("message", |
| 126 l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR)); | 114 l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR)); |
| 127 | 115 |
| 128 const base::StringPiece template_html( | 116 const base::StringPiece template_html( |
| 129 ResourceBundle::GetSharedInstance().GetRawDataResource( | 117 ResourceBundle::GetSharedInstance() |
| 130 IDR_BLOCKED_PLUGIN_HTML)); | 118 .GetRawDataResource(IDR_BLOCKED_PLUGIN_HTML)); |
| 131 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | 119 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); |
| 132 | 120 |
| 133 WebPluginParams params; | 121 WebPluginParams params; |
| 134 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. | 122 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. |
| 135 PluginPlaceholder* plugin = new PluginPlaceholder( | 123 ChromePluginPlaceholder* plugin = new ChromePluginPlaceholder( |
| 136 render_view, NULL, params, html_data, params.mimeType); | 124 render_view, NULL, params, html_data, params.mimeType); |
| 137 | 125 |
| 138 RenderThread::Get()->Send( | 126 RenderThread::Get()->Send(new ChromeViewHostMsg_CouldNotLoadPlugin( |
| 139 new ChromeViewHostMsg_CouldNotLoadPlugin(plugin->routing_id(), | 127 plugin->routing_id(), file_path)); |
| 140 file_path)); | |
| 141 return plugin; | 128 return plugin; |
| 142 } | 129 } |
| 143 | 130 |
| 144 // static | 131 // static |
| 145 PluginPlaceholder* PluginPlaceholder::CreateBlockedPlugin( | 132 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateBlockedPlugin( |
| 146 RenderView* render_view, | 133 RenderView* render_view, |
| 147 WebFrame* frame, | 134 WebFrame* frame, |
| 148 const WebPluginParams& params, | 135 const WebPluginParams& params, |
| 149 const content::WebPluginInfo& plugin, | 136 const content::WebPluginInfo& plugin, |
| 150 const std::string& identifier, | 137 const std::string& identifier, |
| 151 const string16& name, | 138 const string16& name, |
| 152 int template_id, | 139 int template_id, |
| 153 const string16& message) { | 140 const string16& message) { |
| 154 base::DictionaryValue values; | 141 base::DictionaryValue values; |
| 155 values.SetString("message", message); | 142 values.SetString("message", message); |
| 156 values.SetString("name", name); | 143 values.SetString("name", name); |
| 157 values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); | 144 values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); |
| 158 | 145 |
| 159 const base::StringPiece template_html( | 146 const base::StringPiece template_html( |
| 160 ResourceBundle::GetSharedInstance().GetRawDataResource( | 147 ResourceBundle::GetSharedInstance().GetRawDataResource(template_id)); |
| 161 template_id)); | |
| 162 | 148 |
| 163 DCHECK(!template_html.empty()) << "unable to load template. ID: " | 149 DCHECK(!template_html.empty()) << "unable to load template. ID: " |
| 164 << template_id; | 150 << template_id; |
| 165 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | 151 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); |
| 166 | 152 |
| 167 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. | 153 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. |
| 168 PluginPlaceholder* blocked_plugin = new PluginPlaceholder( | 154 ChromePluginPlaceholder* blocked_plugin = |
| 169 render_view, frame, params, html_data, name); | 155 new ChromePluginPlaceholder(render_view, frame, params, html_data, name); |
| 170 blocked_plugin->plugin_info_ = plugin; | 156 blocked_plugin->SetPluginInfo(plugin); |
| 171 blocked_plugin->identifier_ = identifier; | 157 blocked_plugin->SetIdentifier(identifier); |
| 172 return blocked_plugin; | 158 return blocked_plugin; |
| 173 } | 159 } |
| 174 | 160 |
| 175 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | 161 void ChromePluginPlaceholder::SetStatus( |
| 176 // static | 162 const ChromeViewHostMsg_GetPluginInfo_Status& status) { |
| 177 PluginPlaceholder* PluginPlaceholder::CreateMobileYoutubePlugin( | 163 status_->value = status.value; |
| 178 content::RenderView* render_view, | |
| 179 WebFrame* frame, | |
| 180 const WebPluginParams& params) { | |
| 181 const base::StringPiece template_html( | |
| 182 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 183 IDR_MOBILE_YOUTUBE_PLUGIN_HTML)); | |
| 184 | |
| 185 base::DictionaryValue values; | |
| 186 values.SetString("video_id", GetYoutubeVideoId(params)); | |
| 187 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | |
| 188 | |
| 189 // |youtube_plugin| will destroy itself when its WebViewPlugin is going away. | |
| 190 PluginPlaceholder* youtube_plugin = new PluginPlaceholder( | |
| 191 render_view, frame, params, html_data, params.mimeType); | |
| 192 return youtube_plugin; | |
| 193 } | |
| 194 #endif | |
| 195 | |
| 196 PluginPlaceholder::PluginPlaceholder(content::RenderView* render_view, | |
| 197 WebFrame* frame, | |
| 198 const WebPluginParams& params, | |
| 199 const std::string& html_data, | |
| 200 const string16& title) | |
| 201 : content::RenderViewObserver(render_view), | |
| 202 frame_(frame), | |
| 203 plugin_params_(params), | |
| 204 plugin_(WebViewPlugin::Create( | |
| 205 this, render_view->GetWebkitPreferences(), html_data, | |
| 206 GURL(kPluginPlaceholderDataURL))), | |
| 207 title_(title), | |
| 208 status_(new ChromeViewHostMsg_GetPluginInfo_Status), | |
| 209 is_blocked_for_prerendering_(false), | |
| 210 allow_loading_(false), | |
| 211 #if defined(ENABLE_PLUGIN_INSTALLATION) | |
| 212 placeholder_routing_id_(MSG_ROUTING_NONE), | |
| 213 #endif | |
| 214 hidden_(false), | |
| 215 has_host_(false), | |
| 216 finished_loading_(false), | |
| 217 context_menu_request_id_(0) { | |
| 218 RenderThread::Get()->AddObserver(this); | |
| 219 } | |
| 220 | |
| 221 PluginPlaceholder::~PluginPlaceholder() { | |
| 222 RenderThread::Get()->RemoveObserver(this); | |
| 223 if (context_menu_request_id_) | |
| 224 render_view()->CancelContextMenu(context_menu_request_id_); | |
| 225 | |
| 226 #if defined(ENABLE_PLUGIN_INSTALLATION) | |
| 227 if (placeholder_routing_id_ == MSG_ROUTING_NONE) | |
| 228 return; | |
| 229 RenderThread::Get()->RemoveRoute(placeholder_routing_id_); | |
| 230 if (has_host_) { | |
| 231 RenderThread::Get()->Send( | |
| 232 new ChromeViewHostMsg_RemovePluginPlaceholderHost( | |
| 233 routing_id(), placeholder_routing_id_)); | |
| 234 } | |
| 235 #endif | |
| 236 } | 164 } |
| 237 | 165 |
| 238 #if defined(ENABLE_PLUGIN_INSTALLATION) | 166 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 239 int32 PluginPlaceholder::CreateRoutingId() { | 167 int32 ChromePluginPlaceholder::CreateRoutingId() { |
| 240 placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); | 168 placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); |
| 241 RenderThread::Get()->AddRoute(placeholder_routing_id_, this); | 169 RenderThread::Get()->AddRoute(placeholder_routing_id_, this); |
| 242 return placeholder_routing_id_; | 170 return placeholder_routing_id_; |
| 243 } | 171 } |
| 244 #endif | 172 #endif |
| 245 | 173 |
| 246 void PluginPlaceholder::SetStatus( | 174 bool ChromePluginPlaceholder::OnMessageReceived(const IPC::Message& message) { |
| 247 const ChromeViewHostMsg_GetPluginInfo_Status& status) { | |
| 248 status_->value = status.value; | |
| 249 } | |
| 250 | |
| 251 void PluginPlaceholder::BindWebFrame(WebFrame* frame) { | |
| 252 BindToJavascript(frame, "plugin"); | |
| 253 BindCallback("load", base::Bind(&PluginPlaceholder::LoadCallback, | |
| 254 base::Unretained(this))); | |
| 255 BindCallback("hide", base::Bind(&PluginPlaceholder::HideCallback, | |
| 256 base::Unretained(this))); | |
| 257 BindCallback("openAboutPlugins", | |
| 258 base::Bind(&PluginPlaceholder::OpenAboutPluginsCallback, | |
| 259 base::Unretained(this))); | |
| 260 BindCallback("didFinishLoading", | |
| 261 base::Bind(&PluginPlaceholder::DidFinishLoadingCallback, | |
| 262 base::Unretained(this))); | |
| 263 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
| 264 BindCallback("openYoutubeURL", | |
| 265 base::Bind(&PluginPlaceholder::OpenYoutubeUrlCallback, | |
| 266 base::Unretained(this))); | |
| 267 #endif | |
| 268 } | |
| 269 | |
| 270 bool PluginPlaceholder::OnMessageReceived(const IPC::Message& message) { | |
| 271 #if defined(ENABLE_PLUGIN_INSTALLATION) | 175 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 272 bool handled = true; | 176 bool handled = true; |
| 273 IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) | 177 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder, message) |
| 274 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, | 178 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, OnFoundMissingPlugin) |
| 275 OnFoundMissingPlugin) | 179 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, |
| 276 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, | 180 OnDidNotFindMissingPlugin) |
| 277 OnDidNotFindMissingPlugin) | 181 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, |
| 278 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, | 182 OnStartedDownloadingPlugin) |
| 279 OnStartedDownloadingPlugin) | 183 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, |
| 280 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, | 184 OnFinishedDownloadingPlugin) |
| 281 OnFinishedDownloadingPlugin) | 185 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, |
| 282 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, | 186 OnErrorDownloadingPlugin) |
| 283 OnErrorDownloadingPlugin) | 187 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, |
| 284 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, | 188 OnCancelledDownloadingPlugin) |
| 285 OnCancelledDownloadingPlugin) | 189 IPC_MESSAGE_UNHANDLED(handled = false) |
| 286 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 287 IPC_END_MESSAGE_MAP() | 190 IPC_END_MESSAGE_MAP() |
| 288 | 191 |
| 289 if (handled) | 192 if (handled) |
| 290 return true; | 193 return true; |
| 291 #endif | 194 #endif |
| 292 | 195 |
| 293 // We don't swallow these messages because multiple blocked plugins have an | 196 // We don't swallow these messages because multiple blocked plugins have an |
| 294 // interest in them. | 197 // interest in them. |
| 295 IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) | 198 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder, message) |
| 296 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) | 199 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) |
| 297 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) | 200 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) |
| 298 IPC_END_MESSAGE_MAP() | 201 IPC_END_MESSAGE_MAP() |
| 299 | 202 |
| 300 return false; | 203 return false; |
| 301 } | 204 } |
| 302 | 205 |
| 303 void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) { | 206 void ChromePluginPlaceholder::OnLoadBlockedPlugins( |
| 304 CHECK(plugin_); | 207 const std::string& identifier) { |
| 305 if (!new_plugin) { | 208 PluginPlaceholder::OnLoadBlockedPlugins(identifier); |
| 306 PluginUMAReporter::GetInstance()->ReportPluginMissing( | |
| 307 plugin_params_.mimeType.utf8(), | |
| 308 plugin_params_.url); | |
| 309 return; | |
| 310 } | |
| 311 | |
| 312 WebPluginContainer* container = plugin_->container(); | |
| 313 // Set the new plug-in on the container before initializing it. | |
| 314 container->setPlugin(new_plugin); | |
| 315 // Save the element in case the plug-in is removed from the page during | |
| 316 // initialization. | |
| 317 WebElement element = container->element(); | |
| 318 if (!new_plugin->initialize(container)) { | |
| 319 // We couldn't initialize the new plug-in. Restore the old one and abort. | |
| 320 container->setPlugin(plugin_); | |
| 321 return; | |
| 322 } | |
| 323 | |
| 324 // The plug-in has been removed from the page. Destroy the old plug-in | |
| 325 // (which will destroy us). | |
| 326 if (!element.pluginContainer()) { | |
| 327 plugin_->destroy(); | |
| 328 return; | |
| 329 } | |
| 330 | |
| 331 // During initialization, the new plug-in might have replaced itself in turn | |
| 332 // with another plug-in. Make sure not to use the passed in |new_plugin| after | |
| 333 // this point. | |
| 334 new_plugin = container->plugin(); | |
| 335 | |
| 336 plugin_->RestoreTitleText(); | |
| 337 container->invalidate(); | |
| 338 container->reportGeometry(); | |
| 339 plugin_->ReplayReceivedData(new_plugin); | |
| 340 plugin_->destroy(); | |
| 341 } | 209 } |
| 342 | 210 |
| 343 void PluginPlaceholder::HidePlugin() { | 211 void ChromePluginPlaceholder::OpenAboutPluginsCallback( |
| 344 hidden_ = true; | 212 const CppArgumentList& args, |
| 345 WebPluginContainer* container = plugin_->container(); | 213 CppVariant* result) { |
| 346 WebElement element = container->element(); | 214 RenderThread::Get() |
| 347 element.setAttribute("style", "display: none;"); | 215 ->Send(new ChromeViewHostMsg_OpenAboutPlugins(routing_id())); |
| 348 // If we have a width and height, search for a parent (often <div>) with the | |
| 349 // same dimensions. If we find such a parent, hide that as well. | |
| 350 // This makes much more uncovered page content usable (including clickable) | |
| 351 // as opposed to merely visible. | |
| 352 // TODO(cevans) -- it's a foul heurisitc but we're going to tolerate it for | |
| 353 // now for these reasons: | |
| 354 // 1) Makes the user experience better. | |
| 355 // 2) Foulness is encapsulated within this single function. | |
| 356 // 3) Confidence in no fasle positives. | |
| 357 // 4) Seems to have a good / low false negative rate at this time. | |
| 358 if (element.hasAttribute("width") && element.hasAttribute("height")) { | |
| 359 std::string width_str("width:[\\s]*"); | |
| 360 width_str += element.getAttribute("width").utf8().data(); | |
| 361 if (EndsWith(width_str, "px", false)) { | |
| 362 width_str = width_str.substr(0, width_str.length() - 2); | |
| 363 } | |
| 364 TrimWhitespace(width_str, TRIM_TRAILING, &width_str); | |
| 365 width_str += "[\\s]*px"; | |
| 366 std::string height_str("height:[\\s]*"); | |
| 367 height_str += element.getAttribute("height").utf8().data(); | |
| 368 if (EndsWith(height_str, "px", false)) { | |
| 369 height_str = height_str.substr(0, height_str.length() - 2); | |
| 370 } | |
| 371 TrimWhitespace(height_str, TRIM_TRAILING, &height_str); | |
| 372 height_str += "[\\s]*px"; | |
| 373 WebNode parent = element; | |
| 374 while (!parent.parentNode().isNull()) { | |
| 375 parent = parent.parentNode(); | |
| 376 if (!parent.isElementNode()) | |
| 377 continue; | |
| 378 element = parent.toConst<WebElement>(); | |
| 379 if (element.hasAttribute("style")) { | |
| 380 std::string style_str = element.getAttribute("style").utf8(); | |
| 381 if (RE2::PartialMatch(style_str, width_str) && | |
| 382 RE2::PartialMatch(style_str, height_str)) | |
| 383 element.setAttribute("style", "display: none;"); | |
| 384 } | |
| 385 } | |
| 386 } | |
| 387 } | 216 } |
| 388 | 217 |
| 389 void PluginPlaceholder::WillDestroyPlugin() { | 218 void ChromePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { |
| 390 delete this; | 219 PluginPlaceholder::OnSetIsPrerendering(is_prerendering); |
| 391 } | 220 } |
| 392 | 221 |
| 393 #if defined(ENABLE_PLUGIN_INSTALLATION) | 222 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 394 void PluginPlaceholder::OnDidNotFindMissingPlugin() { | 223 void ChromePluginPlaceholder::OnDidNotFindMissingPlugin() { |
| 395 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND)); | 224 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND)); |
| 396 } | 225 } |
| 397 | 226 |
| 398 void PluginPlaceholder::OnFoundMissingPlugin(const string16& plugin_name) { | 227 void ChromePluginPlaceholder::OnFoundMissingPlugin( |
| 228 const string16& plugin_name) { |
| 399 if (status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound) | 229 if (status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound) |
| 400 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND, plugin_name)); | 230 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND, plugin_name)); |
| 401 has_host_ = true; | 231 has_host_ = true; |
| 402 plugin_name_ = plugin_name; | 232 plugin_name_ = plugin_name; |
| 403 } | 233 } |
| 404 | 234 |
| 405 void PluginPlaceholder::OnStartedDownloadingPlugin() { | 235 void ChromePluginPlaceholder::OnStartedDownloadingPlugin() { |
| 406 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_)); | 236 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_)); |
| 407 } | 237 } |
| 408 | 238 |
| 409 void PluginPlaceholder::OnFinishedDownloadingPlugin() { | 239 void ChromePluginPlaceholder::OnFinishedDownloadingPlugin() { |
| 410 bool is_installing = | 240 bool is_installing = |
| 411 status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; | 241 status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; |
| 412 SetMessage(l10n_util::GetStringFUTF16( | 242 SetMessage(l10n_util::GetStringFUTF16( |
| 413 is_installing ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING, | 243 is_installing ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING, |
| 414 plugin_name_)); | 244 plugin_name_)); |
| 415 } | 245 } |
| 416 | 246 |
| 417 void PluginPlaceholder::OnErrorDownloadingPlugin(const std::string& error) { | 247 void ChromePluginPlaceholder::OnErrorDownloadingPlugin( |
| 248 const std::string& error) { |
| 418 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR, | 249 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR, |
| 419 UTF8ToUTF16(error))); | 250 UTF8ToUTF16(error))); |
| 420 } | 251 } |
| 421 | 252 |
| 422 void PluginPlaceholder::OnCancelledDownloadingPlugin() { | 253 void ChromePluginPlaceholder::OnCancelledDownloadingPlugin() { |
| 423 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, | 254 SetMessage( |
| 424 plugin_name_)); | 255 l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, plugin_name_)); |
| 425 } | 256 } |
| 426 #endif // defined(ENABLE_PLUGIN_INSTALLATION) | 257 #endif // defined(ENABLE_PLUGIN_INSTALLATION) |
| 427 | 258 |
| 428 void PluginPlaceholder::PluginListChanged() { | 259 void ChromePluginPlaceholder::PluginListChanged() { |
| 429 if (!frame_) | 260 if (!GetFrame()) |
| 430 return; | 261 return; |
| 431 WebDocument document = frame_->top()->document(); | 262 WebDocument document = GetFrame()->top()->document(); |
| 432 if (document.isNull()) | 263 if (document.isNull()) |
| 433 return; | 264 return; |
| 434 | 265 |
| 435 ChromeViewHostMsg_GetPluginInfo_Output output; | 266 ChromeViewHostMsg_GetPluginInfo_Output output; |
| 436 std::string mime_type(plugin_params_.mimeType.utf8()); | 267 std::string mime_type(GetPluginParams().mimeType.utf8()); |
| 437 render_view()->Send(new ChromeViewHostMsg_GetPluginInfo( | 268 render_view() |
| 438 routing_id(), GURL(plugin_params_.url), document.url(), | 269 ->Send(new ChromeViewHostMsg_GetPluginInfo(routing_id(), |
| 439 mime_type, &output)); | 270 GURL(GetPluginParams().url), |
| 271 document.url(), |
| 272 mime_type, |
| 273 &output)); |
| 440 if (output.status.value == status_->value) | 274 if (output.status.value == status_->value) |
| 441 return; | 275 return; |
| 442 WebPlugin* new_plugin = chrome::ChromeContentRendererClient::CreatePlugin( | 276 WebPlugin* new_plugin = chrome::ChromeContentRendererClient::CreatePlugin( |
| 443 render_view(), frame_, plugin_params_, output); | 277 render_view(), GetFrame(), GetPluginParams(), output); |
| 444 ReplacePlugin(new_plugin); | 278 ReplacePlugin(new_plugin); |
| 445 } | 279 } |
| 446 | 280 |
| 447 void PluginPlaceholder::OnMenuAction(int request_id, unsigned action) { | 281 void ChromePluginPlaceholder::OnMenuAction(int request_id, unsigned action) { |
| 448 DCHECK_EQ(context_menu_request_id_, request_id); | 282 DCHECK_EQ(context_menu_request_id_, request_id); |
| 449 if (g_last_active_menu != this) | 283 if (g_last_active_menu != this) |
| 450 return; | 284 return; |
| 451 switch (action) { | 285 switch (action) { |
| 452 case chrome::MENU_COMMAND_PLUGIN_RUN: { | 286 case chrome::MENU_COMMAND_PLUGIN_RUN: { |
| 453 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Menu"); | 287 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Menu"); |
| 454 LoadPlugin(); | 288 LoadPlugin(); |
| 455 break; | 289 break; |
| 456 } | 290 } |
| 457 case chrome::MENU_COMMAND_PLUGIN_HIDE: { | 291 case chrome::MENU_COMMAND_PLUGIN_HIDE: { |
| 458 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Menu"); | 292 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Menu"); |
| 459 HidePlugin(); | 293 HidePlugin(); |
| 460 break; | 294 break; |
| 461 } | 295 } |
| 462 default: | 296 default: |
| 463 NOTREACHED(); | 297 NOTREACHED(); |
| 464 } | 298 } |
| 465 } | 299 } |
| 466 | 300 |
| 467 void PluginPlaceholder::OnMenuClosed(int request_id) { | 301 void ChromePluginPlaceholder::OnMenuClosed(int request_id) { |
| 468 DCHECK_EQ(context_menu_request_id_, request_id); | 302 DCHECK_EQ(context_menu_request_id_, request_id); |
| 469 context_menu_request_id_ = 0; | 303 context_menu_request_id_ = 0; |
| 470 } | 304 } |
| 471 | 305 |
| 472 void PluginPlaceholder::SetMessage(const string16& message) { | 306 void ChromePluginPlaceholder::ShowContextMenu(const WebMouseEvent& event) { |
| 473 message_ = message; | |
| 474 if (finished_loading_) | |
| 475 UpdateMessage(); | |
| 476 } | |
| 477 | |
| 478 void PluginPlaceholder::UpdateMessage() { | |
| 479 std::string script = "window.setMessage(" + | |
| 480 base::GetDoubleQuotedJson(message_) + ")"; | |
| 481 plugin_->web_view()->mainFrame()->executeScript( | |
| 482 WebScriptSource(ASCIIToUTF16(script))); | |
| 483 } | |
| 484 | |
| 485 void PluginPlaceholder::ShowContextMenu(const WebMouseEvent& event) { | |
| 486 if (context_menu_request_id_) | 307 if (context_menu_request_id_) |
| 487 return; // Don't allow nested context menu requests. | 308 return; // Don't allow nested context menu requests. |
| 488 | 309 |
| 489 content::ContextMenuParams params; | 310 content::ContextMenuParams params; |
| 490 | 311 |
| 491 content::MenuItem name_item; | 312 content::MenuItem name_item; |
| 492 name_item.label = title_; | 313 name_item.label = title_; |
| 493 params.custom_items.push_back(name_item); | 314 params.custom_items.push_back(name_item); |
| 494 | 315 |
| 495 content::MenuItem separator_item; | 316 content::MenuItem separator_item; |
| 496 separator_item.type = content::MenuItem::SEPARATOR; | 317 separator_item.type = content::MenuItem::SEPARATOR; |
| 497 params.custom_items.push_back(separator_item); | 318 params.custom_items.push_back(separator_item); |
| 498 | 319 |
| 499 if (!plugin_info_.path.value().empty()) { | 320 if (!GetPluginInfo().path.value().empty()) { |
| 500 content::MenuItem run_item; | 321 content::MenuItem run_item; |
| 501 run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN; | 322 run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN; |
| 502 // Disable this menu item if the plugin is blocked by policy. | 323 // Disable this menu item if the plugin is blocked by policy. |
| 503 run_item.enabled = allow_loading_; | 324 run_item.enabled = LoadingAllowed(); |
| 504 run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN); | 325 run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN); |
| 505 params.custom_items.push_back(run_item); | 326 params.custom_items.push_back(run_item); |
| 506 } | 327 } |
| 507 | 328 |
| 508 content::MenuItem hide_item; | 329 content::MenuItem hide_item; |
| 509 hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE; | 330 hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE; |
| 510 hide_item.enabled = true; | 331 hide_item.enabled = true; |
| 511 hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE); | 332 hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE); |
| 512 params.custom_items.push_back(hide_item); | 333 params.custom_items.push_back(hide_item); |
| 513 | 334 |
| 514 params.x = event.windowX; | 335 params.x = event.windowX; |
| 515 params.y = event.windowY; | 336 params.y = event.windowY; |
| 516 | 337 |
| 517 context_menu_request_id_ = render_view()->ShowContextMenu(this, params); | 338 context_menu_request_id_ = render_view()->ShowContextMenu(this, params); |
| 518 g_last_active_menu = this; | 339 g_last_active_menu = this; |
| 519 } | 340 } |
| 520 | 341 |
| 521 void PluginPlaceholder::OnLoadBlockedPlugins(const std::string& identifier) { | 342 void ChromePluginPlaceholder::BindWebFrame(WebKit::WebFrame* frame) { |
| 522 if (!identifier.empty() && identifier != identifier_) | 343 PluginPlaceholder::BindWebFrame(frame); |
| 523 return; | 344 BindCallback("openAboutPlugins", |
| 524 | 345 base::Bind(&ChromePluginPlaceholder::OpenAboutPluginsCallback, |
| 525 RenderThread::Get()->RecordUserMetrics("Plugin_Load_UI"); | 346 base::Unretained(this))); |
| 526 LoadPlugin(); | |
| 527 } | 347 } |
| 528 | |
| 529 void PluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { | |
| 530 // Prerendering can only be enabled prior to a RenderView's first navigation, | |
| 531 // so no BlockedPlugin should see the notification that enables prerendering. | |
| 532 DCHECK(!is_prerendering); | |
| 533 if (is_blocked_for_prerendering_ && !is_prerendering) | |
| 534 LoadPlugin(); | |
| 535 } | |
| 536 | |
| 537 void PluginPlaceholder::LoadPlugin() { | |
| 538 // This is not strictly necessary but is an important defense in case the | |
| 539 // event propagation changes between "close" vs. "click-to-play". | |
| 540 if (hidden_) | |
| 541 return; | |
| 542 if (!allow_loading_) { | |
| 543 NOTREACHED(); | |
| 544 return; | |
| 545 } | |
| 546 | |
| 547 // TODO(mmenke): In the case of prerendering, feed into | |
| 548 // ChromeContentRendererClient::CreatePlugin instead, to | |
| 549 // reduce the chance of future regressions. | |
| 550 WebPlugin* plugin = | |
| 551 render_view()->CreatePlugin(frame_, plugin_info_, plugin_params_); | |
| 552 ReplacePlugin(plugin); | |
| 553 } | |
| 554 | |
| 555 void PluginPlaceholder::LoadCallback(const CppArgumentList& args, | |
| 556 CppVariant* result) { | |
| 557 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Click"); | |
| 558 LoadPlugin(); | |
| 559 } | |
| 560 | |
| 561 void PluginPlaceholder::HideCallback(const CppArgumentList& args, | |
| 562 CppVariant* result) { | |
| 563 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Click"); | |
| 564 HidePlugin(); | |
| 565 } | |
| 566 | |
| 567 void PluginPlaceholder::OpenAboutPluginsCallback(const CppArgumentList& args, | |
| 568 CppVariant* result) { | |
| 569 RenderThread::Get()->Send( | |
| 570 new ChromeViewHostMsg_OpenAboutPlugins(routing_id())); | |
| 571 } | |
| 572 | |
| 573 void PluginPlaceholder::DidFinishLoadingCallback(const CppArgumentList& args, | |
| 574 CppVariant* result) { | |
| 575 finished_loading_ = true; | |
| 576 if (message_.length() > 0) | |
| 577 UpdateMessage(); | |
| 578 } | |
| 579 | |
| 580 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
| 581 void PluginPlaceholder::OpenYoutubeUrlCallback(const CppArgumentList& args, | |
| 582 CppVariant* result) { | |
| 583 std::string youtube("vnd.youtube:"); | |
| 584 GURL url(youtube.append(GetYoutubeVideoId(plugin_params_))); | |
| 585 WebURLRequest request; | |
| 586 request.initialize(); | |
| 587 request.setURL(url); | |
| 588 render_view()->LoadURLExternally( | |
| 589 frame_, request, WebKit::WebNavigationPolicyNewForegroundTab); | |
| 590 } | |
| 591 | |
| 592 bool PluginPlaceholder::IsValidYouTubeVideo(const std::string& path) { | |
| 593 unsigned len = strlen(kSlashVSlash); | |
| 594 | |
| 595 // check for more than just /v/ or /e/. | |
| 596 if (path.length() <= len) | |
| 597 return false; | |
| 598 | |
| 599 std::string str = StringToLowerASCII(path); | |
| 600 // Youtube flash url can start with /v/ or /e/. | |
| 601 if (strncmp(str.data(), kSlashVSlash, len) != 0 && | |
| 602 strncmp(str.data(), kSlashESlash, len) != 0) | |
| 603 return false; | |
| 604 | |
| 605 // Start after /v/ | |
| 606 for (unsigned i = len; i < path.length(); i++) { | |
| 607 char c = str[i]; | |
| 608 if (isalpha(c) || isdigit(c) || c == '_' || c == '-') | |
| 609 continue; | |
| 610 // The url can have more parameters such as &hl=en after the video id. | |
| 611 // Once we start seeing extra parameters we can return true. | |
| 612 return c == '&' && i > len; | |
| 613 } | |
| 614 return true; | |
| 615 } | |
| 616 | |
| 617 bool PluginPlaceholder::IsYouTubeURL(const GURL& url, | |
| 618 const std::string& mime_type) { | |
| 619 std::string host = url.host(); | |
| 620 bool is_youtube = EndsWith(host, "youtube.com", true) || | |
| 621 EndsWith(host, "youtube-nocookie.com", true); | |
| 622 | |
| 623 return is_youtube && IsValidYouTubeVideo(url.path()) && | |
| 624 LowerCaseEqualsASCII(mime_type, content::kFlashPluginSwfMimeType); | |
| 625 } | |
| 626 #endif | |
| OLD | NEW |