1// Copyright (c) 2011 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/plugin_observer.h"
6
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/content_settings/host_content_settings_map.h"
9#include "chrome/browser/google/google_util.h"
10#include "chrome/browser/metrics/user_metrics.h"
11#include "chrome/browser/plugin_installer_infobar_delegate.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
14#include "chrome/browser/tab_contents/simple_alert_infobar_delegate.h"
15#include "chrome/common/render_messages.h"
16#include "chrome/common/url_constants.h"
17#include "content/browser/renderer_host/render_view_host.h"
18#include "content/browser/tab_contents/tab_contents.h"
19#include "content/common/view_messages.h"
20#include "grit/generated_resources.h"
21#include "grit/theme_resources.h"
22#include "ui/base/l10n/l10n_util.h"
23#include "ui/base/resource/resource_bundle.h"
24#include "webkit/plugins/npapi/default_plugin_shared.h"
25#include "webkit/plugins/npapi/plugin_group.h"
26#include "webkit/plugins/npapi/plugin_list.h"
27#include "webkit/plugins/npapi/webplugininfo.h"
28
29namespace {
30
31// PluginInfoBarDelegate ------------------------------------------------------
32
33class PluginInfoBarDelegate : public ConfirmInfoBarDelegate {
34 public:
35  PluginInfoBarDelegate(TabContents* tab_contents, const string16& name);
36
37 protected:
38  virtual ~PluginInfoBarDelegate();
39
40  // ConfirmInfoBarDelegate:
41  virtual void InfoBarClosed();
42  virtual bool Cancel();
43  virtual bool LinkClicked(WindowOpenDisposition disposition);
44
45  virtual std::string GetLearnMoreURL() const = 0;
46
47  string16 name_;
48  TabContents* tab_contents_;
49
50 private:
51  // ConfirmInfoBarDelegate:
52  virtual SkBitmap* GetIcon() const;
53  virtual string16 GetLinkText();
54
55  DISALLOW_COPY_AND_ASSIGN(PluginInfoBarDelegate);
56};
57
58PluginInfoBarDelegate::PluginInfoBarDelegate(TabContents* tab_contents,
59                                             const string16& name)
60    : ConfirmInfoBarDelegate(tab_contents),
61      name_(name),
62      tab_contents_(tab_contents) {
63}
64
65PluginInfoBarDelegate::~PluginInfoBarDelegate() {
66}
67
68void PluginInfoBarDelegate::InfoBarClosed() {
69  delete this;
70}
71
72bool PluginInfoBarDelegate::Cancel() {
73  tab_contents_->render_view_host()->LoadBlockedPlugins();
74  return true;
75}
76
77bool PluginInfoBarDelegate::LinkClicked(WindowOpenDisposition disposition) {
78  GURL url = google_util::AppendGoogleLocaleParam(GURL(GetLearnMoreURL()));
79  tab_contents_->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
80  return false;
81}
82
83SkBitmap* PluginInfoBarDelegate::GetIcon() const {
84  return ResourceBundle::GetSharedInstance().GetBitmapNamed(
85      IDR_INFOBAR_PLUGIN_INSTALL);
86}
87
88string16 PluginInfoBarDelegate::GetLinkText() {
89  return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
90}
91
92
93// BlockedPluginInfoBarDelegate -----------------------------------------------
94
95class BlockedPluginInfoBarDelegate : public PluginInfoBarDelegate {
96 public:
97  BlockedPluginInfoBarDelegate(TabContents* tab_contents,
98                               const string16& name);
99
100 private:
101  virtual ~BlockedPluginInfoBarDelegate();
102
103  // PluginInfoBarDelegate:
104  virtual string16 GetMessageText() const;
105  virtual string16 GetButtonLabel(InfoBarButton button) const;
106  virtual bool Accept();
107  virtual bool Cancel();
108  virtual void InfoBarClosed();
109  virtual void InfoBarDismissed();
110  virtual bool LinkClicked(WindowOpenDisposition disposition);
111  virtual std::string GetLearnMoreURL() const;
112
113  DISALLOW_COPY_AND_ASSIGN(BlockedPluginInfoBarDelegate);
114};
115
116BlockedPluginInfoBarDelegate::BlockedPluginInfoBarDelegate(
117    TabContents* tab_contents,
118    const string16& utf16_name)
119    : PluginInfoBarDelegate(tab_contents, utf16_name) {
120  UserMetrics::RecordAction(UserMetricsAction("BlockedPluginInfobar.Shown"));
121  std::string name = UTF16ToUTF8(utf16_name);
122  if (name == webkit::npapi::PluginGroup::kJavaGroupName)
123    UserMetrics::RecordAction(
124        UserMetricsAction("BlockedPluginInfobar.Shown.Java"));
125  else if (name == webkit::npapi::PluginGroup::kQuickTimeGroupName)
126    UserMetrics::RecordAction(
127        UserMetricsAction("BlockedPluginInfobar.Shown.QuickTime"));
128  else if (name == webkit::npapi::PluginGroup::kShockwaveGroupName)
129    UserMetrics::RecordAction(
130        UserMetricsAction("BlockedPluginInfobar.Shown.Shockwave"));
131  else if (name == webkit::npapi::PluginGroup::kRealPlayerGroupName)
132    UserMetrics::RecordAction(
133        UserMetricsAction("BlockedPluginInfobar.Shown.RealPlayer"));
134}
135
136BlockedPluginInfoBarDelegate::~BlockedPluginInfoBarDelegate() {
137}
138
139std::string BlockedPluginInfoBarDelegate::GetLearnMoreURL() const {
140  return chrome::kBlockedPluginLearnMoreURL;
141}
142
143string16 BlockedPluginInfoBarDelegate::GetMessageText() const {
144  return l10n_util::GetStringFUTF16(IDS_PLUGIN_NOT_AUTHORIZED, name_);
145}
146
147string16 BlockedPluginInfoBarDelegate::GetButtonLabel(
148    InfoBarButton button) const {
149  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
150      IDS_PLUGIN_ENABLE_TEMPORARILY : IDS_PLUGIN_ENABLE_ALWAYS);
151}
152
153bool BlockedPluginInfoBarDelegate::Accept() {
154  UserMetrics::RecordAction(
155      UserMetricsAction("BlockedPluginInfobar.AllowThisTime"));
156  return PluginInfoBarDelegate::Cancel();
157}
158
159bool BlockedPluginInfoBarDelegate::Cancel() {
160  UserMetrics::RecordAction(
161      UserMetricsAction("BlockedPluginInfobar.AlwaysAllow"));
162  tab_contents_->profile()->GetHostContentSettingsMap()->AddExceptionForURL(
163      tab_contents_->GetURL(), CONTENT_SETTINGS_TYPE_PLUGINS, std::string(),
164      CONTENT_SETTING_ALLOW);
165  return PluginInfoBarDelegate::Cancel();
166}
167
168void BlockedPluginInfoBarDelegate::InfoBarDismissed() {
169  UserMetrics::RecordAction(
170      UserMetricsAction("BlockedPluginInfobar.Dismissed"));
171}
172
173void BlockedPluginInfoBarDelegate::InfoBarClosed() {
174  UserMetrics::RecordAction(UserMetricsAction("BlockedPluginInfobar.Closed"));
175  PluginInfoBarDelegate::InfoBarClosed();
176}
177
178bool BlockedPluginInfoBarDelegate::LinkClicked(
179    WindowOpenDisposition disposition) {
180  UserMetrics::RecordAction(
181      UserMetricsAction("BlockedPluginInfobar.LearnMore"));
182  return PluginInfoBarDelegate::LinkClicked(disposition);
183}
184
185// OutdatedPluginInfoBarDelegate ----------------------------------------------
186
187class OutdatedPluginInfoBarDelegate : public PluginInfoBarDelegate {
188 public:
189  OutdatedPluginInfoBarDelegate(TabContents* tab_contents,
190                                const string16& name,
191                                const GURL& update_url);
192
193 private:
194  virtual ~OutdatedPluginInfoBarDelegate();
195
196  // PluginInfoBarDelegate:
197  virtual string16 GetMessageText() const;
198  virtual string16 GetButtonLabel(InfoBarButton button) const;
199  virtual bool Accept();
200  virtual bool Cancel();
201  virtual void InfoBarClosed();
202  virtual void InfoBarDismissed();
203  virtual bool LinkClicked(WindowOpenDisposition disposition);
204  virtual std::string GetLearnMoreURL() const;
205
206  GURL update_url_;
207
208  DISALLOW_COPY_AND_ASSIGN(OutdatedPluginInfoBarDelegate);
209};
210
211OutdatedPluginInfoBarDelegate::OutdatedPluginInfoBarDelegate(
212    TabContents* tab_contents,
213    const string16& utf16_name,
214    const GURL& update_url)
215    : PluginInfoBarDelegate(tab_contents, utf16_name),
216      update_url_(update_url) {
217  UserMetrics::RecordAction(UserMetricsAction("OutdatedPluginInfobar.Shown"));
218  std::string name = UTF16ToUTF8(utf16_name);
219  if (name == webkit::npapi::PluginGroup::kJavaGroupName)
220    UserMetrics::RecordAction(
221        UserMetricsAction("OutdatedPluginInfobar.Shown.Java"));
222  else if (name == webkit::npapi::PluginGroup::kQuickTimeGroupName)
223    UserMetrics::RecordAction(
224        UserMetricsAction("OutdatedPluginInfobar.Shown.QuickTime"));
225  else if (name == webkit::npapi::PluginGroup::kShockwaveGroupName)
226    UserMetrics::RecordAction(
227        UserMetricsAction("OutdatedPluginInfobar.Shown.Shockwave"));
228  else if (name == webkit::npapi::PluginGroup::kRealPlayerGroupName)
229    UserMetrics::RecordAction(
230        UserMetricsAction("OutdatedPluginInfobar.Shown.RealPlayer"));
231  else if (name == webkit::npapi::PluginGroup::kSilverlightGroupName)
232    UserMetrics::RecordAction(
233        UserMetricsAction("OutdatedPluginInfobar.Shown.Silverlight"));
234  else if (name == webkit::npapi::PluginGroup::kAdobeReaderGroupName)
235    UserMetrics::RecordAction(
236        UserMetricsAction("OutdatedPluginInfobar.Shown.Reader"));
237}
238
239OutdatedPluginInfoBarDelegate::~OutdatedPluginInfoBarDelegate() {
240}
241
242std::string OutdatedPluginInfoBarDelegate::GetLearnMoreURL() const {
243  return chrome::kOutdatedPluginLearnMoreURL;
244}
245
246string16 OutdatedPluginInfoBarDelegate::GetMessageText() const {
247  return l10n_util::GetStringFUTF16(IDS_PLUGIN_OUTDATED_PROMPT, name_);
248}
249
250string16 OutdatedPluginInfoBarDelegate::GetButtonLabel(
251    InfoBarButton button) const {
252  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
253      IDS_PLUGIN_UPDATE : IDS_PLUGIN_ENABLE_TEMPORARILY);
254}
255
256bool OutdatedPluginInfoBarDelegate::Accept() {
257  UserMetrics::RecordAction(UserMetricsAction("OutdatedPluginInfobar.Update"));
258  tab_contents_->OpenURL(update_url_, GURL(), NEW_FOREGROUND_TAB,
259                         PageTransition::LINK);
260  return false;
261}
262
263bool OutdatedPluginInfoBarDelegate::Cancel() {
264  UserMetrics::RecordAction(
265      UserMetricsAction("OutdatedPluginInfobar.AllowThisTime"));
266  return PluginInfoBarDelegate::Cancel();
267}
268
269void OutdatedPluginInfoBarDelegate::InfoBarDismissed() {
270  UserMetrics::RecordAction(
271      UserMetricsAction("OutdatedPluginInfobar.Dismissed"));
272}
273
274void OutdatedPluginInfoBarDelegate::InfoBarClosed() {
275  UserMetrics::RecordAction(UserMetricsAction("OutdatedPluginInfobar.Closed"));
276  PluginInfoBarDelegate::InfoBarClosed();
277}
278
279bool OutdatedPluginInfoBarDelegate::LinkClicked(
280    WindowOpenDisposition disposition) {
281  UserMetrics::RecordAction(
282      UserMetricsAction("OutdatedPluginInfobar.LearnMore"));
283  return PluginInfoBarDelegate::LinkClicked(disposition);
284}
285
286}  // namespace
287
288
289// PluginObserver -------------------------------------------------------------
290
291PluginObserver::PluginObserver(TabContents* tab_contents)
292    : TabContentsObserver(tab_contents) {
293}
294
295PluginObserver::~PluginObserver() {
296}
297
298bool PluginObserver::OnMessageReceived(const IPC::Message& message) {
299  IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
300    IPC_MESSAGE_HANDLER(ViewHostMsg_MissingPluginStatus, OnMissingPluginStatus)
301    IPC_MESSAGE_HANDLER(ViewHostMsg_CrashedPlugin, OnCrashedPlugin)
302    IPC_MESSAGE_HANDLER(ViewHostMsg_BlockedOutdatedPlugin,
303                        OnBlockedOutdatedPlugin)
304    IPC_MESSAGE_UNHANDLED(return false)
305  IPC_END_MESSAGE_MAP()
306
307  return true;
308}
309
310PluginInstallerInfoBarDelegate* PluginObserver::GetPluginInstaller() {
311  if (plugin_installer_ == NULL)
312    plugin_installer_.reset(new PluginInstallerInfoBarDelegate(tab_contents()));
313  return plugin_installer_->AsPluginInstallerInfoBarDelegate();
314}
315
316void PluginObserver::OnMissingPluginStatus(int status) {
317  // TODO(PORT): pull in when plug-ins work
318#if defined(OS_WIN)
319  if (status == webkit::npapi::default_plugin::MISSING_PLUGIN_AVAILABLE) {
320    tab_contents()->AddInfoBar(
321        new PluginInstallerInfoBarDelegate(tab_contents()));
322    return;
323  }
324
325  DCHECK_EQ(webkit::npapi::default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD,
326            status);
327  for (size_t i = 0; i < tab_contents()->infobar_count(); ++i) {
328    InfoBarDelegate* delegate = tab_contents()->GetInfoBarDelegateAt(i);
329    if (delegate->AsPluginInstallerInfoBarDelegate() != NULL) {
330      tab_contents()->RemoveInfoBar(delegate);
331      return;
332    }
333  }
334#endif
335}
336
337void PluginObserver::OnCrashedPlugin(const FilePath& plugin_path) {
338  DCHECK(!plugin_path.value().empty());
339
340  string16 plugin_name = plugin_path.LossyDisplayName();
341  webkit::npapi::WebPluginInfo plugin_info;
342  if (webkit::npapi::PluginList::Singleton()->GetPluginInfoByPath(
343          plugin_path, &plugin_info) &&
344      !plugin_info.name.empty()) {
345    plugin_name = plugin_info.name;
346#if defined(OS_MACOSX)
347    // Many plugins on the Mac have .plugin in the actual name, which looks
348    // terrible, so look for that and strip it off if present.
349    const std::string kPluginExtension = ".plugin";
350    if (EndsWith(plugin_name, ASCIIToUTF16(kPluginExtension), true))
351      plugin_name.erase(plugin_name.length() - kPluginExtension.length());
352#endif  // OS_MACOSX
353  }
354  SkBitmap* crash_icon = ResourceBundle::GetSharedInstance().GetBitmapNamed(
355      IDR_INFOBAR_PLUGIN_CRASHED);
356  tab_contents()->AddInfoBar(new SimpleAlertInfoBarDelegate(tab_contents(),
357      crash_icon,
358      l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT, plugin_name),
359      true));
360}
361
362void PluginObserver::OnBlockedOutdatedPlugin(const string16& name,
363                                             const GURL& update_url) {
364  tab_contents()->AddInfoBar(update_url.is_empty() ?
365      static_cast<InfoBarDelegate*>(new BlockedPluginInfoBarDelegate(
366          tab_contents(), name)) :
367      new OutdatedPluginInfoBarDelegate(tab_contents(), name, update_url));
368}
369
370