chrome_plugin_placeholder.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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
31using base::UserMetricsAction;
32using blink::WebDocument;
33using blink::WebElement;
34using blink::WebFrame;
35using blink::WebLocalFrame;
36using blink::WebMouseEvent;
37using blink::WebNode;
38using blink::WebPlugin;
39using blink::WebPluginContainer;
40using blink::WebPluginParams;
41using content::RenderThread;
42using content::RenderView;
43
44namespace {
45const plugins::PluginPlaceholder* g_last_active_menu = NULL;
46}  // namespace
47
48const char ChromePluginPlaceholder::kPluginPlaceholderDataURL[] =
49    "chrome://pluginplaceholderdata/";
50
51ChromePluginPlaceholder::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
72ChromePluginPlaceholder::~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
89ChromePluginPlaceholder* 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
121ChromePluginPlaceholder* 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
144ChromePluginPlaceholder* 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
173void ChromePluginPlaceholder::SetStatus(
174    const ChromeViewHostMsg_GetPluginInfo_Status& status) {
175  status_->value = status.value;
176}
177
178#if defined(ENABLE_PLUGIN_INSTALLATION)
179int32 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
186bool 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
218void ChromePluginPlaceholder::OnLoadBlockedPlugins(
219    const std::string& identifier) {
220  plugins::PluginPlaceholder::OnLoadBlockedPlugins(identifier);
221}
222
223void ChromePluginPlaceholder::OpenAboutPluginsCallback() {
224  RenderThread::Get()->Send(
225      new ChromeViewHostMsg_OpenAboutPlugins(routing_id()));
226}
227
228void ChromePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) {
229  plugins::PluginPlaceholder::OnSetIsPrerendering(is_prerendering);
230}
231
232#if defined(ENABLE_PLUGIN_INSTALLATION)
233void ChromePluginPlaceholder::OnDidNotFindMissingPlugin() {
234  SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND));
235}
236
237void 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
245void ChromePluginPlaceholder::OnStartedDownloadingPlugin() {
246  SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING, plugin_name_));
247}
248
249void 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
257void ChromePluginPlaceholder::OnErrorDownloadingPlugin(
258    const std::string& error) {
259  SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR,
260                                        base::UTF8ToUTF16(error)));
261}
262
263void ChromePluginPlaceholder::OnCancelledDownloadingPlugin() {
264  SetMessage(
265      l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED, plugin_name_));
266}
267#endif  // defined(ENABLE_PLUGIN_INSTALLATION)
268
269void 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
295void 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
315void ChromePluginPlaceholder::OnMenuClosed(int request_id) {
316  DCHECK_EQ(context_menu_request_id_, request_id);
317  context_menu_request_id_ = 0;
318}
319
320void 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
358void 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
370gin::ObjectTemplateBuilder ChromePluginPlaceholder::GetObjectTemplateBuilder(
371    v8::Isolate* isolate) {
372  return PluginPlaceholder::GetObjectTemplateBuilder(isolate).SetMethod(
373      "openAboutPlugins", &ChromePluginPlaceholder::OpenAboutPluginsCallback);
374}
375