- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / plugins / plugin_observer.cc
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/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"
35
36 #if defined(ENABLE_PLUGIN_INSTALLATION)
37 #if defined(OS_WIN)
38 #include "base/win/metro.h"
39 #endif
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)
44
45 using content::OpenURLParams;
46 using content::PluginService;
47 using content::Referrer;
48 using content::WebContents;
49
50 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver);
51
52 namespace {
53
54 #if defined(ENABLE_PLUGIN_INSTALLATION)
55
56 // ConfirmInstallDialogDelegate ------------------------------------------------
57
58 class ConfirmInstallDialogDelegate : public TabModalConfirmDialogDelegate,
59                                      public WeakPluginInstallerObserver {
60  public:
61   ConfirmInstallDialogDelegate(content::WebContents* web_contents,
62                                PluginInstaller* installer,
63                                scoped_ptr<PluginMetadata> plugin_metadata);
64
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;
71
72   // WeakPluginInstallerObserver methods:
73   virtual void DownloadStarted() OVERRIDE;
74   virtual void OnlyWeakObserversLeft() OVERRIDE;
75
76  private:
77   content::WebContents* web_contents_;
78   scoped_ptr<PluginMetadata> plugin_metadata_;
79 };
80
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()) {
89 }
90
91 string16 ConfirmInstallDialogDelegate::GetTitle() {
92   return l10n_util::GetStringFUTF16(
93       IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE, plugin_metadata_->name());
94 }
95
96 string16 ConfirmInstallDialogDelegate::GetMessage() {
97   return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG,
98                                     plugin_metadata_->name());
99 }
100
101 string16 ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
102   return l10n_util::GetStringUTF16(
103       IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON);
104 }
105
106 void ConfirmInstallDialogDelegate::OnAccepted() {
107   installer()->StartInstalling(plugin_metadata_->plugin_url(), web_contents_);
108 }
109
110 void ConfirmInstallDialogDelegate::OnCanceled() {
111 }
112
113 void ConfirmInstallDialogDelegate::DownloadStarted() {
114   Cancel();
115 }
116
117 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
118   Cancel();
119 }
120 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
121 }  // namespace
122
123 // PluginObserver -------------------------------------------------------------
124
125 #if defined(ENABLE_PLUGIN_INSTALLATION)
126 class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
127  public:
128   PluginPlaceholderHost(PluginObserver* observer,
129                         int routing_id,
130                         string16 plugin_name,
131                         PluginInstaller* installer)
132       : PluginInstallerObserver(installer),
133         observer_(observer),
134         routing_id_(routing_id) {
135     DCHECK(installer);
136     switch (installer->state()) {
137       case PluginInstaller::INSTALLER_STATE_IDLE: {
138         observer->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_,
139                                                             plugin_name));
140         break;
141       }
142       case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
143         DownloadStarted();
144         break;
145       }
146     }
147   }
148
149   // PluginInstallerObserver methods:
150   virtual void DownloadStarted() OVERRIDE {
151     observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
152   }
153
154   virtual void DownloadError(const std::string& msg) OVERRIDE {
155     observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
156   }
157
158   virtual void DownloadCancelled() OVERRIDE {
159     observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
160   }
161
162   virtual void DownloadFinished() OVERRIDE {
163     observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
164   }
165
166  private:
167   // Weak pointer; owns us.
168   PluginObserver* observer_;
169
170   int routing_id_;
171 };
172 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
173
174 PluginObserver::PluginObserver(content::WebContents* web_contents)
175     : content::WebContentsObserver(web_contents),
176       weak_ptr_factory_(this) {
177 }
178
179 PluginObserver::~PluginObserver() {
180 #if defined(ENABLE_PLUGIN_INSTALLATION)
181   STLDeleteValues(&plugin_placeholders_);
182 #endif
183 }
184
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));
200   }
201 #endif
202 }
203
204 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
205                                    base::ProcessId plugin_pid) {
206   DCHECK(!plugin_path.value().empty());
207
208   string16 plugin_name =
209       PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
210   string16 infobar_text;
211 #if defined(OS_WIN)
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;
221   if (open_result) {
222     is_running = base::GetTerminationStatus(plugin_handle, NULL) ==
223         base::TERMINATION_STATUS_STILL_RUNNING;
224     base::CloseProcessHandle(plugin_handle);
225   }
226
227   if (is_running) {
228     infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
229                                               plugin_name);
230     UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
231   } else {
232     infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
233                                               plugin_name);
234     UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
235   }
236 #else
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,
242                                             plugin_name);
243   UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
244 #endif
245
246   SimpleAlertInfoBarDelegate::Create(
247       InfoBarService::FromWebContents(web_contents()),
248       IDR_INFOBAR_PLUGIN_CRASHED, infobar_text, true);
249 }
250
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,
259                         OnFindMissingPlugin)
260     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost,
261                         OnRemovePluginPlaceholderHost)
262 #endif
263     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins,
264                         OnOpenAboutPlugins)
265     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin,
266                         OnCouldNotLoadPlugin)
267     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported,
268                         OnNPAPINotSupported)
269
270     IPC_MESSAGE_UNHANDLED(return false)
271   IPC_END_MESSAGE_MAP()
272
273   return true;
274 }
275
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(),
283       name, identifier);
284 }
285
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);
294   DCHECK(ret);
295
296   plugin_placeholders_[placeholder_id] =
297       new PluginPlaceholderHost(this, placeholder_id,
298                                 plugin->name(), installer);
299   OutdatedPluginInfoBarDelegate::Create(
300       InfoBarService::FromWebContents(web_contents()), installer,
301       plugin.Pass());
302 #else
303   // If we don't support third-party plug-in installation, we shouldn't have
304   // outdated plug-ins.
305   NOTREACHED();
306 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
307 }
308
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);
317   if (!found_plugin) {
318     Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
319     return;
320   }
321   DCHECK(installer);
322   DCHECK(plugin_metadata.get());
323
324   plugin_placeholders_[placeholder_id] =
325       new PluginPlaceholderHost(this, placeholder_id, plugin_metadata->name(),
326                                 installer);
327   PluginInstallerInfoBarDelegate::Create(
328       InfoBarService::FromWebContents(web_contents()), installer,
329       plugin_metadata.Pass(),
330       base::Bind(&PluginObserver::InstallMissingPlugin,
331                  weak_ptr_factory_.GetWeakPtr(), installer));
332 }
333
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());
339   } else {
340     TabModalConfirmDialog::Create(
341         new ConfirmInstallDialogDelegate(
342             web_contents(), installer, plugin_metadata->Clone()),
343         web_contents());
344   }
345 }
346
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()) {
351     NOTREACHED();
352     return;
353   }
354   delete it->second;
355   plugin_placeholders_.erase(it);
356 }
357 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
358
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));
365 }
366
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,
375                                  plugin_name),
376       true);
377 }
378
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());
383 #endif
384
385   Profile* profile =
386       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
387   if (profile->IsOffTheRecord())
388     return;
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)
396     return;
397
398   scoped_ptr<PluginMetadata> plugin;
399   bool ret = PluginFinder::GetInstance()->FindPluginWithIdentifier(
400       identifier, NULL, &plugin);
401   DCHECK(ret);
402
403   PluginMetroModeInfoBarDelegate::Create(
404       InfoBarService::FromWebContents(web_contents()),
405       PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED, plugin->name());
406 #endif
407 }