| 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" | 13 #include "chrome/renderer/plugins/plugin_uma.h" |
| 19 #include "content/public/common/content_constants.h" | |
| 20 #include "content/public/common/context_menu_params.h" | 14 #include "content/public/common/context_menu_params.h" |
| 21 #include "content/public/renderer/render_thread.h" | 15 #include "content/public/renderer/render_thread.h" |
| 22 #include "content/public/renderer/render_view.h" | 16 #include "content/public/renderer/render_view.h" |
| 23 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
| 24 #include "grit/renderer_resources.h" | 18 #include "grit/renderer_resources.h" |
| 25 #include "grit/webkit_strings.h" | 19 #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" | 20 #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" | 21 #include "third_party/WebKit/public/web/WebFrame.h" |
| 34 #include "third_party/WebKit/public/web/WebInputEvent.h" | 22 #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" | 23 #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" | 24 #include "ui/base/l10n/l10n_util.h" |
| 40 #include "ui/base/resource/resource_bundle.h" | 25 #include "ui/base/resource/resource_bundle.h" |
| 41 #include "ui/webui/jstemplate_builder.h" | 26 #include "ui/webui/jstemplate_builder.h" |
| 42 | 27 |
| 43 using content::RenderThread; | 28 using content::RenderThread; |
| 44 using content::RenderView; | 29 using content::RenderView; |
| 45 using WebKit::WebContextMenuData; | |
| 46 using WebKit::WebDocument; | 30 using WebKit::WebDocument; |
| 47 using WebKit::WebElement; | 31 using WebKit::WebElement; |
| 48 using WebKit::WebFrame; | 32 using WebKit::WebFrame; |
| 49 using WebKit::WebMouseEvent; | 33 using WebKit::WebMouseEvent; |
| 50 using WebKit::WebNode; | 34 using WebKit::WebNode; |
| 51 using WebKit::WebPlugin; | 35 using WebKit::WebPlugin; |
| 52 using WebKit::WebPluginContainer; | 36 using WebKit::WebPluginContainer; |
| 53 using WebKit::WebPluginParams; | 37 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; | 38 using webkit_glue::CppArgumentList; |
| 60 using webkit_glue::CppVariant; | 39 using webkit_glue::CppVariant; |
| 61 | 40 |
| 62 const char* const kPluginPlaceholderDataURL = | 41 namespace { |
| 42 const plugins::PluginPlaceholder* g_last_active_menu = NULL; |
| 43 } // namespace |
| 44 |
| 45 const char ChromePluginPlaceholder::kPluginPlaceholderDataURL[] = |
| 63 "chrome://pluginplaceholderdata/"; | 46 "chrome://pluginplaceholderdata/"; |
| 64 | 47 |
| 65 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | 48 ChromePluginPlaceholder::ChromePluginPlaceholder( |
| 66 // Strings we used to parse the youtube plugin url. | 49 content::RenderView* render_view, |
| 67 const char* const kSlashVSlash = "/v/"; | 50 WebKit::WebFrame* frame, |
| 68 const char* const kSlashESlash = "/e/"; | 51 const WebKit::WebPluginParams& params, |
| 52 const std::string& html_data, |
| 53 const string16& title) |
| 54 : plugins::PluginPlaceholder(render_view, |
| 55 frame, |
| 56 params, |
| 57 html_data, |
| 58 GURL(kPluginPlaceholderDataURL)), |
| 59 status_(new ChromeViewHostMsg_GetPluginInfo_Status), |
| 60 title_(title), |
| 61 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 62 placeholder_routing_id_(MSG_ROUTING_NONE), |
| 69 #endif | 63 #endif |
| 64 has_host_(false), |
| 65 context_menu_request_id_(0) { |
| 66 RenderThread::Get()->AddObserver(this); |
| 67 } |
| 70 | 68 |
| 71 namespace { | 69 ChromePluginPlaceholder::~ChromePluginPlaceholder() { |
| 72 const PluginPlaceholder* g_last_active_menu = NULL; | 70 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 73 | 71 if (placeholder_routing_id_ == MSG_ROUTING_NONE) |
| 74 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | 72 return; |
| 75 // Helper function to get the youtube id from plugin params for old style | 73 RenderThread::Get()->RemoveRoute(placeholder_routing_id_); |
| 76 // embedded youtube video. | 74 if (has_host_) { |
| 77 std::string GetYoutubeVideoId(const WebPluginParams& params) { | 75 RenderThread::Get()->Send(new ChromeViewHostMsg_RemovePluginPlaceholderHost( |
| 78 GURL url(params.url); | 76 routing_id(), placeholder_routing_id_)); |
| 79 std::string video_id = url.path().substr(strlen(kSlashVSlash)); | 77 } |
| 80 | 78 #endif |
| 81 // Extract just the video id | 79 RenderThread::Get()->RemoveObserver(this); |
| 82 size_t video_id_end = video_id.find('&'); | 80 if (context_menu_request_id_) |
| 83 if (video_id_end != std::string::npos) | 81 render_view()->CancelContextMenu(context_menu_request_id_); |
| 84 video_id = video_id.substr(0, video_id_end); | |
| 85 return video_id; | |
| 86 } | 82 } |
| 87 #endif | |
| 88 } // namespace | |
| 89 | 83 |
| 90 // static | 84 // static |
| 91 PluginPlaceholder* PluginPlaceholder::CreateMissingPlugin( | 85 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateMissingPlugin( |
| 92 RenderView* render_view, | 86 RenderView* render_view, |
| 93 WebFrame* frame, | 87 WebFrame* frame, |
| 94 const WebPluginParams& params) { | 88 const WebPluginParams& params) { |
| 95 const base::StringPiece template_html( | 89 const base::StringPiece template_html( |
| 96 ResourceBundle::GetSharedInstance().GetRawDataResource( | 90 ResourceBundle::GetSharedInstance().GetRawDataResource( |
| 97 IDR_BLOCKED_PLUGIN_HTML)); | 91 IDR_BLOCKED_PLUGIN_HTML)); |
| 98 | 92 |
| 99 base::DictionaryValue values; | 93 base::DictionaryValue values; |
| 100 #if defined(ENABLE_PLUGIN_INSTALLATION) | 94 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 101 values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); | 95 values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); |
| 102 #else | 96 #else |
| 103 values.SetString("message", | 97 values.SetString("message", |
| 104 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); | 98 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); |
| 105 #endif | 99 #endif |
| 106 | 100 |
| 107 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | 101 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); |
| 108 | 102 |
| 109 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. | 103 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. |
| 110 PluginPlaceholder* missing_plugin = new PluginPlaceholder( | 104 ChromePluginPlaceholder* missing_plugin = new ChromePluginPlaceholder( |
| 111 render_view, frame, params, html_data, params.mimeType); | 105 render_view, frame, params, html_data, params.mimeType); |
| 112 missing_plugin->set_allow_loading(true); | 106 missing_plugin->set_allow_loading(true); |
| 113 #if defined(ENABLE_PLUGIN_INSTALLATION) | 107 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 114 RenderThread::Get()->Send(new ChromeViewHostMsg_FindMissingPlugin( | 108 RenderThread::Get()->Send( |
| 115 missing_plugin->routing_id(), missing_plugin->CreateRoutingId(), | 109 new ChromeViewHostMsg_FindMissingPlugin(missing_plugin->routing_id(), |
| 116 params.mimeType.utf8())); | 110 missing_plugin->CreateRoutingId(), |
| 111 params.mimeType.utf8())); |
| 117 #endif | 112 #endif |
| 118 return missing_plugin; | 113 return missing_plugin; |
| 119 } | 114 } |
| 120 | 115 |
| 121 PluginPlaceholder* PluginPlaceholder::CreateErrorPlugin( | 116 // static |
| 117 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateErrorPlugin( |
| 122 RenderView* render_view, | 118 RenderView* render_view, |
| 123 const base::FilePath& file_path) { | 119 const base::FilePath& file_path) { |
| 124 base::DictionaryValue values; | 120 base::DictionaryValue values; |
| 125 values.SetString("message", | 121 values.SetString("message", |
| 126 l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR)); | 122 l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR)); |
| 127 | 123 |
| 128 const base::StringPiece template_html( | 124 const base::StringPiece template_html( |
| 129 ResourceBundle::GetSharedInstance().GetRawDataResource( | 125 ResourceBundle::GetSharedInstance().GetRawDataResource( |
| 130 IDR_BLOCKED_PLUGIN_HTML)); | 126 IDR_BLOCKED_PLUGIN_HTML)); |
| 131 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | 127 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); |
| 132 | 128 |
| 133 WebPluginParams params; | 129 WebPluginParams params; |
| 134 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. | 130 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. |
| 135 PluginPlaceholder* plugin = new PluginPlaceholder( | 131 ChromePluginPlaceholder* plugin = new ChromePluginPlaceholder( |
| 136 render_view, NULL, params, html_data, params.mimeType); | 132 render_view, NULL, params, html_data, params.mimeType); |
| 137 | 133 |
| 138 RenderThread::Get()->Send( | 134 RenderThread::Get()->Send(new ChromeViewHostMsg_CouldNotLoadPlugin( |
| 139 new ChromeViewHostMsg_CouldNotLoadPlugin(plugin->routing_id(), | 135 plugin->routing_id(), file_path)); |
| 140 file_path)); | |
| 141 return plugin; | 136 return plugin; |
| 142 } | 137 } |
| 143 | 138 |
| 144 // static | 139 // static |
| 145 PluginPlaceholder* PluginPlaceholder::CreateBlockedPlugin( | 140 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateBlockedPlugin( |
| 146 RenderView* render_view, | 141 RenderView* render_view, |
| 147 WebFrame* frame, | 142 WebFrame* frame, |
| 148 const WebPluginParams& params, | 143 const WebPluginParams& params, |
| 149 const content::WebPluginInfo& plugin, | 144 const content::WebPluginInfo& plugin, |
| 150 const std::string& identifier, | 145 const std::string& identifier, |
| 151 const string16& name, | 146 const string16& name, |
| 152 int template_id, | 147 int template_id, |
| 153 const string16& message) { | 148 const string16& message) { |
| 154 base::DictionaryValue values; | 149 base::DictionaryValue values; |
| 155 values.SetString("message", message); | 150 values.SetString("message", message); |
| 156 values.SetString("name", name); | 151 values.SetString("name", name); |
| 157 values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); | 152 values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); |
| 158 | 153 |
| 159 const base::StringPiece template_html( | 154 const base::StringPiece template_html( |
| 160 ResourceBundle::GetSharedInstance().GetRawDataResource( | 155 ResourceBundle::GetSharedInstance().GetRawDataResource(template_id)); |
| 161 template_id)); | |
| 162 | 156 |
| 163 DCHECK(!template_html.empty()) << "unable to load template. ID: " | 157 DCHECK(!template_html.empty()) << "unable to load template. ID: " |
| 164 << template_id; | 158 << template_id; |
| 165 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | 159 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); |
| 166 | 160 |
| 167 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. | 161 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. |
| 168 PluginPlaceholder* blocked_plugin = new PluginPlaceholder( | 162 ChromePluginPlaceholder* blocked_plugin = |
| 169 render_view, frame, params, html_data, name); | 163 new ChromePluginPlaceholder(render_view, frame, params, html_data, name); |
| 170 blocked_plugin->plugin_info_ = plugin; | 164 blocked_plugin->SetPluginInfo(plugin); |
| 171 blocked_plugin->identifier_ = identifier; | 165 blocked_plugin->SetIdentifier(identifier); |
| 172 return blocked_plugin; | 166 return blocked_plugin; |
| 173 } | 167 } |
| 174 | 168 |
| 175 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | 169 void ChromePluginPlaceholder::SetStatus( |
| 176 // static | 170 const ChromeViewHostMsg_GetPluginInfo_Status& status) { |
| 177 PluginPlaceholder* PluginPlaceholder::CreateMobileYoutubePlugin( | 171 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 } | 172 } |
| 237 | 173 |
| 238 #if defined(ENABLE_PLUGIN_INSTALLATION) | 174 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 239 int32 PluginPlaceholder::CreateRoutingId() { | 175 int32 ChromePluginPlaceholder::CreateRoutingId() { |
| 240 placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); | 176 placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); |
| 241 RenderThread::Get()->AddRoute(placeholder_routing_id_, this); | 177 RenderThread::Get()->AddRoute(placeholder_routing_id_, this); |
| 242 return placeholder_routing_id_; | 178 return placeholder_routing_id_; |
| 243 } | 179 } |
| 244 #endif | 180 #endif |
| 245 | 181 |
| 246 void PluginPlaceholder::SetStatus( | 182 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) | 183 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 272 bool handled = true; | 184 bool handled = true; |
| 273 IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) | 185 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder, message) |
| 274 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, | 186 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, OnFoundMissingPlugin) |
| 275 OnFoundMissingPlugin) | 187 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, |
| 276 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, | 188 OnDidNotFindMissingPlugin) |
| 277 OnDidNotFindMissingPlugin) | 189 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, |
| 278 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, | 190 OnStartedDownloadingPlugin) |
| 279 OnStartedDownloadingPlugin) | 191 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, |
| 280 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, | 192 OnFinishedDownloadingPlugin) |
| 281 OnFinishedDownloadingPlugin) | 193 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, |
| 282 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, | 194 OnErrorDownloadingPlugin) |
| 283 OnErrorDownloadingPlugin) | 195 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, |
| 284 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, | 196 OnCancelledDownloadingPlugin) |
| 285 OnCancelledDownloadingPlugin) | 197 IPC_MESSAGE_UNHANDLED(handled = false) |
| 286 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 287 IPC_END_MESSAGE_MAP() | 198 IPC_END_MESSAGE_MAP() |
| 288 | 199 |
| 289 if (handled) | 200 if (handled) |
| 290 return true; | 201 return true; |
| 291 #endif | 202 #endif |
| 292 | 203 |
| 293 // We don't swallow these messages because multiple blocked plugins have an | 204 // We don't swallow these messages because multiple blocked plugins have an |
| 294 // interest in them. | 205 // interest in them. |
| 295 IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) | 206 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder, message) |
| 296 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) | 207 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) |
| 297 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) | 208 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) |
| 298 IPC_END_MESSAGE_MAP() | 209 IPC_END_MESSAGE_MAP() |
| 299 | 210 |
| 300 return false; | 211 return false; |
| 301 } | 212 } |
| 302 | 213 |
| 303 void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) { | 214 void ChromePluginPlaceholder::OnLoadBlockedPlugins( |
| 304 CHECK(plugin_); | 215 const std::string& identifier) { |
| 305 if (!new_plugin) { | 216 plugins::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 } | 217 } |
| 342 | 218 |
| 343 void PluginPlaceholder::HidePlugin() { | 219 void ChromePluginPlaceholder::OpenAboutPluginsCallback( |
| 344 hidden_ = true; | 220 const CppArgumentList& args, |
| 345 WebPluginContainer* container = plugin_->container(); | 221 CppVariant* result) { |
| 346 WebElement element = container->element(); | 222 RenderThread::Get()->Send( |
| 347 element.setAttribute("style", "display: none;"); | 223 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 } | 224 } |
| 388 | 225 |
| 389 void PluginPlaceholder::WillDestroyPlugin() { | 226 void ChromePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { |
| 390 delete this; | 227 plugins::PluginPlaceholder::OnSetIsPrerendering(is_prerendering); |
| 391 } | 228 } |
| 392 | 229 |
| 393 #if defined(ENABLE_PLUGIN_INSTALLATION) | 230 #if defined(ENABLE_PLUGIN_INSTALLATION) |
| 394 void PluginPlaceholder::OnDidNotFindMissingPlugin() { | 231 void ChromePluginPlaceholder::OnDidNotFindMissingPlugin() { |
| 395 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND)); | 232 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND)); |
| 396 } | 233 } |
| 397 | 234 |
| 398 void PluginPlaceholder::OnFoundMissingPlugin(const string16& plugin_name) { | 235 void ChromePluginPlaceholder::OnFoundMissingPlugin( |
| 236 const string16& plugin_name) { |
| 399 if (status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound) | 237 if (status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound) |
| 400 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND, plugin_name)); | 238 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND, plugin_name)); |
| 401 has_host_ = true; | 239 has_host_ = true; |
| 402 plugin_name_ = plugin_name; | 240 plugin_name_ = plugin_name; |
| 403 } | 241 } |
| 404 | 242 |
| 405 void PluginPlaceholder::OnStartedDownloadingPlugin() { | 243 void ChromePluginPlaceholder::OnStartedDownloadingPlugin() { |
| 406 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_)); | 244 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_)); |
| 407 } | 245 } |
| 408 | 246 |
| 409 void PluginPlaceholder::OnFinishedDownloadingPlugin() { | 247 void ChromePluginPlaceholder::OnFinishedDownloadingPlugin() { |
| 410 bool is_installing = | 248 bool is_installing = |
| 411 status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; | 249 status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; |
| 412 SetMessage(l10n_util::GetStringFUTF16( | 250 SetMessage(l10n_util::GetStringFUTF16( |
| 413 is_installing ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING, | 251 is_installing ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING, |
| 414 plugin_name_)); | 252 plugin_name_)); |
| 415 } | 253 } |
| 416 | 254 |
| 417 void PluginPlaceholder::OnErrorDownloadingPlugin(const std::string& error) { | 255 void ChromePluginPlaceholder::OnErrorDownloadingPlugin( |
| 256 const std::string& error) { |
| 418 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR, | 257 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR, |
| 419 UTF8ToUTF16(error))); | 258 UTF8ToUTF16(error))); |
| 420 } | 259 } |
| 421 | 260 |
| 422 void PluginPlaceholder::OnCancelledDownloadingPlugin() { | 261 void ChromePluginPlaceholder::OnCancelledDownloadingPlugin() { |
| 423 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, | 262 SetMessage( |
| 424 plugin_name_)); | 263 l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, plugin_name_)); |
| 425 } | 264 } |
| 426 #endif // defined(ENABLE_PLUGIN_INSTALLATION) | 265 #endif // defined(ENABLE_PLUGIN_INSTALLATION) |
| 427 | 266 |
| 428 void PluginPlaceholder::PluginListChanged() { | 267 void ChromePluginPlaceholder::PluginListChanged() { |
| 429 if (!frame_) | 268 if (!GetFrame()) |
| 430 return; | 269 return; |
| 431 WebDocument document = frame_->top()->document(); | 270 WebDocument document = GetFrame()->top()->document(); |
| 432 if (document.isNull()) | 271 if (document.isNull()) |
| 433 return; | 272 return; |
| 434 | 273 |
| 435 ChromeViewHostMsg_GetPluginInfo_Output output; | 274 ChromeViewHostMsg_GetPluginInfo_Output output; |
| 436 std::string mime_type(plugin_params_.mimeType.utf8()); | 275 std::string mime_type(GetPluginParams().mimeType.utf8()); |
| 437 render_view()->Send(new ChromeViewHostMsg_GetPluginInfo( | 276 render_view()->Send( |
| 438 routing_id(), GURL(plugin_params_.url), document.url(), | 277 new ChromeViewHostMsg_GetPluginInfo(routing_id(), |
| 439 mime_type, &output)); | 278 GURL(GetPluginParams().url), |
| 279 document.url(), |
| 280 mime_type, |
| 281 &output)); |
| 440 if (output.status.value == status_->value) | 282 if (output.status.value == status_->value) |
| 441 return; | 283 return; |
| 442 WebPlugin* new_plugin = chrome::ChromeContentRendererClient::CreatePlugin( | 284 WebPlugin* new_plugin = chrome::ChromeContentRendererClient::CreatePlugin( |
| 443 render_view(), frame_, plugin_params_, output); | 285 render_view(), GetFrame(), GetPluginParams(), output); |
| 444 ReplacePlugin(new_plugin); | 286 ReplacePlugin(new_plugin); |
| 287 if (!new_plugin) { |
| 288 PluginUMAReporter::GetInstance()->ReportPluginMissing( |
| 289 GetPluginParams().mimeType.utf8(), GURL(GetPluginParams().url)); |
| 290 } |
| 445 } | 291 } |
| 446 | 292 |
| 447 void PluginPlaceholder::OnMenuAction(int request_id, unsigned action) { | 293 void ChromePluginPlaceholder::OnMenuAction(int request_id, unsigned action) { |
| 448 DCHECK_EQ(context_menu_request_id_, request_id); | 294 DCHECK_EQ(context_menu_request_id_, request_id); |
| 449 if (g_last_active_menu != this) | 295 if (g_last_active_menu != this) |
| 450 return; | 296 return; |
| 451 switch (action) { | 297 switch (action) { |
| 452 case chrome::MENU_COMMAND_PLUGIN_RUN: { | 298 case chrome::MENU_COMMAND_PLUGIN_RUN: { |
| 453 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Menu"); | 299 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Menu"); |
| 454 LoadPlugin(); | 300 LoadPlugin(); |
| 455 break; | 301 break; |
| 456 } | 302 } |
| 457 case chrome::MENU_COMMAND_PLUGIN_HIDE: { | 303 case chrome::MENU_COMMAND_PLUGIN_HIDE: { |
| 458 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Menu"); | 304 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Menu"); |
| 459 HidePlugin(); | 305 HidePlugin(); |
| 460 break; | 306 break; |
| 461 } | 307 } |
| 462 default: | 308 default: |
| 463 NOTREACHED(); | 309 NOTREACHED(); |
| 464 } | 310 } |
| 465 } | 311 } |
| 466 | 312 |
| 467 void PluginPlaceholder::OnMenuClosed(int request_id) { | 313 void ChromePluginPlaceholder::OnMenuClosed(int request_id) { |
| 468 DCHECK_EQ(context_menu_request_id_, request_id); | 314 DCHECK_EQ(context_menu_request_id_, request_id); |
| 469 context_menu_request_id_ = 0; | 315 context_menu_request_id_ = 0; |
| 470 } | 316 } |
| 471 | 317 |
| 472 void PluginPlaceholder::SetMessage(const string16& message) { | 318 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_) | 319 if (context_menu_request_id_) |
| 487 return; // Don't allow nested context menu requests. | 320 return; // Don't allow nested context menu requests. |
| 488 | 321 |
| 489 content::ContextMenuParams params; | 322 content::ContextMenuParams params; |
| 490 | 323 |
| 491 content::MenuItem name_item; | 324 content::MenuItem name_item; |
| 492 name_item.label = title_; | 325 name_item.label = title_; |
| 493 params.custom_items.push_back(name_item); | 326 params.custom_items.push_back(name_item); |
| 494 | 327 |
| 495 content::MenuItem separator_item; | 328 content::MenuItem separator_item; |
| 496 separator_item.type = content::MenuItem::SEPARATOR; | 329 separator_item.type = content::MenuItem::SEPARATOR; |
| 497 params.custom_items.push_back(separator_item); | 330 params.custom_items.push_back(separator_item); |
| 498 | 331 |
| 499 if (!plugin_info_.path.value().empty()) { | 332 if (!GetPluginInfo().path.value().empty()) { |
| 500 content::MenuItem run_item; | 333 content::MenuItem run_item; |
| 501 run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN; | 334 run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN; |
| 502 // Disable this menu item if the plugin is blocked by policy. | 335 // Disable this menu item if the plugin is blocked by policy. |
| 503 run_item.enabled = allow_loading_; | 336 run_item.enabled = LoadingAllowed(); |
| 504 run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN); | 337 run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN); |
| 505 params.custom_items.push_back(run_item); | 338 params.custom_items.push_back(run_item); |
| 506 } | 339 } |
| 507 | 340 |
| 508 content::MenuItem hide_item; | 341 content::MenuItem hide_item; |
| 509 hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE; | 342 hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE; |
| 510 hide_item.enabled = true; | 343 hide_item.enabled = true; |
| 511 hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE); | 344 hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE); |
| 512 params.custom_items.push_back(hide_item); | 345 params.custom_items.push_back(hide_item); |
| 513 | 346 |
| 514 params.x = event.windowX; | 347 params.x = event.windowX; |
| 515 params.y = event.windowY; | 348 params.y = event.windowY; |
| 516 | 349 |
| 517 context_menu_request_id_ = render_view()->ShowContextMenu(this, params); | 350 context_menu_request_id_ = render_view()->ShowContextMenu(this, params); |
| 518 g_last_active_menu = this; | 351 g_last_active_menu = this; |
| 519 } | 352 } |
| 520 | 353 |
| 521 void PluginPlaceholder::OnLoadBlockedPlugins(const std::string& identifier) { | 354 void ChromePluginPlaceholder::BindWebFrame(WebKit::WebFrame* frame) { |
| 522 if (!identifier.empty() && identifier != identifier_) | 355 plugins::PluginPlaceholder::BindWebFrame(frame); |
| 523 return; | 356 BindCallback("openAboutPlugins", |
| 524 | 357 base::Bind(&ChromePluginPlaceholder::OpenAboutPluginsCallback, |
| 525 RenderThread::Get()->RecordUserMetrics("Plugin_Load_UI"); | 358 base::Unretained(this))); |
| 526 LoadPlugin(); | |
| 527 } | 359 } |
| 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 |