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/infobars/infobar_service.h"
15 #include "chrome/browser/lifetime/application_lifetime.h"
16 #include "chrome/browser/metrics/metrics_services_manager.h"
17 #include "chrome/browser/plugins/plugin_finder.h"
18 #include "chrome/browser/plugins/plugin_infobar_delegates.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
21 #include "chrome/common/render_messages.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "components/content_settings/content/common/content_settings_messages.h"
25 #include "components/content_settings/core/browser/host_content_settings_map.h"
26 #include "components/infobars/core/confirm_infobar_delegate.h"
27 #include "components/infobars/core/infobar.h"
28 #include "components/infobars/core/simple_alert_infobar_delegate.h"
29 #include "content/public/browser/plugin_service.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_contents_delegate.h"
34 #include "content/public/common/webplugininfo.h"
35 #include "grit/theme_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
38 #if defined(ENABLE_PLUGIN_INSTALLATION)
40 #include "base/win/metro.h"
42 #include "chrome/browser/plugins/plugin_installer.h"
43 #include "chrome/browser/plugins/plugin_installer_observer.h"
44 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
45 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
47 using content::OpenURLParams
;
48 using content::PluginService
;
49 using content::Referrer
;
50 using content::WebContents
;
52 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver
);
56 #if defined(ENABLE_PLUGIN_INSTALLATION)
58 // ConfirmInstallDialogDelegate ------------------------------------------------
60 class ConfirmInstallDialogDelegate
: public TabModalConfirmDialogDelegate
,
61 public WeakPluginInstallerObserver
{
63 ConfirmInstallDialogDelegate(content::WebContents
* web_contents
,
64 PluginInstaller
* installer
,
65 scoped_ptr
<PluginMetadata
> plugin_metadata
);
67 // TabModalConfirmDialogDelegate methods:
68 base::string16
GetTitle() override
;
69 base::string16
GetDialogMessage() override
;
70 base::string16
GetAcceptButtonTitle() override
;
71 void OnAccepted() override
;
72 void OnCanceled() override
;
74 // WeakPluginInstallerObserver methods:
75 void DownloadStarted() override
;
76 void OnlyWeakObserversLeft() override
;
79 content::WebContents
* web_contents_
;
80 scoped_ptr
<PluginMetadata
> plugin_metadata_
;
83 ConfirmInstallDialogDelegate::ConfirmInstallDialogDelegate(
84 content::WebContents
* web_contents
,
85 PluginInstaller
* installer
,
86 scoped_ptr
<PluginMetadata
> plugin_metadata
)
87 : TabModalConfirmDialogDelegate(web_contents
),
88 WeakPluginInstallerObserver(installer
),
89 web_contents_(web_contents
),
90 plugin_metadata_(plugin_metadata
.Pass()) {
93 base::string16
ConfirmInstallDialogDelegate::GetTitle() {
94 return l10n_util::GetStringFUTF16(
95 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE
, plugin_metadata_
->name());
98 base::string16
ConfirmInstallDialogDelegate::GetDialogMessage() {
99 return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG
,
100 plugin_metadata_
->name());
103 base::string16
ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
104 return l10n_util::GetStringUTF16(
105 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON
);
108 void ConfirmInstallDialogDelegate::OnAccepted() {
109 installer()->StartInstalling(plugin_metadata_
->plugin_url(), web_contents_
);
112 void ConfirmInstallDialogDelegate::OnCanceled() {
115 void ConfirmInstallDialogDelegate::DownloadStarted() {
119 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
122 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
124 // ReloadPluginInfoBarDelegate -------------------------------------------------
126 class ReloadPluginInfoBarDelegate
: public ConfirmInfoBarDelegate
{
128 static void Create(InfoBarService
* infobar_service
,
129 content::NavigationController
* controller
,
130 const base::string16
& message
);
133 ReloadPluginInfoBarDelegate(content::NavigationController
* controller
,
134 const base::string16
& message
);
135 ~ReloadPluginInfoBarDelegate() override
;
137 // ConfirmInfobarDelegate:
138 int GetIconID() const override
;
139 base::string16
GetMessageText() const override
;
140 int GetButtons() const override
;
141 base::string16
GetButtonLabel(InfoBarButton button
) const override
;
142 bool Accept() override
;
144 content::NavigationController
* controller_
;
145 base::string16 message_
;
149 void ReloadPluginInfoBarDelegate::Create(
150 InfoBarService
* infobar_service
,
151 content::NavigationController
* controller
,
152 const base::string16
& message
) {
153 infobar_service
->AddInfoBar(
154 infobar_service
->CreateConfirmInfoBar(scoped_ptr
<ConfirmInfoBarDelegate
>(
155 new ReloadPluginInfoBarDelegate(controller
, message
))));
158 ReloadPluginInfoBarDelegate::ReloadPluginInfoBarDelegate(
159 content::NavigationController
* controller
,
160 const base::string16
& message
)
161 : controller_(controller
),
164 ReloadPluginInfoBarDelegate::~ReloadPluginInfoBarDelegate(){ }
166 int ReloadPluginInfoBarDelegate::GetIconID() const {
167 return IDR_INFOBAR_PLUGIN_CRASHED
;
170 base::string16
ReloadPluginInfoBarDelegate::GetMessageText() const {
174 int ReloadPluginInfoBarDelegate::GetButtons() const {
178 base::string16
ReloadPluginInfoBarDelegate::GetButtonLabel(
179 InfoBarButton button
) const {
180 DCHECK_EQ(BUTTON_OK
, button
);
181 return l10n_util::GetStringUTF16(IDS_RELOAD_PAGE_WITH_PLUGIN
);
184 bool ReloadPluginInfoBarDelegate::Accept() {
185 controller_
->Reload(true);
191 // PluginObserver -------------------------------------------------------------
193 #if defined(ENABLE_PLUGIN_INSTALLATION)
194 class PluginObserver::PluginPlaceholderHost
: public PluginInstallerObserver
{
196 PluginPlaceholderHost(PluginObserver
* observer
,
198 base::string16 plugin_name
,
199 PluginInstaller
* installer
)
200 : PluginInstallerObserver(installer
),
202 routing_id_(routing_id
) {
204 switch (installer
->state()) {
205 case PluginInstaller::INSTALLER_STATE_IDLE
: {
206 observer
->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_
,
210 case PluginInstaller::INSTALLER_STATE_DOWNLOADING
: {
217 // PluginInstallerObserver methods:
218 void DownloadStarted() override
{
219 observer_
->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_
));
222 void DownloadError(const std::string
& msg
) override
{
223 observer_
->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_
, msg
));
226 void DownloadCancelled() override
{
227 observer_
->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_
));
230 void DownloadFinished() override
{
231 observer_
->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_
));
235 // Weak pointer; owns us.
236 PluginObserver
* observer_
;
240 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
242 PluginObserver::PluginObserver(content::WebContents
* web_contents
)
243 : content::WebContentsObserver(web_contents
),
244 weak_ptr_factory_(this) {
247 PluginObserver::~PluginObserver() {
248 #if defined(ENABLE_PLUGIN_INSTALLATION)
249 STLDeleteValues(&plugin_placeholders_
);
253 void PluginObserver::RenderFrameCreated(
254 content::RenderFrameHost
* render_frame_host
) {
256 // If the window belongs to the Ash desktop, before we navigate we need
257 // to tell the renderview that NPAPI plugins are not supported so it does
258 // not try to instantiate them. The final decision is actually done in
259 // the IO thread by PluginInfoMessageFilter of this proces,s but it's more
260 // complex to manage a map of Ash views in PluginInfoMessageFilter than
261 // just telling the renderer via IPC.
263 // TODO(shrikant): Implement solution which will help associate
264 // render_view_host/webcontents/view/window instance with host desktop.
265 // Refer to issue http://crbug.com/317940.
266 // When non-active tabs are restored they are not added in view/window parent
267 // hierarchy (chrome::CreateRestoredTab/CreateParams). Normally we traverse
268 // parent hierarchy to identify containing desktop (like in function
269 // chrome::GetHostDesktopTypeForNativeView).
270 // Possible issue with chrome::GetActiveDesktop, is that it's global
271 // state, which remembers last active desktop, which may break in scenarios
272 // where we have instances on both Ash and Native desktop.
274 // We will do both tests. Both have some factor of unreliability.
275 aura::Window
* window
= web_contents()->GetNativeView();
276 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH
||
277 chrome::GetHostDesktopTypeForNativeView(window
) ==
278 chrome::HOST_DESKTOP_TYPE_ASH
) {
279 int routing_id
= render_frame_host
->GetRoutingID();
280 render_frame_host
->Send(new ChromeViewMsg_NPAPINotSupported(routing_id
));
285 void PluginObserver::PluginCrashed(const base::FilePath
& plugin_path
,
286 base::ProcessId plugin_pid
) {
287 DCHECK(!plugin_path
.value().empty());
289 base::string16 plugin_name
=
290 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path
);
291 base::string16 infobar_text
;
293 // Find out whether the plugin process is still alive.
294 // Note: Although the chances are slim, it is possible that after the plugin
295 // process died, |plugin_pid| has been reused by a new process. The
296 // consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
297 // than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
298 base::Process plugin_process
=
299 base::Process::OpenWithAccess(plugin_pid
,
300 PROCESS_QUERY_INFORMATION
| SYNCHRONIZE
);
301 bool is_running
= false;
302 if (plugin_process
.IsValid()) {
304 base::GetTerminationStatus(plugin_process
.Handle(), NULL
) ==
305 base::TERMINATION_STATUS_STILL_RUNNING
;
306 plugin_process
.Close();
310 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT
,
312 UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
314 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT
,
316 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
319 // Calling the POSIX version of base::GetTerminationStatus() may affect other
320 // code which is interested in the process termination status. (Please see the
321 // comment of the function.) Therefore, a better way is needed to distinguish
322 // disconnections from crashes.
323 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT
,
325 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
328 ReloadPluginInfoBarDelegate::Create(
329 InfoBarService::FromWebContents(web_contents()),
330 &web_contents()->GetController(),
334 bool PluginObserver::OnMessageReceived(
335 const IPC::Message
& message
,
336 content::RenderFrameHost
* render_frame_host
) {
337 IPC_BEGIN_MESSAGE_MAP(PluginObserver
, message
)
338 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin
,
339 OnBlockedOutdatedPlugin
)
340 #if defined(ENABLE_PLUGIN_INSTALLATION)
341 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost
,
342 OnRemovePluginPlaceholderHost
)
344 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins
,
346 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin
,
347 OnCouldNotLoadPlugin
)
348 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported
,
351 IPC_MESSAGE_UNHANDLED(return false)
352 IPC_END_MESSAGE_MAP()
357 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id
,
358 const std::string
& identifier
) {
359 #if defined(ENABLE_PLUGIN_INSTALLATION)
360 PluginFinder
* finder
= PluginFinder::GetInstance();
361 // Find plugin to update.
362 PluginInstaller
* installer
= NULL
;
363 scoped_ptr
<PluginMetadata
> plugin
;
364 if (finder
->FindPluginWithIdentifier(identifier
, &installer
, &plugin
)) {
365 plugin_placeholders_
[placeholder_id
] = new PluginPlaceholderHost(
366 this, placeholder_id
, plugin
->name(), installer
);
367 OutdatedPluginInfoBarDelegate::Create(InfoBarService::FromWebContents(
368 web_contents()), installer
, plugin
.Pass());
373 // If we don't support third-party plugin installation, we shouldn't have
376 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
379 #if defined(ENABLE_PLUGIN_INSTALLATION)
380 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id
) {
381 std::map
<int, PluginPlaceholderHost
*>::iterator it
=
382 plugin_placeholders_
.find(placeholder_id
);
383 if (it
== plugin_placeholders_
.end()) {
388 plugin_placeholders_
.erase(it
);
390 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
392 void PluginObserver::OnOpenAboutPlugins() {
393 web_contents()->OpenURL(OpenURLParams(
394 GURL(chrome::kChromeUIPluginsURL
),
395 content::Referrer::SanitizeForRequest(
396 GURL(chrome::kChromeUIPluginsURL
),
397 content::Referrer(web_contents()->GetURL(),
398 blink::WebReferrerPolicyDefault
)),
399 NEW_FOREGROUND_TAB
, ui::PAGE_TRANSITION_AUTO_BOOKMARK
, false));
402 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath
& plugin_path
) {
403 g_browser_process
->GetMetricsServicesManager()->OnPluginLoadingError(
405 base::string16 plugin_name
=
406 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path
);
407 SimpleAlertInfoBarDelegate::Create(
408 InfoBarService::FromWebContents(web_contents()),
409 IDR_INFOBAR_PLUGIN_CRASHED
,
410 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT
,
415 void PluginObserver::OnNPAPINotSupported(const std::string
& identifier
) {
416 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
417 #if !defined(USE_AURA)
418 DCHECK(base::win::IsMetroProcess());
422 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
423 if (profile
->IsOffTheRecord())
425 HostContentSettingsMap
* content_settings
=
426 profile
->GetHostContentSettingsMap();
427 if (content_settings
->GetContentSetting(
428 web_contents()->GetURL(),
429 web_contents()->GetURL(),
430 CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP
,
431 std::string()) == CONTENT_SETTING_BLOCK
)
434 scoped_ptr
<PluginMetadata
> plugin
;
435 bool ret
= PluginFinder::GetInstance()->FindPluginWithIdentifier(
436 identifier
, NULL
, &plugin
);
439 PluginMetroModeInfoBarDelegate::Create(
440 InfoBarService::FromWebContents(web_contents()),
441 PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED
, plugin
->name());