Upstream version 5.34.104.0
[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_frame_host.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "content/public/browser/web_contents_view.h"
32 #include "content/public/common/webplugininfo.h"
33 #include "grit/generated_resources.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
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
92 base::string16 ConfirmInstallDialogDelegate::GetTitle() {
93   return l10n_util::GetStringFUTF16(
94       IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE, plugin_metadata_->name());
95 }
96
97 base::string16 ConfirmInstallDialogDelegate::GetDialogMessage() {
98   return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG,
99                                     plugin_metadata_->name());
100 }
101
102 base::string16 ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
103   return l10n_util::GetStringUTF16(
104       IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON);
105 }
106
107 void ConfirmInstallDialogDelegate::OnAccepted() {
108   installer()->StartInstalling(plugin_metadata_->plugin_url(), web_contents_);
109 }
110
111 void ConfirmInstallDialogDelegate::OnCanceled() {
112 }
113
114 void ConfirmInstallDialogDelegate::DownloadStarted() {
115   Cancel();
116 }
117
118 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
119   Cancel();
120 }
121 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
122 }  // namespace
123
124 // PluginObserver -------------------------------------------------------------
125
126 #if defined(ENABLE_PLUGIN_INSTALLATION)
127 class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
128  public:
129   PluginPlaceholderHost(PluginObserver* observer,
130                         int routing_id,
131                         base::string16 plugin_name,
132                         PluginInstaller* installer)
133       : PluginInstallerObserver(installer),
134         observer_(observer),
135         routing_id_(routing_id) {
136     DCHECK(installer);
137     switch (installer->state()) {
138       case PluginInstaller::INSTALLER_STATE_IDLE: {
139         observer->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_,
140                                                             plugin_name));
141         break;
142       }
143       case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
144         DownloadStarted();
145         break;
146       }
147     }
148   }
149
150   // PluginInstallerObserver methods:
151   virtual void DownloadStarted() OVERRIDE {
152     observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
153   }
154
155   virtual void DownloadError(const std::string& msg) OVERRIDE {
156     observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
157   }
158
159   virtual void DownloadCancelled() OVERRIDE {
160     observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
161   }
162
163   virtual void DownloadFinished() OVERRIDE {
164     observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
165   }
166
167  private:
168   // Weak pointer; owns us.
169   PluginObserver* observer_;
170
171   int routing_id_;
172 };
173 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
174
175 PluginObserver::PluginObserver(content::WebContents* web_contents)
176     : content::WebContentsObserver(web_contents),
177       weak_ptr_factory_(this) {
178 }
179
180 PluginObserver::~PluginObserver() {
181 #if defined(ENABLE_PLUGIN_INSTALLATION)
182   STLDeleteValues(&plugin_placeholders_);
183 #endif
184 }
185
186 void PluginObserver::RenderFrameCreated(
187     content::RenderFrameHost* render_frame_host) {
188 #if defined(OS_WIN)
189   // If the window belongs to the Ash desktop, before we navigate we need
190   // to tell the renderview that NPAPI plugins are not supported so it does
191   // not try to instantiate them. The final decision is actually done in
192   // the IO thread by PluginInfoMessageFilter of this proces,s but it's more
193   // complex to manage a map of Ash views in PluginInfoMessageFilter than
194   // just telling the renderer via IPC.
195
196   // TODO(shrikant): Implement solution which will help associate
197   // render_view_host/webcontents/view/window instance with host desktop.
198   // Refer to issue http://crbug.com/317940.
199   // When non-active tabs are restored they are not added in view/window parent
200   // hierarchy (chrome::CreateRestoredTab/CreateParams). Normally we traverse
201   // parent hierarchy to identify containing desktop (like in function
202   // chrome::GetHostDesktopTypeForNativeView).
203   // Possible issue with chrome::GetActiveDesktop, is that it's global
204   // state, which remembers last active desktop, which may break in scenarios
205   // where we have instances on both Ash and Native desktop.
206
207   // We will do both tests. Both have some factor of unreliability.
208   aura::Window* window = web_contents()->GetView()->GetNativeView();
209   if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH ||
210       chrome::GetHostDesktopTypeForNativeView(window) ==
211       chrome::HOST_DESKTOP_TYPE_ASH) {
212     int routing_id = render_frame_host->GetRoutingID();
213     render_frame_host->Send(new ChromeViewMsg_NPAPINotSupported(routing_id));
214   }
215 #endif
216 }
217
218 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
219                                    base::ProcessId plugin_pid) {
220   DCHECK(!plugin_path.value().empty());
221
222   base::string16 plugin_name =
223       PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
224   base::string16 infobar_text;
225 #if defined(OS_WIN)
226   // Find out whether the plugin process is still alive.
227   // Note: Although the chances are slim, it is possible that after the plugin
228   // process died, |plugin_pid| has been reused by a new process. The
229   // consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
230   // than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
231   base::ProcessHandle plugin_handle = base::kNullProcessHandle;
232   bool open_result = base::OpenProcessHandleWithAccess(
233       plugin_pid, PROCESS_QUERY_INFORMATION | SYNCHRONIZE, &plugin_handle);
234   bool is_running = false;
235   if (open_result) {
236     is_running = base::GetTerminationStatus(plugin_handle, NULL) ==
237         base::TERMINATION_STATUS_STILL_RUNNING;
238     base::CloseProcessHandle(plugin_handle);
239   }
240
241   if (is_running) {
242     infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
243                                               plugin_name);
244     UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
245   } else {
246     infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
247                                               plugin_name);
248     UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
249   }
250 #else
251   // Calling the POSIX version of base::GetTerminationStatus() may affect other
252   // code which is interested in the process termination status. (Please see the
253   // comment of the function.) Therefore, a better way is needed to distinguish
254   // disconnections from crashes.
255   infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
256                                             plugin_name);
257   UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
258 #endif
259
260   SimpleAlertInfoBarDelegate::Create(
261       InfoBarService::FromWebContents(web_contents()),
262       IDR_INFOBAR_PLUGIN_CRASHED, infobar_text, true);
263 }
264
265 bool PluginObserver::OnMessageReceived(const IPC::Message& message) {
266   IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
267     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin,
268                         OnBlockedOutdatedPlugin)
269     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedUnauthorizedPlugin,
270                         OnBlockedUnauthorizedPlugin)
271 #if defined(ENABLE_PLUGIN_INSTALLATION)
272     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin,
273                         OnFindMissingPlugin)
274     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost,
275                         OnRemovePluginPlaceholderHost)
276 #endif
277     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins,
278                         OnOpenAboutPlugins)
279     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin,
280                         OnCouldNotLoadPlugin)
281     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported,
282                         OnNPAPINotSupported)
283
284     IPC_MESSAGE_UNHANDLED(return false)
285   IPC_END_MESSAGE_MAP()
286
287   return true;
288 }
289
290 void PluginObserver::OnBlockedUnauthorizedPlugin(
291     const base::string16& name,
292     const std::string& identifier) {
293   UnauthorizedPluginInfoBarDelegate::Create(
294       InfoBarService::FromWebContents(web_contents()),
295       Profile::FromBrowserContext(web_contents()->GetBrowserContext())->
296           GetHostContentSettingsMap(),
297       name, identifier);
298 }
299
300 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id,
301                                              const std::string& identifier) {
302 #if defined(ENABLE_PLUGIN_INSTALLATION)
303   PluginFinder* finder = PluginFinder::GetInstance();
304   // Find plugin to update.
305   PluginInstaller* installer = NULL;
306   scoped_ptr<PluginMetadata> plugin;
307   if (finder->FindPluginWithIdentifier(identifier, &installer, &plugin)) {
308     plugin_placeholders_[placeholder_id] = new PluginPlaceholderHost(
309         this, placeholder_id, plugin->name(), installer);
310     OutdatedPluginInfoBarDelegate::Create(InfoBarService::FromWebContents(
311         web_contents()), installer, plugin.Pass());
312   } else {
313     NOTREACHED();
314   }
315 #else
316   // If we don't support third-party plug-in installation, we shouldn't have
317   // outdated plug-ins.
318   NOTREACHED();
319 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
320 }
321
322 #if defined(ENABLE_PLUGIN_INSTALLATION)
323 void PluginObserver::OnFindMissingPlugin(int placeholder_id,
324                                          const std::string& mime_type) {
325   std::string lang = "en-US";  // Oh yes.
326   scoped_ptr<PluginMetadata> plugin_metadata;
327   PluginInstaller* installer = NULL;
328   bool found_plugin = PluginFinder::GetInstance()->FindPlugin(
329       mime_type, lang, &installer, &plugin_metadata);
330   if (!found_plugin) {
331     Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
332     return;
333   }
334   DCHECK(installer);
335   DCHECK(plugin_metadata.get());
336
337   plugin_placeholders_[placeholder_id] =
338       new PluginPlaceholderHost(this, placeholder_id, plugin_metadata->name(),
339                                 installer);
340   PluginInstallerInfoBarDelegate::Create(
341       InfoBarService::FromWebContents(web_contents()), installer,
342       plugin_metadata.Pass(),
343       base::Bind(&PluginObserver::InstallMissingPlugin,
344                  weak_ptr_factory_.GetWeakPtr(), installer));
345 }
346
347 void PluginObserver::InstallMissingPlugin(
348     PluginInstaller* installer,
349     const PluginMetadata* plugin_metadata) {
350   if (plugin_metadata->url_for_display()) {
351     installer->OpenDownloadURL(plugin_metadata->plugin_url(), web_contents());
352   } else {
353     TabModalConfirmDialog::Create(
354         new ConfirmInstallDialogDelegate(
355             web_contents(), installer, plugin_metadata->Clone()),
356         web_contents());
357   }
358 }
359
360 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
361   std::map<int, PluginPlaceholderHost*>::iterator it =
362       plugin_placeholders_.find(placeholder_id);
363   if (it == plugin_placeholders_.end()) {
364     NOTREACHED();
365     return;
366   }
367   delete it->second;
368   plugin_placeholders_.erase(it);
369 }
370 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
371
372 void PluginObserver::OnOpenAboutPlugins() {
373   web_contents()->OpenURL(OpenURLParams(
374       GURL(chrome::kChromeUIPluginsURL),
375       content::Referrer(web_contents()->GetURL(),
376                         blink::WebReferrerPolicyDefault),
377       NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
378 }
379
380 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath& plugin_path) {
381   g_browser_process->metrics_service()->LogPluginLoadingError(plugin_path);
382   base::string16 plugin_name =
383       PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
384   SimpleAlertInfoBarDelegate::Create(
385       InfoBarService::FromWebContents(web_contents()),
386       IDR_INFOBAR_PLUGIN_CRASHED,
387       l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
388                                  plugin_name),
389       true);
390 }
391
392 void PluginObserver::OnNPAPINotSupported(const std::string& identifier) {
393 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
394 #if !defined(USE_AURA)
395   DCHECK(base::win::IsMetroProcess());
396 #endif
397
398   Profile* profile =
399       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
400   if (profile->IsOffTheRecord())
401     return;
402   HostContentSettingsMap* content_settings =
403       profile->GetHostContentSettingsMap();
404   if (content_settings->GetContentSetting(
405       web_contents()->GetURL(),
406       web_contents()->GetURL(),
407       CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP,
408       std::string()) == CONTENT_SETTING_BLOCK)
409     return;
410
411   scoped_ptr<PluginMetadata> plugin;
412   bool ret = PluginFinder::GetInstance()->FindPluginWithIdentifier(
413       identifier, NULL, &plugin);
414   DCHECK(ret);
415
416   PluginMetroModeInfoBarDelegate::Create(
417       InfoBarService::FromWebContents(web_contents()),
418       PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED, plugin->name());
419 #endif
420 }