Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_observer.cc
blobb85e35c2a1eecf518d769df2b60882f794e6601c
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/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"
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 virtual base::string16 GetTitle() OVERRIDE;
68 virtual base::string16 GetMessage() OVERRIDE;
69 virtual base::string16 GetAcceptButtonTitle() OVERRIDE;
70 virtual void OnAccepted() OVERRIDE;
71 virtual void OnCanceled() OVERRIDE;
73 // WeakPluginInstallerObserver methods:
74 virtual void DownloadStarted() OVERRIDE;
75 virtual 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::GetMessage() {
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)
122 } // namespace
124 // PluginObserver -------------------------------------------------------------
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;
143 case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
144 DownloadStarted();
145 break;
150 // PluginInstallerObserver methods:
151 virtual void DownloadStarted() OVERRIDE {
152 observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
155 virtual void DownloadError(const std::string& msg) OVERRIDE {
156 observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
159 virtual void DownloadCancelled() OVERRIDE {
160 observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
163 virtual void DownloadFinished() OVERRIDE {
164 observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
167 private:
168 // Weak pointer; owns us.
169 PluginObserver* observer_;
171 int routing_id_;
173 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
175 PluginObserver::PluginObserver(content::WebContents* web_contents)
176 : content::WebContentsObserver(web_contents),
177 weak_ptr_factory_(this) {
180 PluginObserver::~PluginObserver() {
181 #if defined(ENABLE_PLUGIN_INSTALLATION)
182 STLDeleteValues(&plugin_placeholders_);
183 #endif
186 void PluginObserver::RenderFrameCreated(
187 content::RenderFrameHost* render_frame_host) {
188 #if defined(USE_AURA) && 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.
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.
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));
215 #endif
218 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
219 base::ProcessId plugin_pid) {
220 DCHECK(!plugin_path.value().empty());
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);
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);
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
260 SimpleAlertInfoBarDelegate::Create(
261 InfoBarService::FromWebContents(web_contents()),
262 IDR_INFOBAR_PLUGIN_CRASHED, infobar_text, true);
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)
284 IPC_MESSAGE_UNHANDLED(return false)
285 IPC_END_MESSAGE_MAP()
287 return true;
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);
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 bool ret = finder->FindPluginWithIdentifier(identifier, &installer, &plugin);
308 DCHECK(ret);
310 plugin_placeholders_[placeholder_id] =
311 new PluginPlaceholderHost(this, placeholder_id,
312 plugin->name(), installer);
313 OutdatedPluginInfoBarDelegate::Create(
314 InfoBarService::FromWebContents(web_contents()), installer,
315 plugin.Pass());
316 #else
317 // If we don't support third-party plug-in installation, we shouldn't have
318 // outdated plug-ins.
319 NOTREACHED();
320 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
323 #if defined(ENABLE_PLUGIN_INSTALLATION)
324 void PluginObserver::OnFindMissingPlugin(int placeholder_id,
325 const std::string& mime_type) {
326 std::string lang = "en-US"; // Oh yes.
327 scoped_ptr<PluginMetadata> plugin_metadata;
328 PluginInstaller* installer = NULL;
329 bool found_plugin = PluginFinder::GetInstance()->FindPlugin(
330 mime_type, lang, &installer, &plugin_metadata);
331 if (!found_plugin) {
332 Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
333 return;
335 DCHECK(installer);
336 DCHECK(plugin_metadata.get());
338 plugin_placeholders_[placeholder_id] =
339 new PluginPlaceholderHost(this, placeholder_id, plugin_metadata->name(),
340 installer);
341 PluginInstallerInfoBarDelegate::Create(
342 InfoBarService::FromWebContents(web_contents()), installer,
343 plugin_metadata.Pass(),
344 base::Bind(&PluginObserver::InstallMissingPlugin,
345 weak_ptr_factory_.GetWeakPtr(), installer));
348 void PluginObserver::InstallMissingPlugin(
349 PluginInstaller* installer,
350 const PluginMetadata* plugin_metadata) {
351 if (plugin_metadata->url_for_display()) {
352 installer->OpenDownloadURL(plugin_metadata->plugin_url(), web_contents());
353 } else {
354 TabModalConfirmDialog::Create(
355 new ConfirmInstallDialogDelegate(
356 web_contents(), installer, plugin_metadata->Clone()),
357 web_contents());
361 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
362 std::map<int, PluginPlaceholderHost*>::iterator it =
363 plugin_placeholders_.find(placeholder_id);
364 if (it == plugin_placeholders_.end()) {
365 NOTREACHED();
366 return;
368 delete it->second;
369 plugin_placeholders_.erase(it);
371 #endif // defined(ENABLE_PLUGIN_INSTALLATION)
373 void PluginObserver::OnOpenAboutPlugins() {
374 web_contents()->OpenURL(OpenURLParams(
375 GURL(chrome::kAboutPluginsURL),
376 content::Referrer(web_contents()->GetURL(),
377 blink::WebReferrerPolicyDefault),
378 NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
381 void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath& plugin_path) {
382 g_browser_process->metrics_service()->LogPluginLoadingError(plugin_path);
383 base::string16 plugin_name =
384 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
385 SimpleAlertInfoBarDelegate::Create(
386 InfoBarService::FromWebContents(web_contents()),
387 IDR_INFOBAR_PLUGIN_CRASHED,
388 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
389 plugin_name),
390 true);
393 void PluginObserver::OnNPAPINotSupported(const std::string& identifier) {
394 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
395 #if !defined(USE_AURA)
396 DCHECK(base::win::IsMetroProcess());
397 #endif
399 Profile* profile =
400 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
401 if (profile->IsOffTheRecord())
402 return;
403 HostContentSettingsMap* content_settings =
404 profile->GetHostContentSettingsMap();
405 if (content_settings->GetContentSetting(
406 web_contents()->GetURL(),
407 web_contents()->GetURL(),
408 CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP,
409 std::string()) == CONTENT_SETTING_BLOCK)
410 return;
412 scoped_ptr<PluginMetadata> plugin;
413 bool ret = PluginFinder::GetInstance()->FindPluginWithIdentifier(
414 identifier, NULL, &plugin);
415 DCHECK(ret);
417 PluginMetroModeInfoBarDelegate::Create(
418 InfoBarService::FromWebContents(web_contents()),
419 PluginMetroModeInfoBarDelegate::DESKTOP_MODE_REQUIRED, plugin->name());
420 #endif