Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_observer.cc
blobe3808c6c44b44670f91cc391847278ef31e91925
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"
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/infobars/infobar_service.h"
15 #include "chrome/browser/lifetime/application_lifetime.h"
16 #include "chrome/browser/metrics/metrics_services_manager.h"
17 #include "chrome/browser/plugins/plugin_finder.h"
18 #include "chrome/browser/plugins/plugin_infobar_delegates.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
21 #include "chrome/common/render_messages.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "components/content_settings/core/browser/host_content_settings_map.h"
25 #include "components/infobars/core/confirm_infobar_delegate.h"
26 #include "components/infobars/core/infobar.h"
27 #include "components/infobars/core/simple_alert_infobar_delegate.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"
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)
46 using content::OpenURLParams;
47 using content::PluginService;
48 using content::Referrer;
49 using content::WebContents;
51 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver);
53 namespace {
55 #if defined(ENABLE_PLUGIN_INSTALLATION)
57 // ConfirmInstallDialogDelegate ------------------------------------------------
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);
66 // TabModalConfirmDialogDelegate methods:
67 base::string16 GetTitle() override;
68 base::string16 GetDialogMessage() override;
69 base::string16 GetAcceptButtonTitle() override;
70 void OnAccepted() override;
71 void OnCanceled() override;
73 // WeakPluginInstallerObserver methods:
74 void DownloadStarted() override;
75 void OnlyWeakObserversLeft() override;
77 private:
78 content::WebContents* web_contents_;
79 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()) {
92 base::string16 ConfirmInstallDialogDelegate::GetTitle() {
93 return l10n_util::GetStringFUTF16(
94 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE, plugin_metadata_->name());
97 base::string16 ConfirmInstallDialogDelegate::GetDialogMessage() {
98 return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG,
99 plugin_metadata_->name());
102 base::string16 ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
103 return l10n_util::GetStringUTF16(
104 IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON);
107 void ConfirmInstallDialogDelegate::OnAccepted() {
108 installer()->StartInstalling(plugin_metadata_->plugin_url(), web_contents_);
111 void ConfirmInstallDialogDelegate::OnCanceled() {
114 void ConfirmInstallDialogDelegate::DownloadStarted() {
115 Cancel();
118 void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
119 Cancel();
121 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
123 // ReloadPluginInfoBarDelegate -------------------------------------------------
125 class ReloadPluginInfoBarDelegate : public ConfirmInfoBarDelegate {
126 public:
127 static void Create(InfoBarService* infobar_service,
128 content::NavigationController* controller,
129 const base::string16& message);
131 private:
132 ReloadPluginInfoBarDelegate(content::NavigationController* controller,
133 const base::string16& message);
134 ~ReloadPluginInfoBarDelegate() override;
136 // ConfirmInfobarDelegate:
137 int GetIconID() const override;
138 base::string16 GetMessageText() const override;
139 int GetButtons() const override;
140 base::string16 GetButtonLabel(InfoBarButton button) const override;
141 bool Accept() override;
143 content::NavigationController* controller_;
144 base::string16 message_;
147 // static
148 void ReloadPluginInfoBarDelegate::Create(
149 InfoBarService* infobar_service,
150 content::NavigationController* controller,
151 const base::string16& message) {
152 infobar_service->AddInfoBar(
153 infobar_service->CreateConfirmInfoBar(scoped_ptr<ConfirmInfoBarDelegate>(
154 new ReloadPluginInfoBarDelegate(controller, message))));
157 ReloadPluginInfoBarDelegate::ReloadPluginInfoBarDelegate(
158 content::NavigationController* controller,
159 const base::string16& message)
160 : controller_(controller),
161 message_(message) {}
163 ReloadPluginInfoBarDelegate::~ReloadPluginInfoBarDelegate(){ }
165 int ReloadPluginInfoBarDelegate::GetIconID() const {
166 return IDR_INFOBAR_PLUGIN_CRASHED;
169 base::string16 ReloadPluginInfoBarDelegate::GetMessageText() const {
170 return message_;
173 int ReloadPluginInfoBarDelegate::GetButtons() const {
174 return BUTTON_OK;
177 base::string16 ReloadPluginInfoBarDelegate::GetButtonLabel(
178 InfoBarButton button) const {
179 DCHECK_EQ(BUTTON_OK, button);
180 return l10n_util::GetStringUTF16(IDS_RELOAD_PAGE_WITH_PLUGIN);
183 bool ReloadPluginInfoBarDelegate::Accept() {
184 controller_->Reload(true);
185 return true;
188 } // namespace
190 // PluginObserver -------------------------------------------------------------
192 #if defined(ENABLE_PLUGIN_INSTALLATION)
193 class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
194 public:
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;
209 case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
210 DownloadStarted();
211 break;
216 // PluginInstallerObserver methods:
217 void DownloadStarted() override {
218 observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
221 void DownloadError(const std::string& msg) override {
222 observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
225 void DownloadCancelled() override {
226 observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
229 void DownloadFinished() override {
230 observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
233 private:
234 // Weak pointer; owns us.
235 PluginObserver* observer_;
237 int routing_id_;
239 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
241 PluginObserver::PluginObserver(content::WebContents* web_contents)
242 : content::WebContentsObserver(web_contents),
243 weak_ptr_factory_(this) {
246 PluginObserver::~PluginObserver() {
247 #if defined(ENABLE_PLUGIN_INSTALLATION)
248 STLDeleteValues(&plugin_placeholders_);
249 #endif
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.
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.
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));
281 #endif
284 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
285 base::ProcessId plugin_pid) {
286 DCHECK(!plugin_path.value().empty());
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::Process plugin_process =
298 base::Process::OpenWithAccess(plugin_pid,
299 PROCESS_QUERY_INFORMATION | SYNCHRONIZE);
300 bool is_running = false;
301 if (plugin_process.IsValid()) {
302 is_running =
303 base::GetTerminationStatus(plugin_process.Handle(), NULL) ==
304 base::TERMINATION_STATUS_STILL_RUNNING;
305 plugin_process.Close();
308 if (is_running) {
309 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
310 plugin_name);
311 UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
312 } else {
313 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
314 plugin_name);
315 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
317 #else
318 // Calling the POSIX version of base::GetTerminationStatus() may affect other
319 // code which is interested in the process termination status. (Please see the
320 // comment of the function.) Therefore, a better way is needed to distinguish
321 // disconnections from crashes.
322 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
323 plugin_name);
324 UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
325 #endif
327 ReloadPluginInfoBarDelegate::Create(
328 InfoBarService::FromWebContents(web_contents()),
329 &web_contents()->GetController(),
330 infobar_text);
333 bool PluginObserver::OnMessageReceived(
334 const IPC::Message& message,
335 content::RenderFrameHost* render_frame_host) {
336 IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
337 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin,
338 OnBlockedOutdatedPlugin)
339 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported,
340 OnNPAPINotSupported)
342 IPC_MESSAGE_UNHANDLED(return false)
343 IPC_END_MESSAGE_MAP()
345 return true;
348 bool PluginObserver::OnMessageReceived(const IPC::Message& message) {
349 IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
350 #if defined(ENABLE_PLUGIN_INSTALLATION)
351 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost,
352 OnRemovePluginPlaceholderHost)
353 #endif
354 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_OpenAboutPlugins,
355 OnOpenAboutPlugins)
356 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin,
357 OnCouldNotLoadPlugin)
359 IPC_MESSAGE_UNHANDLED(return false)
360 IPC_END_MESSAGE_MAP()
362 return true;
365 void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id,
366 const std::string& identifier) {
367 #if defined(ENABLE_PLUGIN_INSTALLATION)
368 PluginFinder* finder = PluginFinder::GetInstance();
369 // Find plugin to update.
370 PluginInstaller* installer = NULL;
371 scoped_ptr<PluginMetadata> plugin;
372 if (finder->FindPluginWithIdentifier(identifier, &installer, &plugin)) {
373 plugin_placeholders_[placeholder_id] = new PluginPlaceholderHost(
374 this, placeholder_id, plugin->name(), installer);
375 OutdatedPluginInfoBarDelegate::Create(InfoBarService::FromWebContents(
376 web_contents()), installer, plugin.Pass());
377 } else {
378 NOTREACHED();
380 #else
381 // If we don't support third-party plugin installation, we shouldn't have
382 // outdated plugins.
383 NOTREACHED();
384 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
387 #if defined(ENABLE_PLUGIN_INSTALLATION)
388 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
389 std::map<int, PluginPlaceholderHost*>::iterator it =
390 plugin_placeholders_.find(placeholder_id);
391 if (it == plugin_placeholders_.end()) {
392 NOTREACHED();
393 return;
395 delete it->second;
396 plugin_placeholders_.erase(it);
398 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
400 void PluginObserver::OnOpenAboutPlugins() {
401 web_contents()->OpenURL(OpenURLParams(
402 GURL(chrome::kChromeUIPluginsURL),
403 content::Referrer::SanitizeForRequest(
404 GURL(chrome::kChromeUIPluginsURL),
405 content::Referrer(web_contents()->GetURL(),
406 blink::WebReferrerPolicyDefault)),
407 NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
410 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath& plugin_path) {
411 g_browser_process->GetMetricsServicesManager()->OnPluginLoadingError(
412 plugin_path);
413 base::string16 plugin_name =
414 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
415 SimpleAlertInfoBarDelegate::Create(
416 InfoBarService::FromWebContents(web_contents()),
417 IDR_INFOBAR_PLUGIN_CRASHED,
418 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
419 plugin_name),
420 true);
423 void PluginObserver::OnNPAPINotSupported(const std::string& identifier) {
424 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
425 #if !defined(USE_AURA)
426 DCHECK(base::win::IsMetroProcess());
427 #endif
429 Profile* profile =
430 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
431 if (profile->IsOffTheRecord())
432 return;
433 HostContentSettingsMap* content_settings =
434 profile->GetHostContentSettingsMap();
435 if (content_settings->GetContentSetting(
436 web_contents()->GetURL(),
437 web_contents()->GetURL(),
438 CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP,
439 std::string()) == CONTENT_SETTING_BLOCK)
440 return;
442 scoped_ptr<PluginMetadata> plugin;
443 bool ret = PluginFinder::GetInstance()->FindPluginWithIdentifier(
444 identifier, NULL, &plugin);
445 DCHECK(ret);
447 PluginMetroModeInfoBarDelegate::Create(
448 InfoBarService::FromWebContents(web_contents()),
449 PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED, plugin->name());
450 #endif