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 "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 "content/public/browser/web_contents_view.h"
31 #include "content/public/common/webplugininfo.h"
32 #include "grit/generated_resources.h"
33 #include "grit/theme_resources.h"
34 #include "ui/base/l10n/l10n_util.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)
123 // PluginObserver -------------------------------------------------------------
125 #if defined(ENABLE_PLUGIN_INSTALLATION)
126 class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
128 PluginPlaceholderHost(PluginObserver* observer,
130 string16 plugin_name,
131 PluginInstaller* installer)
132 : PluginInstallerObserver(installer),
134 routing_id_(routing_id) {
136 switch (installer->state()) {
137 case PluginInstaller::INSTALLER_STATE_IDLE: {
138 observer->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_,
142 case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
149 // PluginInstallerObserver methods:
150 virtual void DownloadStarted() OVERRIDE {
151 observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
154 virtual void DownloadError(const std::string& msg) OVERRIDE {
155 observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
158 virtual void DownloadCancelled() OVERRIDE {
159 observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
162 virtual void DownloadFinished() OVERRIDE {
163 observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
167 // Weak pointer; owns us.
168 PluginObserver* observer_;
172 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
174 PluginObserver::PluginObserver(content::WebContents* web_contents)
175 : content::WebContentsObserver(web_contents),
176 weak_ptr_factory_(this) {
179 PluginObserver::~PluginObserver() {
180 #if defined(ENABLE_PLUGIN_INSTALLATION)
181 STLDeleteValues(&plugin_placeholders_);
185 void PluginObserver::RenderViewCreated(
186 content::RenderViewHost* render_view_host) {
187 #if defined(USE_AURA) && defined(OS_WIN)
188 // If the window belongs to the Ash desktop, before we navigate we need
189 // to tell the renderview that NPAPI plugins are not supported so it does
190 // not try to instantiate them. The final decision is actually done in
191 // the IO thread by PluginInfoMessageFilter of this proces,s but it's more
192 // complex to manage a map of Ash views in PluginInfoMessageFilter than
193 // just telling the renderer via IPC.
194 content::WebContentsView* wcv = web_contents()->GetView();
195 aura::Window* window = wcv->GetNativeView();
196 if (chrome::GetHostDesktopTypeForNativeView(window) ==
197 chrome::HOST_DESKTOP_TYPE_ASH) {
198 int routing_id = render_view_host->GetRoutingID();
199 render_view_host->Send(new ChromeViewMsg_NPAPINotSupported(routing_id));
204 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
205 base::ProcessId plugin_pid) {
206 DCHECK(!plugin_path.value().empty());
208 string16 plugin_name =
209 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
210 string16 infobar_text;
212 // Find out whether the plugin process is still alive.
213 // Note: Although the chances are slim, it is possible that after the plugin
214 // process died, |plugin_pid| has been reused by a new process. The
215 // consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
216 // than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
217 base::ProcessHandle plugin_handle = base::kNullProcessHandle;
218 bool open_result = base::OpenProcessHandleWithAccess(
219 plugin_pid, PROCESS_QUERY_INFORMATION | SYNCHRONIZE, &plugin_handle);
220 bool is_running = false;
222 is_running = base::GetTerminationStatus(plugin_handle, NULL) ==
223 base::TERMINATION_STATUS_STILL_RUNNING;
224 base::CloseProcessHandle(plugin_handle);
228 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
230 UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
232 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
234 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
237 // Calling the POSIX version of base::GetTerminationStatus() may affect other
238 // code which is interested in the process termination status. (Please see the
239 // comment of the function.) Therefore, a better way is needed to distinguish
240 // disconnections from crashes.
241 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
243 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
246 SimpleAlertInfoBarDelegate::Create(
247 InfoBarService::FromWebContents(web_contents()),
248 IDR_INFOBAR_PLUGIN_CRASHED, infobar_text, true);
251 bool PluginObserver::OnMessageReceived(const IPC::Message& message) {
252 IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
253 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin,
254 OnBlockedOutdatedPlugin)
255 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedUnauthorizedPlugin,
256 OnBlockedUnauthorizedPlugin)
257 #if defined(ENABLE_PLUGIN_INSTALLATION)
258 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin,
260 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost,
261 OnRemovePluginPlaceholderHost)
263 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins,
265 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin,
266 OnCouldNotLoadPlugin)
267 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported,
270 IPC_MESSAGE_UNHANDLED(return false)
271 IPC_END_MESSAGE_MAP()
276 void PluginObserver::OnBlockedUnauthorizedPlugin(
277 const string16& name,
278 const std::string& identifier) {
279 UnauthorizedPluginInfoBarDelegate::Create(
280 InfoBarService::FromWebContents(web_contents()),
281 Profile::FromBrowserContext(web_contents()->GetBrowserContext())->
282 GetHostContentSettingsMap(),
286 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id,
287 const std::string& identifier) {
288 #if defined(ENABLE_PLUGIN_INSTALLATION)
289 PluginFinder* finder = PluginFinder::GetInstance();
290 // Find plugin to update.
291 PluginInstaller* installer = NULL;
292 scoped_ptr<PluginMetadata> plugin;
293 bool ret = finder->FindPluginWithIdentifier(identifier, &installer, &plugin);
296 plugin_placeholders_[placeholder_id] =
297 new PluginPlaceholderHost(this, placeholder_id,
298 plugin->name(), installer);
299 OutdatedPluginInfoBarDelegate::Create(
300 InfoBarService::FromWebContents(web_contents()), installer,
303 // If we don't support third-party plug-in installation, we shouldn't have
304 // outdated plug-ins.
306 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
309 #if defined(ENABLE_PLUGIN_INSTALLATION)
310 void PluginObserver::OnFindMissingPlugin(int placeholder_id,
311 const std::string& mime_type) {
312 std::string lang = "en-US"; // Oh yes.
313 scoped_ptr<PluginMetadata> plugin_metadata;
314 PluginInstaller* installer = NULL;
315 bool found_plugin = PluginFinder::GetInstance()->FindPlugin(
316 mime_type, lang, &installer, &plugin_metadata);
318 Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
322 DCHECK(plugin_metadata.get());
324 plugin_placeholders_[placeholder_id] =
325 new PluginPlaceholderHost(this, placeholder_id, plugin_metadata->name(),
327 PluginInstallerInfoBarDelegate::Create(
328 InfoBarService::FromWebContents(web_contents()), installer,
329 plugin_metadata.Pass(),
330 base::Bind(&PluginObserver::InstallMissingPlugin,
331 weak_ptr_factory_.GetWeakPtr(), installer));
334 void PluginObserver::InstallMissingPlugin(
335 PluginInstaller* installer,
336 const PluginMetadata* plugin_metadata) {
337 if (plugin_metadata->url_for_display()) {
338 installer->OpenDownloadURL(plugin_metadata->plugin_url(), web_contents());
340 TabModalConfirmDialog::Create(
341 new ConfirmInstallDialogDelegate(
342 web_contents(), installer, plugin_metadata->Clone()),
347 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
348 std::map<int, PluginPlaceholderHost*>::iterator it =
349 plugin_placeholders_.find(placeholder_id);
350 if (it == plugin_placeholders_.end()) {
355 plugin_placeholders_.erase(it);
357 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
359 void PluginObserver::OnOpenAboutPlugins() {
360 web_contents()->OpenURL(OpenURLParams(
361 GURL(chrome::kAboutPluginsURL),
362 content::Referrer(web_contents()->GetURL(),
363 WebKit::WebReferrerPolicyDefault),
364 NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
367 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath& plugin_path) {
368 g_browser_process->metrics_service()->LogPluginLoadingError(plugin_path);
369 string16 plugin_name =
370 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
371 SimpleAlertInfoBarDelegate::Create(
372 InfoBarService::FromWebContents(web_contents()),
373 IDR_INFOBAR_PLUGIN_CRASHED,
374 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
379 void PluginObserver::OnNPAPINotSupported(const std::string& identifier) {
380 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
381 #if !defined(USE_AURA)
382 DCHECK(base::win::IsMetroProcess());
386 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
387 if (profile->IsOffTheRecord())
389 HostContentSettingsMap* content_settings =
390 profile->GetHostContentSettingsMap();
391 if (content_settings->GetContentSetting(
392 web_contents()->GetURL(),
393 web_contents()->GetURL(),
394 CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP,
395 std::string()) == CONTENT_SETTING_BLOCK)
398 scoped_ptr<PluginMetadata> plugin;
399 bool ret = PluginFinder::GetInstance()->FindPluginWithIdentifier(
400 identifier, NULL, &plugin);
403 PluginMetroModeInfoBarDelegate::Create(
404 InfoBarService::FromWebContents(web_contents()),
405 PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED, plugin->name());