Home | History | Annotate | Download | only in browser
      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/pdf_unsupported_feature.h"
      6 
      7 #include "base/utf_string_conversions.h"
      8 #include "base/values.h"
      9 #include "base/version.h"
     10 #include "chrome/browser/metrics/user_metrics.h"
     11 #include "chrome/browser/prefs/pref_service.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
     14 #include "chrome/common/chrome_content_client.h"
     15 #include "chrome/common/jstemplate_builder.h"
     16 #include "chrome/common/pref_names.h"
     17 #include "content/browser/plugin_service.h"
     18 #include "content/browser/renderer_host/render_process_host.h"
     19 #include "content/browser/renderer_host/render_view_host.h"
     20 #include "content/browser/tab_contents/interstitial_page.h"
     21 #include "content/browser/tab_contents/tab_contents.h"
     22 #include "grit/browser_resources.h"
     23 #include "grit/generated_resources.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 #include "ui/base/resource/resource_bundle.h"
     26 #include "webkit/plugins/npapi/plugin_group.h"
     27 #include "webkit/plugins/npapi/plugin_list.h"
     28 #include "webkit/plugins/npapi/webplugininfo.h"
     29 
     30 using webkit::npapi::PluginGroup;
     31 using webkit::npapi::PluginList;
     32 using webkit::npapi::WebPluginInfo;
     33 
     34 namespace {
     35 
     36 // Only launch Adobe Reader X or later.
     37 static const uint16 kMinReaderVersionToUse = 10;
     38 
     39 static const char kReaderUpdateUrl[] =
     40     "http://www.adobe.com/go/getreader_chrome";
     41 
     42 // The info bar delegate used to ask the user if they want to use Adobe Reader
     43 // by default.  We want the infobar to have [No][Yes], so we swap the text on
     44 // the buttons, and the meaning of the delegate callbacks.
     45 class PDFEnableAdobeReaderConfirmInfoBarDelegate
     46     : public ConfirmInfoBarDelegate {
     47  public:
     48   PDFEnableAdobeReaderConfirmInfoBarDelegate(
     49       TabContents* tab_contents)
     50       : ConfirmInfoBarDelegate(tab_contents),
     51         tab_contents_(tab_contents) {
     52     UserMetrics::RecordAction(
     53         UserMetricsAction("PDF_EnableReaderInfoBarShown"));
     54   }
     55 
     56   // ConfirmInfoBarDelegate
     57   virtual void InfoBarClosed() {
     58     delete this;
     59   }
     60 
     61   virtual void InfoBarDismissed() {
     62     OnNo();
     63   }
     64 
     65   virtual Type GetInfoBarType() const {
     66     return PAGE_ACTION_TYPE;
     67   }
     68 
     69   virtual bool Accept() {
     70     tab_contents_->profile()->GetPrefs()->SetBoolean(
     71         prefs::kPluginsShowSetReaderDefaultInfobar, false);
     72     return OnNo();
     73   }
     74 
     75   virtual bool Cancel() {
     76     return OnYes();
     77   }
     78 
     79   virtual int GetButtons() const {
     80     return BUTTON_OK | BUTTON_CANCEL;
     81   }
     82 
     83   virtual string16 GetButtonLabel(InfoBarButton button) const {
     84     switch (button) {
     85       case BUTTON_OK:
     86         return l10n_util::GetStringUTF16(
     87             IDS_PDF_INFOBAR_NEVER_USE_READER_BUTTON);
     88       case BUTTON_CANCEL:
     89         return l10n_util::GetStringUTF16(
     90             IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON);
     91       default:
     92         // All buttons are labeled above.
     93         NOTREACHED() << "Bad button id " << button;
     94         return string16();
     95     }
     96   }
     97 
     98   virtual string16 GetMessageText() const {
     99     return l10n_util::GetStringUTF16(
    100         IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER);
    101   }
    102 
    103  private:
    104   bool OnYes() {
    105     UserMetrics::RecordAction(
    106         UserMetricsAction("PDF_EnableReaderInfoBarOK"));
    107     webkit::npapi::PluginList::Singleton()->EnableGroup(
    108         false, ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName));
    109     webkit::npapi::PluginList::Singleton()->EnableGroup(
    110         true, ASCIIToUTF16(webkit::npapi::PluginGroup::kAdobeReaderGroupName));
    111     return true;
    112   }
    113 
    114   bool OnNo() {
    115     UserMetrics::RecordAction(
    116         UserMetricsAction("PDF_EnableReaderInfoBarCancel"));
    117     return true;
    118   }
    119 
    120   TabContents* tab_contents_;
    121 
    122   DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderConfirmInfoBarDelegate);
    123 };
    124 
    125 // Launch the url to get the latest Adbobe Reader installer.
    126 void OpenReaderUpdateURL(TabContents* tab) {
    127   tab->OpenURL(GURL(kReaderUpdateUrl), GURL(), CURRENT_TAB,
    128                PageTransition::LINK);
    129 }
    130 
    131 // Opens the PDF using Adobe Reader.
    132 void OpenUsingReader(TabContents* tab,
    133                      const WebPluginInfo& reader_plugin,
    134                      InfoBarDelegate* old_delegate,
    135                      InfoBarDelegate* new_delegate) {
    136   PluginService::OverriddenPlugin plugin;
    137   plugin.render_process_id = tab->GetRenderProcessHost()->id();
    138   plugin.render_view_id = tab->render_view_host()->routing_id();
    139   plugin.url = tab->GetURL();
    140   plugin.plugin = reader_plugin;
    141   // The plugin is disabled, so enable it to get around the renderer check.
    142   // Also give it a new version so that the renderer doesn't show the blocked
    143   // plugin UI if it's vulnerable, since we already went through the
    144   // interstitial.
    145   plugin.plugin.enabled = WebPluginInfo::USER_ENABLED;
    146   plugin.plugin.version = ASCIIToUTF16("11.0.0.0");
    147 
    148   PluginService::GetInstance()->OverridePluginForTab(plugin);
    149   tab->render_view_host()->ReloadFrame();
    150 
    151   if (new_delegate) {
    152     if (old_delegate) {
    153       tab->ReplaceInfoBar(old_delegate, new_delegate);
    154     } else {
    155       tab->AddInfoBar(new_delegate);
    156     }
    157   }
    158 }
    159 
    160 // An interstitial to be used when the user chooses to open a PDF using Adobe
    161 // Reader, but it is out of date.
    162 class PDFUnsupportedFeatureInterstitial : public InterstitialPage {
    163  public:
    164   PDFUnsupportedFeatureInterstitial(
    165       TabContents* tab,
    166       const WebPluginInfo& reader_webplugininfo)
    167       : InterstitialPage(tab, false, tab->GetURL()),
    168         reader_webplugininfo_(reader_webplugininfo) {
    169     UserMetrics::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown"));
    170   }
    171 
    172  protected:
    173   // InterstitialPage implementation.
    174   virtual std::string GetHTMLContents() {
    175     DictionaryValue strings;
    176     strings.SetString(
    177         "title",
    178         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE));
    179     strings.SetString(
    180         "headLine",
    181         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY));
    182     strings.SetString(
    183         "update",
    184         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE));
    185     strings.SetString(
    186         "open_with_reader",
    187         l10n_util::GetStringUTF16(
    188             IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED));
    189     strings.SetString(
    190         "ok",
    191         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK));
    192     strings.SetString(
    193         "cancel",
    194         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL));
    195 
    196     base::StringPiece html(ResourceBundle::GetSharedInstance().
    197         GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML));
    198 
    199     return jstemplate_builder::GetI18nTemplateHtml(html, &strings);
    200   }
    201 
    202   virtual void CommandReceived(const std::string& command) {
    203     if (command == "0") {
    204       UserMetrics::RecordAction(
    205           UserMetricsAction("PDF_ReaderInterstitialCancel"));
    206       DontProceed();
    207       return;
    208     }
    209 
    210     if (command == "1") {
    211       UserMetrics::RecordAction(
    212           UserMetricsAction("PDF_ReaderInterstitialUpdate"));
    213       OpenReaderUpdateURL(tab());
    214     } else if (command == "2") {
    215       UserMetrics::RecordAction(
    216           UserMetricsAction("PDF_ReaderInterstitialIgnore"));
    217       OpenUsingReader(tab(), reader_webplugininfo_, NULL, NULL);
    218     } else {
    219       NOTREACHED();
    220     }
    221     Proceed();
    222   }
    223 
    224  private:
    225   WebPluginInfo reader_webplugininfo_;
    226 
    227   DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial);
    228 };
    229 
    230 // The info bar delegate used to inform the user that we don't support a feature
    231 // in the PDF.  See the comment about how we swap buttons for
    232 // PDFEnableAdobeReaderConfirmInfoBarDelegate.
    233 class PDFUnsupportedFeatureConfirmInfoBarDelegate
    234     : public ConfirmInfoBarDelegate {
    235  public:
    236   PDFUnsupportedFeatureConfirmInfoBarDelegate(
    237       TabContents* tab_contents,
    238       PluginGroup* reader_group)  // NULL if Adobe Reader isn't installed.
    239       : ConfirmInfoBarDelegate(tab_contents),
    240         tab_contents_(tab_contents),
    241         reader_installed_(!!reader_group),
    242         reader_vulnerable_(false) {
    243     if (reader_installed_) {
    244       UserMetrics::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown"));
    245       std::vector<WebPluginInfo> plugins = reader_group->web_plugin_infos();
    246       DCHECK_EQ(plugins.size(), 1u);
    247       reader_webplugininfo_ = plugins[0];
    248 
    249       reader_vulnerable_ = reader_group->IsVulnerable();
    250       if (!reader_vulnerable_) {
    251         scoped_ptr<Version> version(PluginGroup::CreateVersionFromString(
    252             reader_webplugininfo_.version));
    253         if (version.get()) {
    254           if (version->components()[0] < kMinReaderVersionToUse)
    255             reader_vulnerable_ = true;
    256         }
    257       }
    258     } else {
    259       UserMetrics::RecordAction(
    260           UserMetricsAction("PDF_InstallReaderInfoBarShown"));
    261     }
    262   }
    263 
    264   // ConfirmInfoBarDelegate
    265   virtual void InfoBarClosed() {
    266     delete this;
    267   }
    268 
    269   virtual void InfoBarDismissed() {
    270     OnNo();
    271   }
    272 
    273   virtual Type GetInfoBarType() const {
    274     return PAGE_ACTION_TYPE;
    275   }
    276 
    277   virtual bool Accept() {
    278     return OnNo();
    279   }
    280 
    281   virtual bool Cancel() {
    282     return OnYes();
    283   }
    284 
    285   virtual int GetButtons() const {
    286     return BUTTON_OK | BUTTON_CANCEL;
    287   }
    288 
    289   virtual string16 GetButtonLabel(InfoBarButton button) const {
    290     switch (button) {
    291       case BUTTON_OK:
    292         return l10n_util::GetStringUTF16(
    293             IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL);
    294       case BUTTON_CANCEL:
    295         return l10n_util::GetStringUTF16(
    296             IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL);
    297       default:
    298         // All buttons are labeled above.
    299         NOTREACHED() << "Bad button id " << button;
    300         return string16();
    301     }
    302   }
    303 
    304   virtual string16 GetMessageText() const {
    305     return l10n_util::GetStringUTF16(reader_installed_ ?
    306         IDS_PDF_INFOBAR_QUESTION_READER_INSTALLED :
    307         IDS_PDF_INFOBAR_QUESTION_READER_NOT_INSTALLED);
    308   }
    309 
    310  private:
    311   bool OnYes() {
    312    if (!reader_installed_) {
    313       UserMetrics::RecordAction(
    314           UserMetricsAction("PDF_InstallReaderInfoBarOK"));
    315       OpenReaderUpdateURL(tab_contents_);
    316       return true;
    317     }
    318 
    319     UserMetrics::RecordAction(
    320         UserMetricsAction("PDF_UseReaderInfoBarOK"));
    321 
    322     if (reader_vulnerable_) {
    323       PDFUnsupportedFeatureInterstitial* interstitial = new
    324           PDFUnsupportedFeatureInterstitial(
    325               tab_contents_, reader_webplugininfo_);
    326       interstitial->Show();
    327       return true;
    328     }
    329 
    330     InfoBarDelegate* bar = NULL;
    331     if (tab_contents_->profile()->GetPrefs()->GetBoolean(
    332             prefs::kPluginsShowSetReaderDefaultInfobar)) {
    333       bar = new PDFEnableAdobeReaderConfirmInfoBarDelegate(tab_contents_);
    334     }
    335 
    336     if (bar) {
    337       OpenUsingReader(tab_contents_, reader_webplugininfo_, this, bar);
    338       return false;
    339     } else {
    340       OpenUsingReader(tab_contents_, reader_webplugininfo_, NULL, NULL);
    341       return true;
    342     }
    343   }
    344 
    345   bool OnNo() {
    346     if (reader_installed_) {
    347       UserMetrics::RecordAction(
    348           UserMetricsAction("PDF_UseReaderInfoBarCancel"));
    349     } else {
    350       UserMetrics::RecordAction(
    351           UserMetricsAction("PDF_InstallReaderInfoBarCancel"));
    352     }
    353     return true;
    354   }
    355 
    356   TabContents* tab_contents_;
    357   bool reader_installed_;
    358   bool reader_vulnerable_;
    359   WebPluginInfo reader_webplugininfo_;
    360 
    361   DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeatureConfirmInfoBarDelegate);
    362 };
    363 
    364 }  // namespace
    365 
    366 void PDFHasUnsupportedFeature(TabContents* tab) {
    367 #if !defined(OS_WIN)
    368   // Only works for Windows for now.  For Mac, we'll have to launch the file
    369   // externally since Adobe Reader doesn't work inside Chrome.
    370   return;
    371 #endif
    372   string16 reader_group_name(ASCIIToUTF16(PluginGroup::kAdobeReaderGroupName));
    373 
    374   // If the Reader plugin is disabled by policy, don't prompt them.
    375   if (PluginGroup::IsPluginNameDisabledByPolicy(reader_group_name))
    376     return;
    377 
    378   PluginGroup* reader_group = NULL;
    379   std::vector<PluginGroup> plugin_groups;
    380   PluginList::Singleton()->GetPluginGroups(
    381       false, &plugin_groups);
    382   for (size_t i = 0; i < plugin_groups.size(); ++i) {
    383     if (plugin_groups[i].GetGroupName() == reader_group_name) {
    384       reader_group = &plugin_groups[i];
    385       break;
    386     }
    387   }
    388 
    389   tab->AddInfoBar(new PDFUnsupportedFeatureConfirmInfoBarDelegate(
    390       tab, reader_group));
    391 }
    392