Home | History | Annotate | Download | only in pdf
      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/ui/pdf/pdf_unsupported_feature.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/values.h"
     12 #include "base/version.h"
     13 #include "chrome/browser/lifetime/application_lifetime.h"
     14 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
     15 #include "chrome/browser/plugins/plugin_finder.h"
     16 #include "chrome/browser/plugins/plugin_metadata.h"
     17 #include "chrome/browser/plugins/plugin_prefs.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/browser/renderer_preferences_util.h"
     20 #include "chrome/browser/tab_contents/tab_util.h"
     21 #include "chrome/browser/ui/pdf/open_pdf_in_reader_prompt_delegate.h"
     22 #include "chrome/browser/ui/pdf/pdf_tab_helper.h"
     23 #include "chrome/common/chrome_content_client.h"
     24 #include "chrome/common/pref_names.h"
     25 #include "content/public/browser/interstitial_page.h"
     26 #include "content/public/browser/interstitial_page_delegate.h"
     27 #include "content/public/browser/navigation_details.h"
     28 #include "content/public/browser/navigation_entry.h"
     29 #include "content/public/browser/plugin_service.h"
     30 #include "content/public/browser/render_process_host.h"
     31 #include "content/public/browser/render_view_host.h"
     32 #include "content/public/browser/user_metrics.h"
     33 #include "content/public/browser/web_contents.h"
     34 #include "content/public/common/page_transition_types.h"
     35 #include "grit/browser_resources.h"
     36 #include "grit/generated_resources.h"
     37 #include "grit/theme_resources.h"
     38 #include "ui/base/l10n/l10n_util.h"
     39 #include "ui/base/resource/resource_bundle.h"
     40 #include "ui/gfx/image/image.h"
     41 #include "ui/webui/jstemplate_builder.h"
     42 
     43 #if defined(OS_WIN)
     44 #include "base/win/metro.h"
     45 #endif
     46 
     47 using content::InterstitialPage;
     48 using content::OpenURLParams;
     49 using content::PluginService;
     50 using content::Referrer;
     51 using content::UserMetricsAction;
     52 using content::WebContents;
     53 using content::WebPluginInfo;
     54 
     55 namespace {
     56 
     57 static const char kAdobeReaderIdentifier[] = "adobe-reader";
     58 static const char kAdobeReaderUpdateUrl[] =
     59     "http://www.adobe.com/go/getreader_chrome";
     60 
     61 // The prompt delegate used to ask the user if they want to use Adobe Reader
     62 // by default.
     63 class PDFEnableAdobeReaderPromptDelegate
     64     : public OpenPDFInReaderPromptDelegate {
     65  public:
     66   explicit PDFEnableAdobeReaderPromptDelegate(Profile* profile);
     67   virtual ~PDFEnableAdobeReaderPromptDelegate();
     68 
     69   // OpenPDFInReaderPromptDelegate
     70   virtual string16 GetMessageText() const OVERRIDE;
     71   virtual string16 GetAcceptButtonText() const OVERRIDE;
     72   virtual string16 GetCancelButtonText() const OVERRIDE;
     73   virtual bool ShouldExpire(
     74       const content::LoadCommittedDetails& details) const OVERRIDE;
     75   virtual void Accept() OVERRIDE;
     76   virtual void Cancel() OVERRIDE;
     77 
     78  private:
     79   void OnYes();
     80   void OnNo();
     81 
     82   Profile* profile_;
     83 
     84   DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptDelegate);
     85 };
     86 
     87 PDFEnableAdobeReaderPromptDelegate::PDFEnableAdobeReaderPromptDelegate(
     88     Profile* profile)
     89     : profile_(profile) {
     90   content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown"));
     91 }
     92 
     93 PDFEnableAdobeReaderPromptDelegate::~PDFEnableAdobeReaderPromptDelegate() {
     94 }
     95 
     96 bool PDFEnableAdobeReaderPromptDelegate::ShouldExpire(
     97     const content::LoadCommittedDetails& details) const {
     98   content::PageTransition transition =
     99       content::PageTransitionStripQualifier(details.entry->GetTransitionType());
    100   // We don't want to expire on a reload, because that is how we open the PDF in
    101   // Reader.
    102   return !details.is_in_page && transition != content::PAGE_TRANSITION_RELOAD;
    103 }
    104 
    105 void PDFEnableAdobeReaderPromptDelegate::Accept() {
    106   content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarOK"));
    107   PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get();
    108   plugin_prefs->EnablePluginGroup(
    109       true, ASCIIToUTF16(PluginMetadata::kAdobeReaderGroupName));
    110   plugin_prefs->EnablePluginGroup(
    111       false, ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName));
    112 }
    113 
    114 void PDFEnableAdobeReaderPromptDelegate::Cancel() {
    115   content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel"));
    116 }
    117 
    118 string16 PDFEnableAdobeReaderPromptDelegate::GetAcceptButtonText() const {
    119   return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON);
    120 }
    121 
    122 string16 PDFEnableAdobeReaderPromptDelegate::GetCancelButtonText() const {
    123   return l10n_util::GetStringUTF16(IDS_DONE);
    124 }
    125 
    126 string16 PDFEnableAdobeReaderPromptDelegate::GetMessageText() const {
    127   return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER);
    128 }
    129 
    130 // Launch the url to get the latest Adbobe Reader installer.
    131 void OpenReaderUpdateURL(WebContents* web_contents) {
    132   OpenURLParams params(
    133       GURL(kAdobeReaderUpdateUrl), Referrer(), NEW_FOREGROUND_TAB,
    134       content::PAGE_TRANSITION_LINK, false);
    135   web_contents->OpenURL(params);
    136 }
    137 
    138 // Opens the PDF using Adobe Reader.
    139 void OpenUsingReader(WebContents* web_contents,
    140                      const WebPluginInfo& reader_plugin,
    141                      OpenPDFInReaderPromptDelegate* delegate) {
    142   ChromePluginServiceFilter::GetInstance()->OverridePluginForTab(
    143       web_contents->GetRenderProcessHost()->GetID(),
    144       web_contents->GetRenderViewHost()->GetRoutingID(),
    145       web_contents->GetURL(),
    146       reader_plugin);
    147   web_contents->GetRenderViewHost()->ReloadFrame();
    148 
    149   PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
    150   if (delegate)
    151     pdf_tab_helper->ShowOpenInReaderPrompt(make_scoped_ptr(delegate));
    152 }
    153 
    154 // An interstitial to be used when the user chooses to open a PDF using Adobe
    155 // Reader, but it is out of date.
    156 class PDFUnsupportedFeatureInterstitial
    157     : public content::InterstitialPageDelegate {
    158  public:
    159   PDFUnsupportedFeatureInterstitial(
    160       WebContents* web_contents,
    161       const WebPluginInfo& reader_webplugininfo)
    162       : web_contents_(web_contents),
    163         reader_webplugininfo_(reader_webplugininfo) {
    164     content::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown"));
    165     interstitial_page_ = InterstitialPage::Create(
    166         web_contents, false, web_contents->GetURL(), this);
    167     interstitial_page_->Show();
    168   }
    169 
    170  protected:
    171   // InterstitialPageDelegate implementation.
    172   virtual std::string GetHTMLContents() OVERRIDE {
    173     DictionaryValue strings;
    174     strings.SetString(
    175         "title",
    176         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE));
    177     strings.SetString(
    178         "headLine",
    179         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY));
    180     strings.SetString(
    181         "update",
    182         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE));
    183     strings.SetString(
    184         "open_with_reader",
    185         l10n_util::GetStringUTF16(
    186             IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED));
    187     strings.SetString(
    188         "ok",
    189         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK));
    190     strings.SetString(
    191         "cancel",
    192         l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL));
    193 
    194     base::StringPiece html(ResourceBundle::GetSharedInstance().
    195                            GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML));
    196 
    197     return webui::GetI18nTemplateHtml(html, &strings);
    198   }
    199 
    200   virtual void CommandReceived(const std::string& command) OVERRIDE {
    201     if (command == "0") {
    202       content::RecordAction(
    203           UserMetricsAction("PDF_ReaderInterstitialCancel"));
    204       interstitial_page_->DontProceed();
    205       return;
    206     }
    207 
    208     if (command == "1") {
    209       content::RecordAction(
    210           UserMetricsAction("PDF_ReaderInterstitialUpdate"));
    211       OpenReaderUpdateURL(web_contents_);
    212     } else if (command == "2") {
    213       content::RecordAction(
    214           UserMetricsAction("PDF_ReaderInterstitialIgnore"));
    215       // Pretend that the plug-in is up-to-date so that we don't block it.
    216       reader_webplugininfo_.version = ASCIIToUTF16("11.0.0.0");
    217       OpenUsingReader(web_contents_, reader_webplugininfo_, NULL);
    218     } else {
    219       NOTREACHED();
    220     }
    221     interstitial_page_->Proceed();
    222   }
    223 
    224   virtual void OverrideRendererPrefs(
    225       content::RendererPreferences* prefs) OVERRIDE {
    226     Profile* profile =
    227         Profile::FromBrowserContext(web_contents_->GetBrowserContext());
    228     renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
    229   }
    230 
    231  private:
    232   WebContents* web_contents_;
    233   WebPluginInfo reader_webplugininfo_;
    234   InterstitialPage* interstitial_page_;  // Owns us.
    235 
    236   DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial);
    237 };
    238 
    239 // The delegate for the bubble used to inform the user that we don't support a
    240 // feature in the PDF.
    241 class PDFUnsupportedFeaturePromptDelegate
    242     : public OpenPDFInReaderPromptDelegate {
    243  public:
    244   // |reader| is NULL if Adobe Reader isn't installed.
    245   PDFUnsupportedFeaturePromptDelegate(WebContents* web_contents,
    246                                       const content::WebPluginInfo* reader,
    247                                       PluginFinder* plugin_finder);
    248   virtual ~PDFUnsupportedFeaturePromptDelegate();
    249 
    250   // OpenPDFInReaderPromptDelegate:
    251   virtual string16 GetMessageText() const OVERRIDE;
    252   virtual string16 GetAcceptButtonText() const OVERRIDE;
    253   virtual string16 GetCancelButtonText() const OVERRIDE;
    254   virtual bool ShouldExpire(
    255       const content::LoadCommittedDetails& details) const OVERRIDE;
    256   virtual void Accept() OVERRIDE;
    257   virtual void Cancel() OVERRIDE;
    258 
    259  private:
    260   WebContents* web_contents_;
    261   bool reader_installed_;
    262   bool reader_vulnerable_;
    263   WebPluginInfo reader_webplugininfo_;
    264 
    265   DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptDelegate);
    266 };
    267 
    268 PDFUnsupportedFeaturePromptDelegate::PDFUnsupportedFeaturePromptDelegate(
    269     WebContents* web_contents,
    270     const content::WebPluginInfo* reader,
    271     PluginFinder* plugin_finder)
    272     : web_contents_(web_contents),
    273       reader_installed_(!!reader),
    274       reader_vulnerable_(false) {
    275   if (!reader_installed_) {
    276     content::RecordAction(
    277         UserMetricsAction("PDF_InstallReaderInfoBarShown"));
    278     return;
    279   }
    280 
    281   content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown"));
    282   reader_webplugininfo_ = *reader;
    283 
    284 #if defined(ENABLE_PLUGIN_INSTALLATION)
    285   scoped_ptr<PluginMetadata> plugin_metadata(
    286       plugin_finder->GetPluginMetadata(reader_webplugininfo_));
    287 
    288   reader_vulnerable_ = plugin_metadata->GetSecurityStatus(*reader) !=
    289                        PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
    290 #else
    291   NOTREACHED();
    292 #endif
    293 }
    294 
    295 PDFUnsupportedFeaturePromptDelegate::~PDFUnsupportedFeaturePromptDelegate() {
    296 }
    297 
    298 string16 PDFUnsupportedFeaturePromptDelegate::GetMessageText() const {
    299   return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE);
    300 }
    301 
    302 string16 PDFUnsupportedFeaturePromptDelegate::GetAcceptButtonText() const {
    303 #if defined(OS_WIN)
    304   if (base::win::IsMetroProcess())
    305     return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK);
    306 #endif
    307 
    308   if (reader_installed_)
    309     return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_OPEN_IN_READER_LINK);
    310 
    311   return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_INSTALL_READER_LINK);
    312 }
    313 
    314 string16 PDFUnsupportedFeaturePromptDelegate::GetCancelButtonText() const {
    315   return l10n_util::GetStringUTF16(IDS_DONE);
    316 }
    317 
    318 bool PDFUnsupportedFeaturePromptDelegate::ShouldExpire(
    319     const content::LoadCommittedDetails& details) const {
    320   return !details.is_in_page;
    321 }
    322 
    323 void PDFUnsupportedFeaturePromptDelegate::Accept() {
    324 #if defined(OS_WIN)
    325   if (base::win::IsMetroProcess()) {
    326     chrome::AttemptRestartWithModeSwitch();
    327     return;
    328   }
    329 #endif
    330 
    331   if (!reader_installed_) {
    332     content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK"));
    333     OpenReaderUpdateURL(web_contents_);
    334     return;
    335   }
    336 
    337   content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK"));
    338 
    339   if (reader_vulnerable_) {
    340     new PDFUnsupportedFeatureInterstitial(web_contents_, reader_webplugininfo_);
    341     return;
    342   }
    343 
    344   Profile* profile =
    345       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
    346   OpenPDFInReaderPromptDelegate* delegate =
    347       new PDFEnableAdobeReaderPromptDelegate(profile);
    348 
    349   OpenUsingReader(web_contents_, reader_webplugininfo_, delegate);
    350 }
    351 
    352 void PDFUnsupportedFeaturePromptDelegate::Cancel() {
    353   content::RecordAction(reader_installed_ ?
    354                         UserMetricsAction("PDF_UseReaderInfoBarCancel") :
    355                         UserMetricsAction("PDF_InstallReaderInfoBarCancel"));
    356 }
    357 
    358 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
    359 void GotPluginsCallback(int process_id,
    360                         int routing_id,
    361                         const std::vector<content::WebPluginInfo>& plugins) {
    362   WebContents* web_contents =
    363       tab_util::GetWebContentsByID(process_id, routing_id);
    364   if (!web_contents)
    365     return;
    366 
    367   const content::WebPluginInfo* reader = NULL;
    368   PluginFinder* plugin_finder = PluginFinder::GetInstance();
    369   for (size_t i = 0; i < plugins.size(); ++i) {
    370     scoped_ptr<PluginMetadata> plugin_metadata(
    371         plugin_finder->GetPluginMetadata(plugins[i]));
    372     if (plugin_metadata->identifier() != kAdobeReaderIdentifier)
    373       continue;
    374 
    375     DCHECK(!reader);
    376     reader = &plugins[i];
    377     // If the Reader plugin is disabled by policy, don't prompt them.
    378     Profile* profile =
    379         Profile::FromBrowserContext(web_contents->GetBrowserContext());
    380     PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile);
    381     if (plugin_prefs->PolicyStatusForPlugin(plugin_metadata->name()) ==
    382         PluginPrefs::POLICY_DISABLED) {
    383       return;
    384     }
    385     break;
    386   }
    387 
    388   scoped_ptr<OpenPDFInReaderPromptDelegate> prompt(
    389       new PDFUnsupportedFeaturePromptDelegate(
    390           web_contents, reader, plugin_finder));
    391   PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
    392   pdf_tab_helper->ShowOpenInReaderPrompt(prompt.Pass());
    393 }
    394 #endif  // defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
    395 
    396 }  // namespace
    397 
    398 void PDFHasUnsupportedFeature(content::WebContents* web_contents) {
    399 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
    400   // Only works for Windows for now.  For Mac, we'll have to launch the file
    401   // externally since Adobe Reader doesn't work inside Chrome.
    402   PluginService::GetInstance()->GetPlugins(base::Bind(&GotPluginsCallback,
    403       web_contents->GetRenderProcessHost()->GetID(),
    404       web_contents->GetRenderViewHost()->GetRoutingID()));
    405 #endif
    406 }
    407