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.
5 #include "chrome/browser/plugins/plugin_observer.h"
7 #include "base/auto_reset.h"
9 #include "base/debug/crash_logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/stl_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/content_settings/host_content_settings_map.h"
15 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
16 #include "chrome/browser/infobars/infobar_service.h"
17 #include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
18 #include "chrome/browser/lifetime/application_lifetime.h"
19 #include "chrome/browser/metrics/metrics_service.h"
20 #include "chrome/browser/plugins/plugin_finder.h"
21 #include "chrome/browser/plugins/plugin_infobar_delegates.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
24 #include "chrome/common/render_messages.h"
25 #include "chrome/common/url_constants.h"
26 #include "components/infobars/core/infobar.h"
27 #include "content/public/browser/plugin_service.h"
28 #include "content/public/browser/render_frame_host.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/browser/web_contents_delegate.h"
32 #include "content/public/common/webplugininfo.h"
33 #include "grit/generated_resources.h"
34 #include "grit/theme_resources.h"
35 #include "ui/base/l10n/l10n_util.h"
37 #if defined(ENABLE_PLUGIN_INSTALLATION)
39 #include "base/win/metro.h"
41 #include "chrome/browser/plugins/plugin_installer.h"
42 #include "chrome/browser/plugins/plugin_installer_observer.h"
43 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
44 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
46 using content::OpenURLParams
;
47 using content::PluginService
;
48 using content::Referrer
;
49 using content::WebContents
;
51 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver
);
55 #if defined(ENABLE_PLUGIN_INSTALLATION)
57 // ConfirmInstallDialogDelegate ------------------------------------------------
59 class ConfirmInstallDialogDelegate
: public TabModalConfirmDialogDelegate
,
60 public WeakPluginInstallerObserver
{
62 ConfirmInstallDialogDelegate(content::WebContents
* web_contents
,
63 PluginInstaller
* installer
,
64 scoped_ptr
<PluginMetadata
> plugin_metadata
);
66 // TabModalConfirmDialogDelegate methods:
67 virtual base::string16
GetTitle() OVERRIDE
;
68 virtual base::string16
GetDialogMessage() OVERRIDE
;
69 virtual base::string16
GetAcceptButtonTitle() OVERRIDE
;
70 virtual void OnAccepted() OVERRIDE
;
71 virtual void OnCanceled() OVERRIDE
;
73 // WeakPluginInstallerObserver methods:
74 virtual void DownloadStarted() OVERRIDE
;
75 virtual void OnlyWeakObserversLeft() OVERRIDE
;
78 content::WebContents
* web_contents_
;
79 scoped_ptr
<PluginMetadata
> plugin_metadata_
;
82 ConfirmInstallDialogDelegate::ConfirmInstallDialogDelegate(
83 content::WebContents
* web_contents
,
84 PluginInstaller
* installer
,
85 scoped_ptr
<PluginMetadata
> plugin_metadata
)
86 : TabModalConfirmDialogDelegate(web_contents
),
87 WeakPluginInstallerObserver(installer
),
88 web_contents_(web_contents
),
89 plugin_metadata_(plugin_metadata
.Pass()) {
92 base::string16
ConfirmInstallDialogDelegate::GetTitle() {
93 return l10n_util::GetStringFUTF16(
94 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE
, plugin_metadata_
->name());
97 base::string16
ConfirmInstallDialogDelegate::GetDialogMessage() {
98 return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG
,
99 plugin_metadata_
->name());
102 base::string16
ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
103 return l10n_util::GetStringUTF16(
104 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON
);
107 void ConfirmInstallDialogDelegate::OnAccepted() {
108 installer()->StartInstalling(plugin_metadata_
->plugin_url(), web_contents_
);
111 void ConfirmInstallDialogDelegate::OnCanceled() {
114 void ConfirmInstallDialogDelegate::DownloadStarted() {
118 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
121 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
123 // ReloadPluginInfoBarDelegate -------------------------------------------------
125 class ReloadPluginInfoBarDelegate
: public ConfirmInfoBarDelegate
{
127 static void Create(InfoBarService
* infobar_service
,
128 content::NavigationController
* controller
,
129 const base::string16
& message
);
132 ReloadPluginInfoBarDelegate(content::NavigationController
* controller
,
133 const base::string16
& message
);
134 virtual ~ReloadPluginInfoBarDelegate();
136 // ConfirmInfobarDelegate:
137 virtual int GetIconID() const OVERRIDE
;
138 virtual base::string16
GetMessageText() const OVERRIDE
;
139 virtual int GetButtons() const OVERRIDE
;
140 virtual base::string16
GetButtonLabel(InfoBarButton button
) const OVERRIDE
;
141 virtual bool Accept() OVERRIDE
;
143 content::NavigationController
* controller_
;
144 base::string16 message_
;
148 void ReloadPluginInfoBarDelegate::Create(
149 InfoBarService
* infobar_service
,
150 content::NavigationController
* controller
,
151 const base::string16
& message
) {
152 infobar_service
->AddInfoBar(
153 ConfirmInfoBarDelegate::CreateInfoBar(scoped_ptr
<ConfirmInfoBarDelegate
>(
154 new ReloadPluginInfoBarDelegate(controller
, message
))));
157 ReloadPluginInfoBarDelegate::ReloadPluginInfoBarDelegate(
158 content::NavigationController
* controller
,
159 const base::string16
& message
)
160 : controller_(controller
),
163 ReloadPluginInfoBarDelegate::~ReloadPluginInfoBarDelegate(){ }
165 int ReloadPluginInfoBarDelegate::GetIconID() const {
166 return IDR_INFOBAR_PLUGIN_CRASHED
;
169 base::string16
ReloadPluginInfoBarDelegate::GetMessageText() const {
173 int ReloadPluginInfoBarDelegate::GetButtons() const {
177 base::string16
ReloadPluginInfoBarDelegate::GetButtonLabel(
178 InfoBarButton button
) const {
179 DCHECK_EQ(BUTTON_OK
, button
);
180 return l10n_util::GetStringUTF16(IDS_RELOAD_PAGE_WITH_PLUGIN
);
183 bool ReloadPluginInfoBarDelegate::Accept() {
184 controller_
->Reload(true);
190 // PluginObserver -------------------------------------------------------------
192 #if defined(ENABLE_PLUGIN_INSTALLATION)
193 class PluginObserver::PluginPlaceholderHost
: public PluginInstallerObserver
{
195 PluginPlaceholderHost(PluginObserver
* observer
,
197 base::string16 plugin_name
,
198 PluginInstaller
* installer
)
199 : PluginInstallerObserver(installer
),
201 routing_id_(routing_id
) {
203 switch (installer
->state()) {
204 case PluginInstaller::INSTALLER_STATE_IDLE
: {
205 observer
->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_
,
209 case PluginInstaller::INSTALLER_STATE_DOWNLOADING
: {
216 // PluginInstallerObserver methods:
217 virtual void DownloadStarted() OVERRIDE
{
218 observer_
->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_
));
221 virtual void DownloadError(const std::string
& msg
) OVERRIDE
{
222 observer_
->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_
, msg
));
225 virtual void DownloadCancelled() OVERRIDE
{
226 observer_
->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_
));
229 virtual void DownloadFinished() OVERRIDE
{
230 observer_
->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_
));
234 // Weak pointer; owns us.
235 PluginObserver
* observer_
;
239 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
241 PluginObserver::PluginObserver(content::WebContents
* web_contents
)
242 : content::WebContentsObserver(web_contents
),
243 weak_ptr_factory_(this) {
246 PluginObserver::~PluginObserver() {
247 #if defined(ENABLE_PLUGIN_INSTALLATION)
248 STLDeleteValues(&plugin_placeholders_
);
252 void PluginObserver::RenderFrameCreated(
253 content::RenderFrameHost
* render_frame_host
) {
255 // If the window belongs to the Ash desktop, before we navigate we need
256 // to tell the renderview that NPAPI plugins are not supported so it does
257 // not try to instantiate them. The final decision is actually done in
258 // the IO thread by PluginInfoMessageFilter of this proces,s but it's more
259 // complex to manage a map of Ash views in PluginInfoMessageFilter than
260 // just telling the renderer via IPC.
262 // TODO(shrikant): Implement solution which will help associate
263 // render_view_host/webcontents/view/window instance with host desktop.
264 // Refer to issue http://crbug.com/317940.
265 // When non-active tabs are restored they are not added in view/window parent
266 // hierarchy (chrome::CreateRestoredTab/CreateParams). Normally we traverse
267 // parent hierarchy to identify containing desktop (like in function
268 // chrome::GetHostDesktopTypeForNativeView).
269 // Possible issue with chrome::GetActiveDesktop, is that it's global
270 // state, which remembers last active desktop, which may break in scenarios
271 // where we have instances on both Ash and Native desktop.
273 // We will do both tests. Both have some factor of unreliability.
274 aura::Window
* window
= web_contents()->GetNativeView();
275 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH
||
276 chrome::GetHostDesktopTypeForNativeView(window
) ==
277 chrome::HOST_DESKTOP_TYPE_ASH
) {
278 int routing_id
= render_frame_host
->GetRoutingID();
279 render_frame_host
->Send(new ChromeViewMsg_NPAPINotSupported(routing_id
));
284 void PluginObserver::PluginCrashed(const base::FilePath
& plugin_path
,
285 base::ProcessId plugin_pid
) {
286 DCHECK(!plugin_path
.value().empty());
288 base::string16 plugin_name
=
289 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path
);
290 base::string16 infobar_text
;
292 // Find out whether the plugin process is still alive.
293 // Note: Although the chances are slim, it is possible that after the plugin
294 // process died, |plugin_pid| has been reused by a new process. The
295 // consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
296 // than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
297 base::ProcessHandle plugin_handle
= base::kNullProcessHandle
;
298 bool open_result
= base::OpenProcessHandleWithAccess(
299 plugin_pid
, PROCESS_QUERY_INFORMATION
| SYNCHRONIZE
, &plugin_handle
);
300 bool is_running
= false;
302 is_running
= base::GetTerminationStatus(plugin_handle
, NULL
) ==
303 base::TERMINATION_STATUS_STILL_RUNNING
;
304 base::CloseProcessHandle(plugin_handle
);
308 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT
,
310 UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
312 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT
,
314 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
317 // Calling the POSIX version of base::GetTerminationStatus() may affect other
318 // code which is interested in the process termination status. (Please see the
319 // comment of the function.) Therefore, a better way is needed to distinguish
320 // disconnections from crashes.
321 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT
,
323 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
326 ReloadPluginInfoBarDelegate::Create(
327 InfoBarService::FromWebContents(web_contents()),
328 &web_contents()->GetController(),
332 bool PluginObserver::OnMessageReceived(
333 const IPC::Message
& message
,
334 content::RenderFrameHost
* render_frame_host
) {
335 IPC_BEGIN_MESSAGE_MAP(PluginObserver
, message
)
336 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin
,
337 OnBlockedOutdatedPlugin
)
338 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedUnauthorizedPlugin
,
339 OnBlockedUnauthorizedPlugin
)
340 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported
,
343 IPC_MESSAGE_UNHANDLED(return false)
344 IPC_END_MESSAGE_MAP()
349 bool PluginObserver::OnMessageReceived(const IPC::Message
& message
) {
350 IPC_BEGIN_MESSAGE_MAP(PluginObserver
, message
)
351 #if defined(ENABLE_PLUGIN_INSTALLATION)
352 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin
,
354 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost
,
355 OnRemovePluginPlaceholderHost
)
357 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins
,
359 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin
,
360 OnCouldNotLoadPlugin
)
362 IPC_MESSAGE_UNHANDLED(return false)
363 IPC_END_MESSAGE_MAP()
368 void PluginObserver::OnBlockedUnauthorizedPlugin(
369 const base::string16
& name
,
370 const std::string
& identifier
) {
371 UnauthorizedPluginInfoBarDelegate::Create(
372 InfoBarService::FromWebContents(web_contents()),
373 Profile::FromBrowserContext(web_contents()->GetBrowserContext())->
374 GetHostContentSettingsMap(),
378 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id
,
379 const std::string
& identifier
) {
380 #if defined(ENABLE_PLUGIN_INSTALLATION)
381 PluginFinder
* finder
= PluginFinder::GetInstance();
382 // Find plugin to update.
383 PluginInstaller
* installer
= NULL
;
384 scoped_ptr
<PluginMetadata
> plugin
;
385 if (finder
->FindPluginWithIdentifier(identifier
, &installer
, &plugin
)) {
386 plugin_placeholders_
[placeholder_id
] = new PluginPlaceholderHost(
387 this, placeholder_id
, plugin
->name(), installer
);
388 OutdatedPluginInfoBarDelegate::Create(InfoBarService::FromWebContents(
389 web_contents()), installer
, plugin
.Pass());
394 // If we don't support third-party plug-in installation, we shouldn't have
395 // outdated plug-ins.
397 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
400 #if defined(ENABLE_PLUGIN_INSTALLATION)
401 void PluginObserver::OnFindMissingPlugin(int placeholder_id
,
402 const std::string
& mime_type
) {
403 std::string lang
= "en-US"; // Oh yes.
404 scoped_ptr
<PluginMetadata
> plugin_metadata
;
405 PluginInstaller
* installer
= NULL
;
406 bool found_plugin
= PluginFinder::GetInstance()->FindPlugin(
407 mime_type
, lang
, &installer
, &plugin_metadata
);
409 Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id
));
413 DCHECK(plugin_metadata
.get());
415 plugin_placeholders_
[placeholder_id
] =
416 new PluginPlaceholderHost(this, placeholder_id
, plugin_metadata
->name(),
418 PluginInstallerInfoBarDelegate::Create(
419 InfoBarService::FromWebContents(web_contents()), installer
,
420 plugin_metadata
.Pass(),
421 base::Bind(&PluginObserver::InstallMissingPlugin
,
422 weak_ptr_factory_
.GetWeakPtr(), installer
));
425 void PluginObserver::InstallMissingPlugin(
426 PluginInstaller
* installer
,
427 const PluginMetadata
* plugin_metadata
) {
428 if (plugin_metadata
->url_for_display()) {
429 installer
->OpenDownloadURL(plugin_metadata
->plugin_url(), web_contents());
431 TabModalConfirmDialog::Create(
432 new ConfirmInstallDialogDelegate(
433 web_contents(), installer
, plugin_metadata
->Clone()),
438 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id
) {
439 std::map
<int, PluginPlaceholderHost
*>::iterator it
=
440 plugin_placeholders_
.find(placeholder_id
);
441 if (it
== plugin_placeholders_
.end()) {
446 plugin_placeholders_
.erase(it
);
448 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
450 void PluginObserver::OnOpenAboutPlugins() {
451 web_contents()->OpenURL(OpenURLParams(
452 GURL(chrome::kChromeUIPluginsURL
),
453 content::Referrer(web_contents()->GetURL(),
454 blink::WebReferrerPolicyDefault
),
455 NEW_FOREGROUND_TAB
, content::PAGE_TRANSITION_AUTO_BOOKMARK
, false));
458 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath
& plugin_path
) {
459 g_browser_process
->metrics_service()->LogPluginLoadingError(plugin_path
);
460 base::string16 plugin_name
=
461 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path
);
462 SimpleAlertInfoBarDelegate::Create(
463 InfoBarService::FromWebContents(web_contents()),
464 IDR_INFOBAR_PLUGIN_CRASHED
,
465 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT
,
470 void PluginObserver::OnNPAPINotSupported(const std::string
& identifier
) {
471 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
472 #if !defined(USE_AURA)
473 DCHECK(base::win::IsMetroProcess());
477 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
478 if (profile
->IsOffTheRecord())
480 HostContentSettingsMap
* content_settings
=
481 profile
->GetHostContentSettingsMap();
482 if (content_settings
->GetContentSetting(
483 web_contents()->GetURL(),
484 web_contents()->GetURL(),
485 CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP
,
486 std::string()) == CONTENT_SETTING_BLOCK
)
489 scoped_ptr
<PluginMetadata
> plugin
;
490 bool ret
= PluginFinder::GetInstance()->FindPluginWithIdentifier(
491 identifier
, NULL
, &plugin
);
494 PluginMetroModeInfoBarDelegate::Create(
495 InfoBarService::FromWebContents(web_contents()),
496 PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED
, plugin
->name());