| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/renderer/plugins/plugin_placeholder.h" | |
| 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" | |
| 13 #include "base/values.h" | |
| 14 #include "chrome/common/prerender_messages.h" | |
| 15 #include "chrome/common/render_messages.h" | |
| 16 #include "chrome/renderer/chrome_content_renderer_client.h" | |
| 17 #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" | |
| 21 #include "content/public/renderer/render_thread.h" | |
| 22 #include "content/public/renderer/render_view.h" | |
| 23 #include "grit/generated_resources.h" | |
| 24 #include "grit/renderer_resources.h" | |
| 25 #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" | |
| 32 #include "third_party/WebKit/public/web/WebElement.h" | |
| 33 #include "third_party/WebKit/public/web/WebFrame.h" | |
| 34 #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" | |
| 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" | |
| 40 #include "ui/base/resource/resource_bundle.h" | |
| 41 #include "ui/webui/jstemplate_builder.h" | |
| 42 | |
| 43 using content::RenderThread; | |
| 44 using content::RenderView; | |
| 45 using WebKit::WebContextMenuData; | |
| 46 using WebKit::WebDocument; | |
| 47 using WebKit::WebElement; | |
| 48 using WebKit::WebFrame; | |
| 49 using WebKit::WebMouseEvent; | |
| 50 using WebKit::WebNode; | |
| 51 using WebKit::WebPlugin; | |
| 52 using WebKit::WebPluginContainer; | |
| 53 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; | |
| 60 using webkit_glue::CppVariant; | |
| 61 | |
| 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 { | |
| 72 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 | |
| 89 | |
| 90 // static | |
| 91 PluginPlaceholder* PluginPlaceholder::CreateMissingPlugin( | |
| 92 RenderView* render_view, | |
| 93 WebFrame* frame, | |
| 94 const WebPluginParams& params) { | |
| 95 const base::StringPiece template_html( | |
| 96 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 97 IDR_BLOCKED_PLUGIN_HTML)); | |
| 98 | |
| 99 base::DictionaryValue values; | |
| 100 #if defined(ENABLE_PLUGIN_INSTALLATION) | |
| 101 values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); | |
| 102 #else | |
| 103 values.SetString("message", | |
| 104 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); | |
| 105 #endif | |
| 106 | |
| 107 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | |
| 108 | |
| 109 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. | |
| 110 PluginPlaceholder* missing_plugin = new PluginPlaceholder( | |
| 111 render_view, frame, params, html_data, params.mimeType); | |
| 112 missing_plugin->set_allow_loading(true); | |
| 113 #if defined(ENABLE_PLUGIN_INSTALLATION) | |
| 114 RenderThread::Get()->Send(new ChromeViewHostMsg_FindMissingPlugin( | |
| 115 missing_plugin->routing_id(), missing_plugin->CreateRoutingId(), | |
| 116 params.mimeType.utf8())); | |
| 117 #endif | |
| 118 return missing_plugin; | |
| 119 } | |
| 120 | |
| 121 PluginPlaceholder* PluginPlaceholder::CreateErrorPlugin( | |
| 122 RenderView* render_view, | |
| 123 const base::FilePath& file_path) { | |
| 124 base::DictionaryValue values; | |
| 125 values.SetString("message", | |
| 126 l10n_util::GetStringUTF8(IDS_PLUGIN_INITIALIZATION_ERROR)); | |
| 127 | |
| 128 const base::StringPiece template_html( | |
| 129 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 130 IDR_BLOCKED_PLUGIN_HTML)); | |
| 131 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | |
| 132 | |
| 133 WebPluginParams params; | |
| 134 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. | |
| 135 PluginPlaceholder* plugin = new PluginPlaceholder( | |
| 136 render_view, NULL, params, html_data, params.mimeType); | |
| 137 | |
| 138 RenderThread::Get()->Send( | |
| 139 new ChromeViewHostMsg_CouldNotLoadPlugin(plugin->routing_id(), | |
| 140 file_path)); | |
| 141 return plugin; | |
| 142 } | |
| 143 | |
| 144 // static | |
| 145 PluginPlaceholder* PluginPlaceholder::CreateBlockedPlugin( | |
| 146 RenderView* render_view, | |
| 147 WebFrame* frame, | |
| 148 const WebPluginParams& params, | |
| 149 const content::WebPluginInfo& plugin, | |
| 150 const std::string& identifier, | |
| 151 const string16& name, | |
| 152 int template_id, | |
| 153 const string16& message) { | |
| 154 base::DictionaryValue values; | |
| 155 values.SetString("message", message); | |
| 156 values.SetString("name", name); | |
| 157 values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); | |
| 158 | |
| 159 const base::StringPiece template_html( | |
| 160 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 161 template_id)); | |
| 162 | |
| 163 DCHECK(!template_html.empty()) << "unable to load template. ID: " | |
| 164 << template_id; | |
| 165 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); | |
| 166 | |
| 167 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. | |
| 168 PluginPlaceholder* blocked_plugin = new PluginPlaceholder( | |
| 169 render_view, frame, params, html_data, name); | |
| 170 blocked_plugin->plugin_info_ = plugin; | |
| 171 blocked_plugin->identifier_ = identifier; | |
| 172 return blocked_plugin; | |
| 173 } | |
| 174 | |
| 175 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
| 176 // static | |
| 177 PluginPlaceholder* PluginPlaceholder::CreateMobileYoutubePlugin( | |
| 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 } | |
| 237 | |
| 238 #if defined(ENABLE_PLUGIN_INSTALLATION) | |
| 239 int32 PluginPlaceholder::CreateRoutingId() { | |
| 240 placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); | |
| 241 RenderThread::Get()->AddRoute(placeholder_routing_id_, this); | |
| 242 return placeholder_routing_id_; | |
| 243 } | |
| 244 #endif | |
| 245 | |
| 246 void PluginPlaceholder::SetStatus( | |
| 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) | |
| 272 bool handled = true; | |
| 273 IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) | |
| 274 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, | |
| 275 OnFoundMissingPlugin) | |
| 276 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, | |
| 277 OnDidNotFindMissingPlugin) | |
| 278 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, | |
| 279 OnStartedDownloadingPlugin) | |
| 280 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, | |
| 281 OnFinishedDownloadingPlugin) | |
| 282 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, | |
| 283 OnErrorDownloadingPlugin) | |
| 284 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, | |
| 285 OnCancelledDownloadingPlugin) | |
| 286 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 287 IPC_END_MESSAGE_MAP() | |
| 288 | |
| 289 if (handled) | |
| 290 return true; | |
| 291 #endif | |
| 292 | |
| 293 // We don't swallow these messages because multiple blocked plugins have an | |
| 294 // interest in them. | |
| 295 IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) | |
| 296 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) | |
| 297 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) | |
| 298 IPC_END_MESSAGE_MAP() | |
| 299 | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) { | |
| 304 CHECK(plugin_); | |
| 305 if (!new_plugin) { | |
| 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 } | |
| 342 | |
| 343 void PluginPlaceholder::HidePlugin() { | |
| 344 hidden_ = true; | |
| 345 WebPluginContainer* container = plugin_->container(); | |
| 346 WebElement element = container->element(); | |
| 347 element.setAttribute("style", "display: none;"); | |
| 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 } | |
| 388 | |
| 389 void PluginPlaceholder::WillDestroyPlugin() { | |
| 390 delete this; | |
| 391 } | |
| 392 | |
| 393 #if defined(ENABLE_PLUGIN_INSTALLATION) | |
| 394 void PluginPlaceholder::OnDidNotFindMissingPlugin() { | |
| 395 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND)); | |
| 396 } | |
| 397 | |
| 398 void PluginPlaceholder::OnFoundMissingPlugin(const string16& plugin_name) { | |
| 399 if (status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound) | |
| 400 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND, plugin_name)); | |
| 401 has_host_ = true; | |
| 402 plugin_name_ = plugin_name; | |
| 403 } | |
| 404 | |
| 405 void PluginPlaceholder::OnStartedDownloadingPlugin() { | |
| 406 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_)); | |
| 407 } | |
| 408 | |
| 409 void PluginPlaceholder::OnFinishedDownloadingPlugin() { | |
| 410 bool is_installing = | |
| 411 status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; | |
| 412 SetMessage(l10n_util::GetStringFUTF16( | |
| 413 is_installing ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING, | |
| 414 plugin_name_)); | |
| 415 } | |
| 416 | |
| 417 void PluginPlaceholder::OnErrorDownloadingPlugin(const std::string& error) { | |
| 418 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR, | |
| 419 UTF8ToUTF16(error))); | |
| 420 } | |
| 421 | |
| 422 void PluginPlaceholder::OnCancelledDownloadingPlugin() { | |
| 423 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, | |
| 424 plugin_name_)); | |
| 425 } | |
| 426 #endif // defined(ENABLE_PLUGIN_INSTALLATION) | |
| 427 | |
| 428 void PluginPlaceholder::PluginListChanged() { | |
| 429 if (!frame_) | |
| 430 return; | |
| 431 WebDocument document = frame_->top()->document(); | |
| 432 if (document.isNull()) | |
| 433 return; | |
| 434 | |
| 435 ChromeViewHostMsg_GetPluginInfo_Output output; | |
| 436 std::string mime_type(plugin_params_.mimeType.utf8()); | |
| 437 render_view()->Send(new ChromeViewHostMsg_GetPluginInfo( | |
| 438 routing_id(), GURL(plugin_params_.url), document.url(), | |
| 439 mime_type, &output)); | |
| 440 if (output.status.value == status_->value) | |
| 441 return; | |
| 442 WebPlugin* new_plugin = chrome::ChromeContentRendererClient::CreatePlugin( | |
| 443 render_view(), frame_, plugin_params_, output); | |
| 444 ReplacePlugin(new_plugin); | |
| 445 } | |
| 446 | |
| 447 void PluginPlaceholder::OnMenuAction(int request_id, unsigned action) { | |
| 448 DCHECK_EQ(context_menu_request_id_, request_id); | |
| 449 if (g_last_active_menu != this) | |
| 450 return; | |
| 451 switch (action) { | |
| 452 case chrome::MENU_COMMAND_PLUGIN_RUN: { | |
| 453 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Menu"); | |
| 454 LoadPlugin(); | |
| 455 break; | |
| 456 } | |
| 457 case chrome::MENU_COMMAND_PLUGIN_HIDE: { | |
| 458 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Menu"); | |
| 459 HidePlugin(); | |
| 460 break; | |
| 461 } | |
| 462 default: | |
| 463 NOTREACHED(); | |
| 464 } | |
| 465 } | |
| 466 | |
| 467 void PluginPlaceholder::OnMenuClosed(int request_id) { | |
| 468 DCHECK_EQ(context_menu_request_id_, request_id); | |
| 469 context_menu_request_id_ = 0; | |
| 470 } | |
| 471 | |
| 472 void PluginPlaceholder::SetMessage(const string16& message) { | |
| 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_) | |
| 487 return; // Don't allow nested context menu requests. | |
| 488 | |
| 489 content::ContextMenuParams params; | |
| 490 | |
| 491 content::MenuItem name_item; | |
| 492 name_item.label = title_; | |
| 493 params.custom_items.push_back(name_item); | |
| 494 | |
| 495 content::MenuItem separator_item; | |
| 496 separator_item.type = content::MenuItem::SEPARATOR; | |
| 497 params.custom_items.push_back(separator_item); | |
| 498 | |
| 499 if (!plugin_info_.path.value().empty()) { | |
| 500 content::MenuItem run_item; | |
| 501 run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN; | |
| 502 // Disable this menu item if the plugin is blocked by policy. | |
| 503 run_item.enabled = allow_loading_; | |
| 504 run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN); | |
| 505 params.custom_items.push_back(run_item); | |
| 506 } | |
| 507 | |
| 508 content::MenuItem hide_item; | |
| 509 hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE; | |
| 510 hide_item.enabled = true; | |
| 511 hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE); | |
| 512 params.custom_items.push_back(hide_item); | |
| 513 | |
| 514 params.x = event.windowX; | |
| 515 params.y = event.windowY; | |
| 516 | |
| 517 context_menu_request_id_ = render_view()->ShowContextMenu(this, params); | |
| 518 g_last_active_menu = this; | |
| 519 } | |
| 520 | |
| 521 void PluginPlaceholder::OnLoadBlockedPlugins(const std::string& identifier) { | |
| 522 if (!identifier.empty() && identifier != identifier_) | |
| 523 return; | |
| 524 | |
| 525 RenderThread::Get()->RecordUserMetrics("Plugin_Load_UI"); | |
| 526 LoadPlugin(); | |
| 527 } | |
| 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 |