Home | History | Annotate | Download | only in plugins
      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/grit/generated_resources.h"
     12 #include "chrome/grit/renderer_resources.h"
     13 #include "chrome/renderer/chrome_content_renderer_client.h"
     14 #include "chrome/renderer/custom_menu_commands.h"
     15 #include "chrome/renderer/plugins/plugin_uma.h"
     16 #include "content/app/strings/grit/content_strings.h"
     17 #include "content/public/common/context_menu_params.h"
     18 #include "content/public/renderer/render_frame.h"
     19 #include "content/public/renderer/render_thread.h"
     20 #include "gin/handle.h"
     21 #include "gin/object_template_builder.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