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 3 // found in the LICENSE file. 4 5 #include "chrome/renderer/plugins/chrome_plugin_placeholder.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "base/values.h" 9 #include "chrome/common/prerender_messages.h" 10 #include "chrome/common/render_messages.h" 11 #include "chrome/renderer/chrome_content_renderer_client.h" 12 #include "chrome/renderer/custom_menu_commands.h" 13 #include "chrome/renderer/plugins/plugin_uma.h" 14 #include "content/public/common/context_menu_params.h" 15 #include "content/public/renderer/render_frame.h" 16 #include "content/public/renderer/render_thread.h" 17 #include "gin/handle.h" 18 #include "gin/object_template_builder.h" 19 #include "grit/generated_resources.h" 20 #include "grit/renderer_resources.h" 21 #include "grit/webkit_strings.h" 22 #include "third_party/WebKit/public/web/WebDocument.h" 23 #include "third_party/WebKit/public/web/WebInputEvent.h" 24 #include "third_party/WebKit/public/web/WebKit.h" 25 #include "third_party/WebKit/public/web/WebLocalFrame.h" 26 #include "third_party/WebKit/public/web/WebScriptSource.h" 27 #include "ui/base/l10n/l10n_util.h" 28 #include "ui/base/resource/resource_bundle.h" 29 #include "ui/base/webui/jstemplate_builder.h" 30 31 using base::UserMetricsAction; 32 using blink::WebDocument; 33 using blink::WebElement; 34 using blink::WebFrame; 35 using blink::WebLocalFrame; 36 using blink::WebMouseEvent; 37 using blink::WebNode; 38 using blink::WebPlugin; 39 using blink::WebPluginContainer; 40 using blink::WebPluginParams; 41 using content::RenderThread; 42 using content::RenderView; 43 44 namespace { 45 const plugins::PluginPlaceholder* g_last_active_menu = NULL; 46 } // namespace 47 48 const char ChromePluginPlaceholder::kPluginPlaceholderDataURL[] = 49 "chrome://pluginplaceholderdata/"; 50 51 ChromePluginPlaceholder::ChromePluginPlaceholder( 52 content::RenderFrame* render_frame, 53 blink::WebLocalFrame* frame, 54 const blink::WebPluginParams& params, 55 const std::string& html_data, 56 const base::string16& title) 57 : plugins::PluginPlaceholder(render_frame, 58 frame, 59 params, 60 html_data, 61 GURL(kPluginPlaceholderDataURL)), 62 status_(new ChromeViewHostMsg_GetPluginInfo_Status), 63 title_(title), 64 #if defined(ENABLE_PLUGIN_INSTALLATION) 65 placeholder_routing_id_(MSG_ROUTING_NONE), 66 #endif 67 has_host_(false), 68 context_menu_request_id_(0) { 69 RenderThread::Get()->AddObserver(this); 70 } 71 72 ChromePluginPlaceholder::~ChromePluginPlaceholder() { 73 RenderThread::Get()->RemoveObserver(this); 74 if (context_menu_request_id_ && render_frame()) 75 render_frame()->CancelContextMenu(context_menu_request_id_); 76 77 #if defined(ENABLE_PLUGIN_INSTALLATION) 78 if (placeholder_routing_id_ == MSG_ROUTING_NONE) 79 return; 80 RenderThread::Get()->RemoveRoute(placeholder_routing_id_); 81 if (has_host_) { 82 RenderThread::Get()->Send(new ChromeViewHostMsg_RemovePluginPlaceholderHost( 83 routing_id(), placeholder_routing_id_)); 84 } 85 #endif 86 } 87 88 // static 89 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateMissingPlugin( 90 content::RenderFrame* render_frame, 91 WebLocalFrame* frame, 92 const WebPluginParams& params) { 93 const base::StringPiece template_html( 94 ResourceBundle::GetSharedInstance().GetRawDataResource( 95 IDR_BLOCKED_PLUGIN_HTML)); 96 97 base::DictionaryValue values; 98 #if defined(ENABLE_PLUGIN_INSTALLATION) 99 values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING)); 100 #else 101 values.SetString("message", 102 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED)); 103 #endif 104 105 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); 106 107 // |missing_plugin| will destroy itself when its WebViewPlugin is going away. 108 ChromePluginPlaceholder* missing_plugin = new ChromePluginPlaceholder( 109 render_frame, frame, params, html_data, params.mimeType); 110 missing_plugin->set_allow_loading(true); 111 #if defined(ENABLE_PLUGIN_INSTALLATION) 112 RenderThread::Get()->Send( 113 new ChromeViewHostMsg_FindMissingPlugin(missing_plugin->routing_id(), 114 missing_plugin->CreateRoutingId(), 115 params.mimeType.utf8())); 116 #endif 117 return missing_plugin; 118 } 119 120 // static 121 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateErrorPlugin( 122 content::RenderFrame* render_frame, 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 ChromePluginPlaceholder* plugin = new ChromePluginPlaceholder( 136 render_frame, NULL, params, html_data, params.mimeType); 137 138 RenderThread::Get()->Send(new ChromeViewHostMsg_CouldNotLoadPlugin( 139 plugin->routing_id(), file_path)); 140 return plugin; 141 } 142 143 // static 144 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateBlockedPlugin( 145 content::RenderFrame* render_frame, 146 WebLocalFrame* frame, 147 const WebPluginParams& params, 148 const content::WebPluginInfo& plugin, 149 const std::string& identifier, 150 const base::string16& name, 151 int template_id, 152 const base::string16& message) { 153 base::DictionaryValue values; 154 values.SetString("message", message); 155 values.SetString("name", name); 156 values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE)); 157 158 const base::StringPiece template_html( 159 ResourceBundle::GetSharedInstance().GetRawDataResource(template_id)); 160 161 DCHECK(!template_html.empty()) << "unable to load template. ID: " 162 << template_id; 163 std::string html_data = webui::GetI18nTemplateHtml(template_html, &values); 164 165 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away. 166 ChromePluginPlaceholder* blocked_plugin = new ChromePluginPlaceholder( 167 render_frame, frame, params, html_data, name); 168 blocked_plugin->SetPluginInfo(plugin); 169 blocked_plugin->SetIdentifier(identifier); 170 return blocked_plugin; 171 } 172 173 void ChromePluginPlaceholder::SetStatus( 174 const ChromeViewHostMsg_GetPluginInfo_Status& status) { 175 status_->value = status.value; 176 } 177 178 #if defined(ENABLE_PLUGIN_INSTALLATION) 179 int32 ChromePluginPlaceholder::CreateRoutingId() { 180 placeholder_routing_id_ = RenderThread::Get()->GenerateRoutingID(); 181 RenderThread::Get()->AddRoute(placeholder_routing_id_, this); 182 return placeholder_routing_id_; 183 } 184 #endif 185 186 bool ChromePluginPlaceholder::OnMessageReceived(const IPC::Message& message) { 187 #if defined(ENABLE_PLUGIN_INSTALLATION) 188 bool handled = true; 189 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder, message) 190 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin, OnFoundMissingPlugin) 191 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin, 192 OnDidNotFindMissingPlugin) 193 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin, 194 OnStartedDownloadingPlugin) 195 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin, 196 OnFinishedDownloadingPlugin) 197 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin, 198 OnErrorDownloadingPlugin) 199 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin, 200 OnCancelledDownloadingPlugin) 201 IPC_MESSAGE_UNHANDLED(handled = false) 202 IPC_END_MESSAGE_MAP() 203 204 if (handled) 205 return true; 206 #endif 207 208 // We don't swallow these messages because multiple blocked plugins and other 209 // objects have an interest in them. 210 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder, message) 211 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) 212 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) 213 IPC_END_MESSAGE_MAP() 214 215 return false; 216 } 217 218 void ChromePluginPlaceholder::OnLoadBlockedPlugins( 219 const std::string& identifier) { 220 plugins::PluginPlaceholder::OnLoadBlockedPlugins(identifier); 221 } 222 223 void ChromePluginPlaceholder::OpenAboutPluginsCallback() { 224 RenderThread::Get()->Send( 225 new ChromeViewHostMsg_OpenAboutPlugins(routing_id())); 226 } 227 228 void ChromePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { 229 plugins::PluginPlaceholder::OnSetIsPrerendering(is_prerendering); 230 } 231 232 #if defined(ENABLE_PLUGIN_INSTALLATION) 233 void ChromePluginPlaceholder::OnDidNotFindMissingPlugin() { 234 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND)); 235 } 236 237 void ChromePluginPlaceholder::OnFoundMissingPlugin( 238 const base::string16& plugin_name) { 239 if (status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound) 240 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND, plugin_name)); 241 has_host_ = true; 242 plugin_name_ = plugin_name; 243 } 244 245 void ChromePluginPlaceholder::OnStartedDownloadingPlugin() { 246 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_)); 247 } 248 249 void ChromePluginPlaceholder::OnFinishedDownloadingPlugin() { 250 bool is_installing = 251 status_->value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; 252 SetMessage(l10n_util::GetStringFUTF16( 253 is_installing ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING, 254 plugin_name_)); 255 } 256 257 void ChromePluginPlaceholder::OnErrorDownloadingPlugin( 258 const std::string& error) { 259 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR, 260 base::UTF8ToUTF16(error))); 261 } 262 263 void ChromePluginPlaceholder::OnCancelledDownloadingPlugin() { 264 SetMessage( 265 l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, plugin_name_)); 266 } 267 #endif // defined(ENABLE_PLUGIN_INSTALLATION) 268 269 void ChromePluginPlaceholder::PluginListChanged() { 270 if (!GetFrame() || !plugin()) 271 return; 272 WebDocument document = GetFrame()->top()->document(); 273 if (document.isNull()) 274 return; 275 276 ChromeViewHostMsg_GetPluginInfo_Output output; 277 std::string mime_type(GetPluginParams().mimeType.utf8()); 278 render_frame()->Send( 279 new ChromeViewHostMsg_GetPluginInfo(routing_id(), 280 GURL(GetPluginParams().url), 281 document.url(), 282 mime_type, 283 &output)); 284 if (output.status.value == status_->value) 285 return; 286 WebPlugin* new_plugin = ChromeContentRendererClient::CreatePlugin( 287 render_frame(), GetFrame(), GetPluginParams(), output); 288 ReplacePlugin(new_plugin); 289 if (!new_plugin) { 290 PluginUMAReporter::GetInstance()->ReportPluginMissing( 291 GetPluginParams().mimeType.utf8(), GURL(GetPluginParams().url)); 292 } 293 } 294 295 void ChromePluginPlaceholder::OnMenuAction(int request_id, unsigned action) { 296 DCHECK_EQ(context_menu_request_id_, request_id); 297 if (g_last_active_menu != this) 298 return; 299 switch (action) { 300 case chrome::MENU_COMMAND_PLUGIN_RUN: { 301 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Menu")); 302 LoadPlugin(); 303 break; 304 } 305 case chrome::MENU_COMMAND_PLUGIN_HIDE: { 306 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Hide_Menu")); 307 HidePlugin(); 308 break; 309 } 310 default: 311 NOTREACHED(); 312 } 313 } 314 315 void ChromePluginPlaceholder::OnMenuClosed(int request_id) { 316 DCHECK_EQ(context_menu_request_id_, request_id); 317 context_menu_request_id_ = 0; 318 } 319 320 void ChromePluginPlaceholder::ShowContextMenu(const WebMouseEvent& event) { 321 #if !defined(OS_ANDROID) // The context menu is not applicable on Android. 322 if (context_menu_request_id_) 323 return; // Don't allow nested context menu requests. 324 325 content::ContextMenuParams params; 326 327 content::MenuItem name_item; 328 name_item.label = title_; 329 params.custom_items.push_back(name_item); 330 331 content::MenuItem separator_item; 332 separator_item.type = content::MenuItem::SEPARATOR; 333 params.custom_items.push_back(separator_item); 334 335 if (!GetPluginInfo().path.value().empty()) { 336 content::MenuItem run_item; 337 run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN; 338 // Disable this menu item if the plugin is blocked by policy. 339 run_item.enabled = LoadingAllowed(); 340 run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN); 341 params.custom_items.push_back(run_item); 342 } 343 344 content::MenuItem hide_item; 345 hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE; 346 hide_item.enabled = true; 347 hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE); 348 params.custom_items.push_back(hide_item); 349 350 params.x = event.windowX; 351 params.y = event.windowY; 352 353 context_menu_request_id_ = render_frame()->ShowContextMenu(this, params); 354 g_last_active_menu = this; 355 #endif // OS_ANDROID 356 } 357 358 void ChromePluginPlaceholder::BindWebFrame(blink::WebFrame* frame) { 359 v8::Isolate* isolate = blink::mainThreadIsolate(); 360 v8::HandleScope handle_scope(isolate); 361 v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); 362 DCHECK(!context.IsEmpty()); 363 364 v8::Context::Scope context_scope(context); 365 v8::Handle<v8::Object> global = context->Global(); 366 global->Set(gin::StringToV8(isolate, "plugin"), 367 gin::CreateHandle(isolate, this).ToV8()); 368 } 369 370 gin::ObjectTemplateBuilder ChromePluginPlaceholder::GetObjectTemplateBuilder( 371 v8::Isolate* isolate) { 372 return PluginPlaceholder::GetObjectTemplateBuilder(isolate).SetMethod( 373 "openAboutPlugins", &ChromePluginPlaceholder::OpenAboutPluginsCallback); 374 } 375