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 |