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