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.
5 #include "chrome/renderer/plugins/chrome_plugin_placeholder.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/values.h"
10 #include "chrome/common/prerender_messages.h"
11 #include "chrome/common/render_messages.h"
12 #include "chrome/grit/generated_resources.h"
13 #include "chrome/grit/renderer_resources.h"
14 #include "chrome/renderer/chrome_content_renderer_client.h"
15 #include "chrome/renderer/custom_menu_commands.h"
16 #include "chrome/renderer/plugins/plugin_preroller.h"
17 #include "chrome/renderer/plugins/plugin_uma.h"
18 #include "components/content_settings/content/common/content_settings_messages.h"
19 #include "content/public/common/context_menu_params.h"
20 #include "content/public/renderer/render_frame.h"
21 #include "content/public/renderer/render_thread.h"
22 #include "gin/object_template_builder.h"
23 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebInputEvent.h"
25 #include "third_party/WebKit/public/web/WebLocalFrame.h"
26 #include "third_party/WebKit/public/web/WebScriptSource.h"
27 #include "third_party/WebKit/public/web/WebView.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/base/webui/jstemplate_builder.h"
31 #include "ui/gfx/geometry/size.h"
32 #include "url/url_util.h"
34 using base::UserMetricsAction
;
35 using content::RenderThread
;
36 using content::RenderView
;
39 const ChromePluginPlaceholder
* g_last_active_menu
= NULL
;
42 gin::WrapperInfo
ChromePluginPlaceholder::kWrapperInfo
= {
43 gin::kEmbedderNativeGin
};
45 ChromePluginPlaceholder::ChromePluginPlaceholder(
46 content::RenderFrame
* render_frame
,
47 blink::WebLocalFrame
* frame
,
48 const blink::WebPluginParams
& params
,
49 const std::string
& html_data
,
50 const base::string16
& title
)
51 : plugins::LoadablePluginPlaceholder(render_frame
,
55 status_(ChromeViewHostMsg_GetPluginInfo_Status::kAllowed
),
57 #if defined(ENABLE_PLUGIN_INSTALLATION)
58 placeholder_routing_id_(MSG_ROUTING_NONE
),
61 context_menu_request_id_(0) {
62 RenderThread::Get()->AddObserver(this);
65 ChromePluginPlaceholder::~ChromePluginPlaceholder() {
66 RenderThread::Get()->RemoveObserver(this);
67 if (context_menu_request_id_
&& render_frame())
68 render_frame()->CancelContextMenu(context_menu_request_id_
);
70 #if defined(ENABLE_PLUGIN_INSTALLATION)
71 if (placeholder_routing_id_
== MSG_ROUTING_NONE
)
73 RenderThread::Get()->RemoveRoute(placeholder_routing_id_
);
75 RenderThread::Get()->Send(new ChromeViewHostMsg_RemovePluginPlaceholderHost(
76 routing_id(), placeholder_routing_id_
));
82 ChromePluginPlaceholder
* ChromePluginPlaceholder::CreateLoadableMissingPlugin(
83 content::RenderFrame
* render_frame
,
84 blink::WebLocalFrame
* frame
,
85 const blink::WebPluginParams
& params
) {
86 const base::StringPiece
template_html(
87 ResourceBundle::GetSharedInstance().GetRawDataResource(
88 IDR_BLOCKED_PLUGIN_HTML
));
90 base::DictionaryValue values
;
91 values
.SetString("message",
92 l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED
));
94 std::string html_data
= webui::GetI18nTemplateHtml(template_html
, &values
);
96 // Will destroy itself when its WebViewPlugin is going away.
97 return new ChromePluginPlaceholder(render_frame
, frame
, params
, html_data
,
102 ChromePluginPlaceholder
* ChromePluginPlaceholder::CreateBlockedPlugin(
103 content::RenderFrame
* render_frame
,
104 blink::WebLocalFrame
* frame
,
105 const blink::WebPluginParams
& params
,
106 const content::WebPluginInfo
& info
,
107 const std::string
& identifier
,
108 const base::string16
& name
,
110 const base::string16
& message
,
111 const PlaceholderPosterInfo
& poster_info
) {
112 base::DictionaryValue values
;
113 values
.SetString("message", message
);
114 values
.SetString("name", name
);
115 values
.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE
));
116 values
.SetString("pluginType",
117 frame
->view()->mainFrame()->document().isPluginDocument()
121 if (!poster_info
.poster_attribute
.empty()) {
122 values
.SetString("poster", poster_info
.poster_attribute
);
123 values
.SetString("baseurl", poster_info
.base_url
.spec());
125 if (!poster_info
.custom_poster_size
.IsEmpty()) {
127 blink::WebView::zoomLevelToZoomFactor(frame
->view()->zoomLevel());
128 int width
= roundf(poster_info
.custom_poster_size
.width() / zoom_factor
);
130 roundf(poster_info
.custom_poster_size
.height() / zoom_factor
);
131 values
.SetString("visibleWidth", base::IntToString(width
) + "px");
132 values
.SetString("visibleHeight", base::IntToString(height
) + "px");
136 const base::StringPiece
template_html(
137 ResourceBundle::GetSharedInstance().GetRawDataResource(template_id
));
139 DCHECK(!template_html
.empty()) << "unable to load template. ID: "
141 std::string html_data
= webui::GetI18nTemplateHtml(template_html
, &values
);
143 // |blocked_plugin| will destroy itself when its WebViewPlugin is going away.
144 ChromePluginPlaceholder
* blocked_plugin
= new ChromePluginPlaceholder(
145 render_frame
, frame
, params
, html_data
, name
);
147 if (!poster_info
.poster_attribute
.empty())
148 blocked_plugin
->BlockForPowerSaverPoster();
149 blocked_plugin
->SetPluginInfo(info
);
150 blocked_plugin
->SetIdentifier(identifier
);
151 return blocked_plugin
;
154 void ChromePluginPlaceholder::SetStatus(
155 ChromeViewHostMsg_GetPluginInfo_Status status
) {
159 #if defined(ENABLE_PLUGIN_INSTALLATION)
160 int32
ChromePluginPlaceholder::CreateRoutingId() {
161 placeholder_routing_id_
= RenderThread::Get()->GenerateRoutingID();
162 RenderThread::Get()->AddRoute(placeholder_routing_id_
, this);
163 return placeholder_routing_id_
;
167 bool ChromePluginPlaceholder::OnMessageReceived(const IPC::Message
& message
) {
168 #if defined(ENABLE_PLUGIN_INSTALLATION)
170 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder
, message
)
171 IPC_MESSAGE_HANDLER(ChromeViewMsg_FoundMissingPlugin
, OnFoundMissingPlugin
)
172 IPC_MESSAGE_HANDLER(ChromeViewMsg_DidNotFindMissingPlugin
,
173 OnDidNotFindMissingPlugin
)
174 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartedDownloadingPlugin
,
175 OnStartedDownloadingPlugin
)
176 IPC_MESSAGE_HANDLER(ChromeViewMsg_FinishedDownloadingPlugin
,
177 OnFinishedDownloadingPlugin
)
178 IPC_MESSAGE_HANDLER(ChromeViewMsg_ErrorDownloadingPlugin
,
179 OnErrorDownloadingPlugin
)
180 IPC_MESSAGE_HANDLER(ChromeViewMsg_CancelledDownloadingPlugin
,
181 OnCancelledDownloadingPlugin
)
182 IPC_MESSAGE_UNHANDLED(handled
= false)
183 IPC_END_MESSAGE_MAP()
189 // We don't swallow these messages because multiple blocked plugins and other
190 // objects have an interest in them.
191 IPC_BEGIN_MESSAGE_MAP(ChromePluginPlaceholder
, message
)
192 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering
, OnSetIsPrerendering
)
193 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins
, OnLoadBlockedPlugins
)
194 IPC_END_MESSAGE_MAP()
199 void ChromePluginPlaceholder::OpenAboutPluginsCallback() {
200 RenderThread::Get()->Send(
201 new ChromeViewHostMsg_OpenAboutPlugins(routing_id()));
204 #if defined(ENABLE_PLUGIN_INSTALLATION)
205 void ChromePluginPlaceholder::OnDidNotFindMissingPlugin() {
206 SetMessage(l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_FOUND
));
209 void ChromePluginPlaceholder::OnFoundMissingPlugin(
210 const base::string16
& plugin_name
) {
211 if (status_
== ChromeViewHostMsg_GetPluginInfo_Status::kNotFound
)
212 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_FOUND
, plugin_name
));
214 plugin_name_
= plugin_name
;
217 void ChromePluginPlaceholder::OnStartedDownloadingPlugin() {
218 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING
, plugin_name_
));
221 void ChromePluginPlaceholder::OnFinishedDownloadingPlugin() {
223 status_
== ChromeViewHostMsg_GetPluginInfo_Status::kNotFound
;
224 SetMessage(l10n_util::GetStringFUTF16(
225 is_installing
? IDS_PLUGIN_INSTALLING
: IDS_PLUGIN_UPDATING
,
229 void ChromePluginPlaceholder::OnErrorDownloadingPlugin(
230 const std::string
& error
) {
231 SetMessage(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR
,
232 base::UTF8ToUTF16(error
)));
235 void ChromePluginPlaceholder::OnCancelledDownloadingPlugin() {
237 l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED
, plugin_name_
));
239 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
241 void ChromePluginPlaceholder::PluginListChanged() {
242 if (!GetFrame() || !plugin())
244 blink::WebDocument document
= GetFrame()->top()->document();
245 if (document
.isNull())
248 ChromeViewHostMsg_GetPluginInfo_Output output
;
249 std::string
mime_type(GetPluginParams().mimeType
.utf8());
250 blink::WebString top_origin
= GetFrame()->top()->securityOrigin().toString();
251 render_frame()->Send(
252 new ChromeViewHostMsg_GetPluginInfo(routing_id(),
253 GURL(GetPluginParams().url
),
257 if (output
.status
== status_
)
259 blink::WebPlugin
* new_plugin
= ChromeContentRendererClient::CreatePlugin(
260 render_frame(), GetFrame(), GetPluginParams(), output
);
261 ReplacePlugin(new_plugin
);
263 PluginUMAReporter::GetInstance()->ReportPluginMissing(
264 GetPluginParams().mimeType
.utf8(), GURL(GetPluginParams().url
));
268 void ChromePluginPlaceholder::OnMenuAction(int request_id
, unsigned action
) {
269 DCHECK_EQ(context_menu_request_id_
, request_id
);
270 if (g_last_active_menu
!= this)
273 case chrome::MENU_COMMAND_PLUGIN_RUN
: {
274 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Menu"));
276 content::PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK
);
280 case chrome::MENU_COMMAND_PLUGIN_HIDE
: {
281 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Hide_Menu"));
290 void ChromePluginPlaceholder::OnMenuClosed(int request_id
) {
291 DCHECK_EQ(context_menu_request_id_
, request_id
);
292 context_menu_request_id_
= 0;
295 v8::Local
<v8::Value
> ChromePluginPlaceholder::GetV8Handle(
296 v8::Isolate
* isolate
) {
297 return gin::CreateHandle(isolate
, this).ToV8();
300 void ChromePluginPlaceholder::ShowContextMenu(
301 const blink::WebMouseEvent
& event
) {
302 if (context_menu_request_id_
)
303 return; // Don't allow nested context menu requests.
305 content::ContextMenuParams params
;
307 if (!title_
.empty()) {
308 content::MenuItem name_item
;
309 name_item
.label
= title_
;
310 params
.custom_items
.push_back(name_item
);
312 content::MenuItem separator_item
;
313 separator_item
.type
= content::MenuItem::SEPARATOR
;
314 params
.custom_items
.push_back(separator_item
);
317 if (!GetPluginInfo().path
.value().empty()) {
318 content::MenuItem run_item
;
319 run_item
.action
= chrome::MENU_COMMAND_PLUGIN_RUN
;
320 // Disable this menu item if the plugin is blocked by policy.
321 run_item
.enabled
= LoadingAllowed();
322 run_item
.label
= l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN
);
323 params
.custom_items
.push_back(run_item
);
326 content::MenuItem hide_item
;
327 hide_item
.action
= chrome::MENU_COMMAND_PLUGIN_HIDE
;
329 !GetFrame()->view()->mainFrame()->document().isPluginDocument();
330 hide_item
.label
= l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE
);
331 params
.custom_items
.push_back(hide_item
);
333 params
.x
= event
.windowX
;
334 params
.y
= event
.windowY
;
336 context_menu_request_id_
= render_frame()->ShowContextMenu(this, params
);
337 g_last_active_menu
= this;
340 blink::WebPlugin
* ChromePluginPlaceholder::CreatePlugin() {
341 scoped_ptr
<content::PluginInstanceThrottler
> throttler
;
342 // If the plugin has already been marked essential in its placeholder form,
343 // we shouldn't create a new throttler and start the process all over again.
344 if (power_saver_enabled()) {
345 throttler
= content::PluginInstanceThrottler::Create();
346 // PluginPreroller manages its own lifetime.
347 new PluginPreroller(render_frame(), GetFrame(), GetPluginParams(),
348 GetPluginInfo(), GetIdentifier(), title_
,
349 l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED
, title_
),
352 return render_frame()->CreatePlugin(GetFrame(), GetPluginInfo(),
353 GetPluginParams(), throttler
.Pass());
356 gin::ObjectTemplateBuilder
ChromePluginPlaceholder::GetObjectTemplateBuilder(
357 v8::Isolate
* isolate
) {
358 return gin::Wrappable
<ChromePluginPlaceholder
>::GetObjectTemplateBuilder(
360 .SetMethod
<void (ChromePluginPlaceholder::*)()>(
361 "hide", &ChromePluginPlaceholder::HideCallback
)
362 .SetMethod
<void (ChromePluginPlaceholder::*)()>(
363 "load", &ChromePluginPlaceholder::LoadCallback
)
364 .SetMethod
<void (ChromePluginPlaceholder::*)()>(
366 &ChromePluginPlaceholder::DidFinishLoadingCallback
)
367 .SetMethod("openAboutPlugins",
368 &ChromePluginPlaceholder::OpenAboutPluginsCallback
);