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/metrics/histogram.h"
10 #include "base/process_util.h"
11 #include "base/stl_util.h"
12 #include "base/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 "content/public/browser/plugin_service.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/browser/web_contents_delegate.h"
30 #include "grit/generated_resources.h"
31 #include "grit/theme_resources.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/resource/resource_bundle.h"
34 #include "webkit/plugins/webplugininfo.h"
36 #if defined(ENABLE_PLUGIN_INSTALLATION)
38 #include "base/win/metro.h"
40 #include "chrome/browser/plugins/plugin_installer.h"
41 #include "chrome/browser/plugins/plugin_installer_observer.h"
42 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
43 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
45 using content::OpenURLParams
;
46 using content::PluginService
;
47 using content::Referrer
;
48 using content::WebContents
;
50 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver
);
54 #if defined(ENABLE_PLUGIN_INSTALLATION)
56 // ConfirmInstallDialogDelegate ------------------------------------------------
58 class ConfirmInstallDialogDelegate
: public TabModalConfirmDialogDelegate
,
59 public WeakPluginInstallerObserver
{
61 ConfirmInstallDialogDelegate(content::WebContents
* web_contents
,
62 PluginInstaller
* installer
,
63 scoped_ptr
<PluginMetadata
> plugin_metadata
);
65 // TabModalConfirmDialogDelegate methods:
66 virtual string16
GetTitle() OVERRIDE
;
67 virtual string16
GetMessage() OVERRIDE
;
68 virtual string16
GetAcceptButtonTitle() OVERRIDE
;
69 virtual void OnAccepted() OVERRIDE
;
70 virtual void OnCanceled() OVERRIDE
;
72 // WeakPluginInstallerObserver methods:
73 virtual void DownloadStarted() OVERRIDE
;
74 virtual void OnlyWeakObserversLeft() OVERRIDE
;
77 content::WebContents
* web_contents_
;
78 scoped_ptr
<PluginMetadata
> plugin_metadata_
;
81 ConfirmInstallDialogDelegate::ConfirmInstallDialogDelegate(
82 content::WebContents
* web_contents
,
83 PluginInstaller
* installer
,
84 scoped_ptr
<PluginMetadata
> plugin_metadata
)
85 : TabModalConfirmDialogDelegate(web_contents
),
86 WeakPluginInstallerObserver(installer
),
87 web_contents_(web_contents
),
88 plugin_metadata_(plugin_metadata
.Pass()) {
91 string16
ConfirmInstallDialogDelegate::GetTitle() {
92 return l10n_util::GetStringFUTF16(
93 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE
, plugin_metadata_
->name());
96 string16
ConfirmInstallDialogDelegate::GetMessage() {
97 return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG
,
98 plugin_metadata_
->name());
101 string16
ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
102 return l10n_util::GetStringUTF16(
103 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON
);
106 void ConfirmInstallDialogDelegate::OnAccepted() {
107 installer()->StartInstalling(plugin_metadata_
->plugin_url(), web_contents_
);
110 void ConfirmInstallDialogDelegate::OnCanceled() {
113 void ConfirmInstallDialogDelegate::DownloadStarted() {
117 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
120 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
124 // PluginObserver -------------------------------------------------------------
126 #if defined(ENABLE_PLUGIN_INSTALLATION)
127 class PluginObserver::PluginPlaceholderHost
: public PluginInstallerObserver
{
129 PluginPlaceholderHost(PluginObserver
* observer
,
131 string16 plugin_name
,
132 PluginInstaller
* installer
)
133 : PluginInstallerObserver(installer
),
135 routing_id_(routing_id
) {
137 switch (installer
->state()) {
138 case PluginInstaller::INSTALLER_STATE_IDLE
: {
139 observer
->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_
,
143 case PluginInstaller::INSTALLER_STATE_DOWNLOADING
: {
150 // PluginInstallerObserver methods:
151 virtual void DownloadStarted() OVERRIDE
{
152 observer_
->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_
));
155 virtual void DownloadError(const std::string
& msg
) OVERRIDE
{
156 observer_
->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_
, msg
));
159 virtual void DownloadCancelled() OVERRIDE
{
160 observer_
->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_
));
163 virtual void DownloadFinished() OVERRIDE
{
164 observer_
->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_
));
168 // Weak pointer; owns us.
169 PluginObserver
* observer_
;
173 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
175 PluginObserver::PluginObserver(content::WebContents
* web_contents
)
176 : content::WebContentsObserver(web_contents
),
177 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
180 PluginObserver::~PluginObserver() {
181 #if defined(ENABLE_PLUGIN_INSTALLATION)
182 STLDeleteValues(&plugin_placeholders_
);
186 void PluginObserver::PluginCrashed(const base::FilePath
& plugin_path
,
187 base::ProcessId plugin_pid
) {
188 DCHECK(!plugin_path
.value().empty());
190 string16 plugin_name
=
191 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path
);
192 string16 infobar_text
;
194 // Find out whether the plugin process is still alive.
195 // Note: Although the chances are slim, it is possible that after the plugin
196 // process died, |plugin_pid| has been reused by a new process. The
197 // consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
198 // than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
199 base::ProcessHandle plugin_handle
= base::kNullProcessHandle
;
200 bool open_result
= base::OpenProcessHandleWithAccess(
201 plugin_pid
, PROCESS_QUERY_INFORMATION
| SYNCHRONIZE
, &plugin_handle
);
202 bool is_running
= false;
204 is_running
= base::GetTerminationStatus(plugin_handle
, NULL
) ==
205 base::TERMINATION_STATUS_STILL_RUNNING
;
206 base::CloseProcessHandle(plugin_handle
);
210 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT
,
212 UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
214 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT
,
216 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
219 // Calling the POSIX version of base::GetTerminationStatus() may affect other
220 // code which is interested in the process termination status. (Please see the
221 // comment of the function.) Therefore, a better way is needed to distinguish
222 // disconnections from crashes.
223 infobar_text
= l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT
,
225 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
228 gfx::Image
* icon
= &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
229 IDR_INFOBAR_PLUGIN_CRASHED
);
230 SimpleAlertInfoBarDelegate::Create(
231 InfoBarService::FromWebContents(web_contents()), icon
, infobar_text
,
235 bool PluginObserver::OnMessageReceived(const IPC::Message
& message
) {
236 IPC_BEGIN_MESSAGE_MAP(PluginObserver
, message
)
237 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin
,
238 OnBlockedOutdatedPlugin
)
239 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedUnauthorizedPlugin
,
240 OnBlockedUnauthorizedPlugin
)
241 #if defined(ENABLE_PLUGIN_INSTALLATION)
242 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin
,
244 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost
,
245 OnRemovePluginPlaceholderHost
)
247 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins
,
249 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin
,
250 OnCouldNotLoadPlugin
)
251 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported
,
254 IPC_MESSAGE_UNHANDLED(return false)
255 IPC_END_MESSAGE_MAP()
260 void PluginObserver::OnBlockedUnauthorizedPlugin(
261 const string16
& name
,
262 const std::string
& identifier
) {
263 UnauthorizedPluginInfoBarDelegate::Create(
264 InfoBarService::FromWebContents(web_contents()),
265 Profile::FromBrowserContext(web_contents()->GetBrowserContext())->
266 GetHostContentSettingsMap(),
270 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id
,
271 const std::string
& identifier
) {
272 #if defined(ENABLE_PLUGIN_INSTALLATION)
273 PluginFinder
* finder
= PluginFinder::GetInstance();
274 // Find plugin to update.
275 PluginInstaller
* installer
= NULL
;
276 scoped_ptr
<PluginMetadata
> plugin
;
277 if (!finder
->FindPluginWithIdentifier(identifier
, &installer
, &plugin
)) {
282 plugin_placeholders_
[placeholder_id
] =
283 new PluginPlaceholderHost(this, placeholder_id
,
284 plugin
->name(), installer
);
285 OutdatedPluginInfoBarDelegate::Create(
286 InfoBarService::FromWebContents(web_contents()), installer
,
289 // If we don't support third-party plug-in installation, we shouldn't have
290 // outdated plug-ins.
292 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
295 #if defined(ENABLE_PLUGIN_INSTALLATION)
296 void PluginObserver::OnFindMissingPlugin(int placeholder_id
,
297 const std::string
& mime_type
) {
298 std::string lang
= "en-US"; // Oh yes.
299 scoped_ptr
<PluginMetadata
> plugin_metadata
;
300 PluginInstaller
* installer
= NULL
;
301 bool found_plugin
= PluginFinder::GetInstance()->FindPlugin(
302 mime_type
, lang
, &installer
, &plugin_metadata
);
304 Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id
));
308 DCHECK(plugin_metadata
.get());
310 plugin_placeholders_
[placeholder_id
] =
311 new PluginPlaceholderHost(this, placeholder_id
,
312 plugin_metadata
->name(),
314 PluginInstallerInfoBarDelegate::Create(
315 InfoBarService::FromWebContents(web_contents()), installer
,
316 plugin_metadata
.Pass(),
317 base::Bind(&PluginObserver::InstallMissingPlugin
,
318 weak_ptr_factory_
.GetWeakPtr(), installer
));
321 void PluginObserver::InstallMissingPlugin(
322 PluginInstaller
* installer
,
323 const PluginMetadata
* plugin_metadata
) {
324 if (plugin_metadata
->url_for_display()) {
325 installer
->OpenDownloadURL(plugin_metadata
->plugin_url(), web_contents());
327 TabModalConfirmDialog::Create(
328 new ConfirmInstallDialogDelegate(
329 web_contents(), installer
, plugin_metadata
->Clone()),
334 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id
) {
335 std::map
<int, PluginPlaceholderHost
*>::iterator it
=
336 plugin_placeholders_
.find(placeholder_id
);
337 if (it
== plugin_placeholders_
.end()) {
342 plugin_placeholders_
.erase(it
);
344 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
346 void PluginObserver::OnOpenAboutPlugins() {
347 web_contents()->OpenURL(OpenURLParams(
348 GURL(chrome::kAboutPluginsURL
),
349 content::Referrer(web_contents()->GetURL(),
350 WebKit::WebReferrerPolicyDefault
),
351 NEW_FOREGROUND_TAB
, content::PAGE_TRANSITION_AUTO_BOOKMARK
, false));
354 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath
& plugin_path
) {
355 g_browser_process
->metrics_service()->LogPluginLoadingError(plugin_path
);
356 string16 plugin_name
=
357 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path
);
358 SimpleAlertInfoBarDelegate::Create(
359 InfoBarService::FromWebContents(web_contents()),
360 &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
361 IDR_INFOBAR_PLUGIN_CRASHED
),
362 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT
,
364 true /* auto_expire */);
367 void PluginObserver::OnNPAPINotSupported(const std::string
& identifier
) {
368 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
369 DCHECK(base::win::IsMetroProcess());
371 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
372 if (profile
->IsOffTheRecord())
374 HostContentSettingsMap
* content_settings
=
375 profile
->GetHostContentSettingsMap();
376 if (content_settings
->GetContentSetting(
377 web_contents()->GetURL(),
378 web_contents()->GetURL(),
379 CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP
,
380 std::string()) == CONTENT_SETTING_BLOCK
)
383 scoped_ptr
<PluginMetadata
> plugin
;
384 if (!PluginFinder::GetInstance()->FindPluginWithIdentifier(
385 identifier
, NULL
, &plugin
)) {
390 PluginMetroModeInfoBarDelegate::Create(
391 InfoBarService::FromWebContents(web_contents()),
392 PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED
, plugin
->name());