• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/plugins/plugin_observer.h"
6 
7 #include "base/auto_reset.h"
8 #include "base/bind.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/infobar_service.h"
16 #include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
17 #include "chrome/browser/lifetime/application_lifetime.h"
18 #include "chrome/browser/metrics/metrics_services_manager.h"
19 #include "chrome/browser/plugins/plugin_finder.h"
20 #include "chrome/browser/plugins/plugin_infobar_delegates.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
23 #include "chrome/common/render_messages.h"
24 #include "chrome/common/url_constants.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "components/infobars/core/confirm_infobar_delegate.h"
27 #include "components/infobars/core/infobar.h"
28 #include "content/public/browser/plugin_service.h"
29 #include "content/public/browser/render_frame_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_delegate.h"
33 #include "content/public/common/webplugininfo.h"
34 #include "grit/theme_resources.h"
35 #include "ui/base/l10n/l10n_util.h"
36 
37 #if defined(ENABLE_PLUGIN_INSTALLATION)
38 #if defined(OS_WIN)
39 #include "base/win/metro.h"
40 #endif
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)
45 
46 using content::OpenURLParams;
47 using content::PluginService;
48 using content::Referrer;
49 using content::WebContents;
50 
51 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver);
52 
53 namespace {
54 
55 #if defined(ENABLE_PLUGIN_INSTALLATION)
56 
57 // ConfirmInstallDialogDelegate ------------------------------------------------
58 
59 class ConfirmInstallDialogDelegate : public TabModalConfirmDialogDelegate,
60                                      public WeakPluginInstallerObserver {
61  public:
62   ConfirmInstallDialogDelegate(content::WebContents* web_contents,
63                                PluginInstaller* installer,
64                                scoped_ptr<PluginMetadata> plugin_metadata);
65 
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;
72 
73   // WeakPluginInstallerObserver methods:
74   virtual void DownloadStarted() OVERRIDE;
75   virtual void OnlyWeakObserversLeft() OVERRIDE;
76 
77  private:
78   content::WebContents* web_contents_;
79   scoped_ptr<PluginMetadata> plugin_metadata_;
80 };
81 
ConfirmInstallDialogDelegate(content::WebContents * web_contents,PluginInstaller * installer,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()) {
90 }
91 
GetTitle()92 base::string16 ConfirmInstallDialogDelegate::GetTitle() {
93   return l10n_util::GetStringFUTF16(
94       IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE, plugin_metadata_->name());
95 }
96 
GetDialogMessage()97 base::string16 ConfirmInstallDialogDelegate::GetDialogMessage() {
98   return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG,
99                                     plugin_metadata_->name());
100 }
101 
GetAcceptButtonTitle()102 base::string16 ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
103   return l10n_util::GetStringUTF16(
104       IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON);
105 }
106 
OnAccepted()107 void ConfirmInstallDialogDelegate::OnAccepted() {
108   installer()->StartInstalling(plugin_metadata_->plugin_url(), web_contents_);
109 }
110 
OnCanceled()111 void ConfirmInstallDialogDelegate::OnCanceled() {
112 }
113 
DownloadStarted()114 void ConfirmInstallDialogDelegate::DownloadStarted() {
115   Cancel();
116 }
117 
OnlyWeakObserversLeft()118 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
119   Cancel();
120 }
121 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
122 
123 // ReloadPluginInfoBarDelegate -------------------------------------------------
124 
125 class ReloadPluginInfoBarDelegate : public ConfirmInfoBarDelegate {
126  public:
127   static void Create(InfoBarService* infobar_service,
128                      content::NavigationController* controller,
129                      const base::string16& message);
130 
131  private:
132   ReloadPluginInfoBarDelegate(content::NavigationController* controller,
133                               const base::string16& message);
134   virtual ~ReloadPluginInfoBarDelegate();
135 
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;
142 
143   content::NavigationController* controller_;
144   base::string16 message_;
145 };
146 
147 // static
Create(InfoBarService * infobar_service,content::NavigationController * controller,const 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))));
155 }
156 
ReloadPluginInfoBarDelegate(content::NavigationController * controller,const base::string16 & message)157 ReloadPluginInfoBarDelegate::ReloadPluginInfoBarDelegate(
158     content::NavigationController* controller,
159     const base::string16& message)
160     : controller_(controller),
161       message_(message) {}
162 
~ReloadPluginInfoBarDelegate()163 ReloadPluginInfoBarDelegate::~ReloadPluginInfoBarDelegate(){ }
164 
GetIconID() const165 int ReloadPluginInfoBarDelegate::GetIconID() const {
166   return IDR_INFOBAR_PLUGIN_CRASHED;
167 }
168 
GetMessageText() const169 base::string16 ReloadPluginInfoBarDelegate::GetMessageText() const {
170   return message_;
171 }
172 
GetButtons() const173 int ReloadPluginInfoBarDelegate::GetButtons() const {
174   return BUTTON_OK;
175 }
176 
GetButtonLabel(InfoBarButton button) const177 base::string16 ReloadPluginInfoBarDelegate::GetButtonLabel(
178     InfoBarButton button) const {
179   DCHECK_EQ(BUTTON_OK, button);
180   return l10n_util::GetStringUTF16(IDS_RELOAD_PAGE_WITH_PLUGIN);
181 }
182 
Accept()183 bool ReloadPluginInfoBarDelegate::Accept() {
184   controller_->Reload(true);
185   return true;
186 }
187 
188 }  // namespace
189 
190 // PluginObserver -------------------------------------------------------------
191 
192 #if defined(ENABLE_PLUGIN_INSTALLATION)
193 class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
194  public:
PluginPlaceholderHost(PluginObserver * observer,int routing_id,base::string16 plugin_name,PluginInstaller * installer)195   PluginPlaceholderHost(PluginObserver* observer,
196                         int routing_id,
197                         base::string16 plugin_name,
198                         PluginInstaller* installer)
199       : PluginInstallerObserver(installer),
200         observer_(observer),
201         routing_id_(routing_id) {
202     DCHECK(installer);
203     switch (installer->state()) {
204       case PluginInstaller::INSTALLER_STATE_IDLE: {
205         observer->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_,
206                                                             plugin_name));
207         break;
208       }
209       case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
210         DownloadStarted();
211         break;
212       }
213     }
214   }
215 
216   // PluginInstallerObserver methods:
DownloadStarted()217   virtual void DownloadStarted() OVERRIDE {
218     observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
219   }
220 
DownloadError(const std::string & msg)221   virtual void DownloadError(const std::string& msg) OVERRIDE {
222     observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
223   }
224 
DownloadCancelled()225   virtual void DownloadCancelled() OVERRIDE {
226     observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
227   }
228 
DownloadFinished()229   virtual void DownloadFinished() OVERRIDE {
230     observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
231   }
232 
233  private:
234   // Weak pointer; owns us.
235   PluginObserver* observer_;
236 
237   int routing_id_;
238 };
239 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
240 
PluginObserver(content::WebContents * web_contents)241 PluginObserver::PluginObserver(content::WebContents* web_contents)
242     : content::WebContentsObserver(web_contents),
243       weak_ptr_factory_(this) {
244 }
245 
~PluginObserver()246 PluginObserver::~PluginObserver() {
247 #if defined(ENABLE_PLUGIN_INSTALLATION)
248   STLDeleteValues(&plugin_placeholders_);
249 #endif
250 }
251 
RenderFrameCreated(content::RenderFrameHost * render_frame_host)252 void PluginObserver::RenderFrameCreated(
253     content::RenderFrameHost* render_frame_host) {
254 #if defined(OS_WIN)
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.
261 
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.
272 
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));
280   }
281 #endif
282 }
283 
PluginCrashed(const base::FilePath & plugin_path,base::ProcessId plugin_pid)284 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
285                                    base::ProcessId plugin_pid) {
286   DCHECK(!plugin_path.value().empty());
287 
288   base::string16 plugin_name =
289       PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
290   base::string16 infobar_text;
291 #if defined(OS_WIN)
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;
301   if (open_result) {
302     is_running = base::GetTerminationStatus(plugin_handle, NULL) ==
303         base::TERMINATION_STATUS_STILL_RUNNING;
304     base::CloseProcessHandle(plugin_handle);
305   }
306 
307   if (is_running) {
308     infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
309                                               plugin_name);
310     UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
311   } else {
312     infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
313                                               plugin_name);
314     UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
315   }
316 #else
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,
322                                             plugin_name);
323   UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
324 #endif
325 
326   ReloadPluginInfoBarDelegate::Create(
327       InfoBarService::FromWebContents(web_contents()),
328       &web_contents()->GetController(),
329       infobar_text);
330 }
331 
OnMessageReceived(const IPC::Message & message,content::RenderFrameHost * render_frame_host)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,
341                         OnNPAPINotSupported)
342 #if defined(ENABLE_PLUGIN_INSTALLATION)
343     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin,
344                         OnFindMissingPlugin)
345 #endif
346 
347     IPC_MESSAGE_UNHANDLED(return false)
348   IPC_END_MESSAGE_MAP()
349 
350   return true;
351 }
352 
OnMessageReceived(const IPC::Message & message)353 bool PluginObserver::OnMessageReceived(const IPC::Message& message) {
354   IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
355 #if defined(ENABLE_PLUGIN_INSTALLATION)
356     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost,
357                         OnRemovePluginPlaceholderHost)
358 #endif
359     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins,
360                         OnOpenAboutPlugins)
361     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin,
362                         OnCouldNotLoadPlugin)
363 
364     IPC_MESSAGE_UNHANDLED(return false)
365   IPC_END_MESSAGE_MAP()
366 
367   return true;
368 }
369 
OnBlockedUnauthorizedPlugin(const base::string16 & name,const std::string & identifier)370 void PluginObserver::OnBlockedUnauthorizedPlugin(
371     const base::string16& name,
372     const std::string& identifier) {
373   UnauthorizedPluginInfoBarDelegate::Create(
374       InfoBarService::FromWebContents(web_contents()),
375       Profile::FromBrowserContext(web_contents()->GetBrowserContext())->
376           GetHostContentSettingsMap(),
377       name, identifier);
378 }
379 
OnBlockedOutdatedPlugin(int placeholder_id,const std::string & identifier)380 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id,
381                                              const std::string& identifier) {
382 #if defined(ENABLE_PLUGIN_INSTALLATION)
383   PluginFinder* finder = PluginFinder::GetInstance();
384   // Find plugin to update.
385   PluginInstaller* installer = NULL;
386   scoped_ptr<PluginMetadata> plugin;
387   if (finder->FindPluginWithIdentifier(identifier, &installer, &plugin)) {
388     plugin_placeholders_[placeholder_id] = new PluginPlaceholderHost(
389         this, placeholder_id, plugin->name(), installer);
390     OutdatedPluginInfoBarDelegate::Create(InfoBarService::FromWebContents(
391         web_contents()), installer, plugin.Pass());
392   } else {
393     NOTREACHED();
394   }
395 #else
396   // If we don't support third-party plug-in installation, we shouldn't have
397   // outdated plug-ins.
398   NOTREACHED();
399 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
400 }
401 
402 #if defined(ENABLE_PLUGIN_INSTALLATION)
OnFindMissingPlugin(int placeholder_id,const std::string & mime_type)403 void PluginObserver::OnFindMissingPlugin(int placeholder_id,
404                                          const std::string& mime_type) {
405   std::string lang = "en-US";  // Oh yes.
406   scoped_ptr<PluginMetadata> plugin_metadata;
407   PluginInstaller* installer = NULL;
408   bool found_plugin = PluginFinder::GetInstance()->FindPlugin(
409       mime_type, lang, &installer, &plugin_metadata);
410   if (!found_plugin) {
411     Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
412     return;
413   }
414   DCHECK(installer);
415   DCHECK(plugin_metadata.get());
416 
417   plugin_placeholders_[placeholder_id] =
418       new PluginPlaceholderHost(this, placeholder_id, plugin_metadata->name(),
419                                 installer);
420   PluginInstallerInfoBarDelegate::Create(
421       InfoBarService::FromWebContents(web_contents()), installer,
422       plugin_metadata.Pass(),
423       base::Bind(&PluginObserver::InstallMissingPlugin,
424                  weak_ptr_factory_.GetWeakPtr(), installer));
425 }
426 
InstallMissingPlugin(PluginInstaller * installer,const PluginMetadata * plugin_metadata)427 void PluginObserver::InstallMissingPlugin(
428     PluginInstaller* installer,
429     const PluginMetadata* plugin_metadata) {
430   if (plugin_metadata->url_for_display()) {
431     installer->OpenDownloadURL(plugin_metadata->plugin_url(), web_contents());
432   } else {
433     TabModalConfirmDialog::Create(
434         new ConfirmInstallDialogDelegate(
435             web_contents(), installer, plugin_metadata->Clone()),
436         web_contents());
437   }
438 }
439 
OnRemovePluginPlaceholderHost(int placeholder_id)440 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
441   std::map<int, PluginPlaceholderHost*>::iterator it =
442       plugin_placeholders_.find(placeholder_id);
443   if (it == plugin_placeholders_.end()) {
444     NOTREACHED();
445     return;
446   }
447   delete it->second;
448   plugin_placeholders_.erase(it);
449 }
450 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
451 
OnOpenAboutPlugins()452 void PluginObserver::OnOpenAboutPlugins() {
453   web_contents()->OpenURL(OpenURLParams(
454       GURL(chrome::kChromeUIPluginsURL),
455       content::Referrer(web_contents()->GetURL(),
456                         blink::WebReferrerPolicyDefault),
457       NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
458 }
459 
OnCouldNotLoadPlugin(const base::FilePath & plugin_path)460 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath& plugin_path) {
461   g_browser_process->GetMetricsServicesManager()->OnPluginLoadingError(
462       plugin_path);
463   base::string16 plugin_name =
464       PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
465   SimpleAlertInfoBarDelegate::Create(
466       InfoBarService::FromWebContents(web_contents()),
467       IDR_INFOBAR_PLUGIN_CRASHED,
468       l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
469                                  plugin_name),
470       true);
471 }
472 
OnNPAPINotSupported(const std::string & identifier)473 void PluginObserver::OnNPAPINotSupported(const std::string& identifier) {
474 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
475 #if !defined(USE_AURA)
476   DCHECK(base::win::IsMetroProcess());
477 #endif
478 
479   Profile* profile =
480       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
481   if (profile->IsOffTheRecord())
482     return;
483   HostContentSettingsMap* content_settings =
484       profile->GetHostContentSettingsMap();
485   if (content_settings->GetContentSetting(
486       web_contents()->GetURL(),
487       web_contents()->GetURL(),
488       CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP,
489       std::string()) == CONTENT_SETTING_BLOCK)
490     return;
491 
492   scoped_ptr<PluginMetadata> plugin;
493   bool ret = PluginFinder::GetInstance()->FindPluginWithIdentifier(
494       identifier, NULL, &plugin);
495   DCHECK(ret);
496 
497   PluginMetroModeInfoBarDelegate::Create(
498       InfoBarService::FromWebContents(web_contents()),
499       PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED, plugin->name());
500 #endif
501 }
502