root/chrome/browser/ui/pdf/pdf_unsupported_feature.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ShouldExpire
  2. Accept
  3. Cancel
  4. GetAcceptButtonText
  5. GetCancelButtonText
  6. GetMessageText
  7. OpenReaderUpdateURL
  8. OpenUsingReader
  9. reader_webplugininfo_
  10. GetHTMLContents
  11. CommandReceived
  12. OverrideRendererPrefs
  13. reader_vulnerable_
  14. GetMessageText
  15. GetAcceptButtonText
  16. GetCancelButtonText
  17. ShouldExpire
  18. Accept
  19. Cancel
  20. GotPluginsCallback
  21. PDFHasUnsupportedFeature

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/pdf/pdf_unsupported_feature.h"

#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
#include "chrome/browser/plugins/plugin_finder.h"
#include "chrome/browser/plugins/plugin_metadata.h"
#include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/pdf/open_pdf_in_reader_prompt_delegate.h"
#include "chrome/browser/ui/pdf/pdf_tab_helper.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/interstitial_page.h"
#include "content/public/browser/interstitial_page_delegate.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/page_transition_types.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/jstemplate_builder.h"
#include "ui/gfx/image/image.h"

#if defined(OS_WIN)
#include "base/win/metro.h"
#endif

using base::UserMetricsAction;
using content::InterstitialPage;
using content::OpenURLParams;
using content::PluginService;
using content::Referrer;
using content::WebContents;
using content::WebPluginInfo;

namespace {

const char kAdobeReaderUpdateUrl[] = "http://www.adobe.com/go/getreader_chrome";

#if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
const char kAdobeReaderIdentifier[] = "adobe-reader";
#endif

// The prompt delegate used to ask the user if they want to use Adobe Reader
// by default.
class PDFEnableAdobeReaderPromptDelegate
    : public OpenPDFInReaderPromptDelegate {
 public:
  explicit PDFEnableAdobeReaderPromptDelegate(Profile* profile);
  virtual ~PDFEnableAdobeReaderPromptDelegate();

  // OpenPDFInReaderPromptDelegate
  virtual base::string16 GetMessageText() const OVERRIDE;
  virtual base::string16 GetAcceptButtonText() const OVERRIDE;
  virtual base::string16 GetCancelButtonText() const OVERRIDE;
  virtual bool ShouldExpire(
      const content::LoadCommittedDetails& details) const OVERRIDE;
  virtual void Accept() OVERRIDE;
  virtual void Cancel() OVERRIDE;

 private:
  void OnYes();
  void OnNo();

  Profile* profile_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptDelegate);
};

PDFEnableAdobeReaderPromptDelegate::PDFEnableAdobeReaderPromptDelegate(
    Profile* profile)
    : profile_(profile) {
  content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown"));
}

PDFEnableAdobeReaderPromptDelegate::~PDFEnableAdobeReaderPromptDelegate() {
}

bool PDFEnableAdobeReaderPromptDelegate::ShouldExpire(
    const content::LoadCommittedDetails& details) const {
  content::PageTransition transition =
      content::PageTransitionStripQualifier(details.entry->GetTransitionType());
  // We don't want to expire on a reload, because that is how we open the PDF in
  // Reader.
  return !details.is_in_page && transition != content::PAGE_TRANSITION_RELOAD;
}

void PDFEnableAdobeReaderPromptDelegate::Accept() {
  content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarOK"));
  PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get();
  plugin_prefs->EnablePluginGroup(
      true, base::ASCIIToUTF16(PluginMetadata::kAdobeReaderGroupName));
  plugin_prefs->EnablePluginGroup(
      false, base::ASCIIToUTF16(ChromeContentClient::kPDFPluginName));
}

void PDFEnableAdobeReaderPromptDelegate::Cancel() {
  content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel"));
}

base::string16 PDFEnableAdobeReaderPromptDelegate::GetAcceptButtonText() const {
  return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON);
}

base::string16 PDFEnableAdobeReaderPromptDelegate::GetCancelButtonText() const {
  return l10n_util::GetStringUTF16(IDS_DONE);
}

base::string16 PDFEnableAdobeReaderPromptDelegate::GetMessageText() const {
  return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER);
}

// Launch the url to get the latest Adbobe Reader installer.
void OpenReaderUpdateURL(WebContents* web_contents) {
  OpenURLParams params(
      GURL(kAdobeReaderUpdateUrl), Referrer(), NEW_FOREGROUND_TAB,
      content::PAGE_TRANSITION_LINK, false);
  web_contents->OpenURL(params);
}

// Opens the PDF using Adobe Reader.
void OpenUsingReader(WebContents* web_contents,
                     const WebPluginInfo& reader_plugin,
                     OpenPDFInReaderPromptDelegate* delegate) {
  ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
      web_contents->GetRenderProcessHost()->GetID(),
      web_contents->GetMainFrame()->GetRoutingID(),
      web_contents->GetURL(),
      reader_plugin);
  web_contents->ReloadFocusedFrame(false);

  PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
  if (delegate)
    pdf_tab_helper->ShowOpenInReaderPrompt(make_scoped_ptr(delegate));
}

// An interstitial to be used when the user chooses to open a PDF using Adobe
// Reader, but it is out of date.
class PDFUnsupportedFeatureInterstitial
    : public content::InterstitialPageDelegate {
 public:
  PDFUnsupportedFeatureInterstitial(
      WebContents* web_contents,
      const WebPluginInfo& reader_webplugininfo)
      : web_contents_(web_contents),
        reader_webplugininfo_(reader_webplugininfo) {
    content::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown"));
    interstitial_page_ = InterstitialPage::Create(
        web_contents, false, web_contents->GetURL(), this);
    interstitial_page_->Show();
  }

 protected:
  // InterstitialPageDelegate implementation.
  virtual std::string GetHTMLContents() OVERRIDE {
    base::DictionaryValue strings;
    strings.SetString(
        "title",
        l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE));
    strings.SetString(
        "headLine",
        l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY));
    strings.SetString(
        "update",
        l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE));
    strings.SetString(
        "open_with_reader",
        l10n_util::GetStringUTF16(
            IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED));
    strings.SetString(
        "ok",
        l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK));
    strings.SetString(
        "cancel",
        l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL));

    base::StringPiece html(ResourceBundle::GetSharedInstance().
                           GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML));

    return webui::GetI18nTemplateHtml(html, &strings);
  }

  virtual void CommandReceived(const std::string& command) OVERRIDE {
    if (command == "0") {
      content::RecordAction(
          UserMetricsAction("PDF_ReaderInterstitialCancel"));
      interstitial_page_->DontProceed();
      return;
    }

    if (command == "1") {
      content::RecordAction(
          UserMetricsAction("PDF_ReaderInterstitialUpdate"));
      OpenReaderUpdateURL(web_contents_);
    } else if (command == "2") {
      content::RecordAction(
          UserMetricsAction("PDF_ReaderInterstitialIgnore"));
      // Pretend that the plug-in is up-to-date so that we don't block it.
      reader_webplugininfo_.version = base::ASCIIToUTF16("11.0.0.0");
      OpenUsingReader(web_contents_, reader_webplugininfo_, NULL);
    } else {
      NOTREACHED();
    }
    interstitial_page_->Proceed();
  }

  virtual void OverrideRendererPrefs(
      content::RendererPreferences* prefs) OVERRIDE {
    Profile* profile =
        Profile::FromBrowserContext(web_contents_->GetBrowserContext());
    renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
  }

 private:
  WebContents* web_contents_;
  WebPluginInfo reader_webplugininfo_;
  InterstitialPage* interstitial_page_;  // Owns us.

  DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial);
};

// The delegate for the bubble used to inform the user that we don't support a
// feature in the PDF.
class PDFUnsupportedFeaturePromptDelegate
    : public OpenPDFInReaderPromptDelegate {
 public:
  // |reader| is NULL if Adobe Reader isn't installed.
  PDFUnsupportedFeaturePromptDelegate(WebContents* web_contents,
                                      const content::WebPluginInfo* reader,
                                      PluginFinder* plugin_finder);
  virtual ~PDFUnsupportedFeaturePromptDelegate();

  // OpenPDFInReaderPromptDelegate:
  virtual base::string16 GetMessageText() const OVERRIDE;
  virtual base::string16 GetAcceptButtonText() const OVERRIDE;
  virtual base::string16 GetCancelButtonText() const OVERRIDE;
  virtual bool ShouldExpire(
      const content::LoadCommittedDetails& details) const OVERRIDE;
  virtual void Accept() OVERRIDE;
  virtual void Cancel() OVERRIDE;

 private:
  WebContents* web_contents_;
  bool reader_installed_;
  bool reader_vulnerable_;
  WebPluginInfo reader_webplugininfo_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptDelegate);
};

PDFUnsupportedFeaturePromptDelegate::PDFUnsupportedFeaturePromptDelegate(
    WebContents* web_contents,
    const content::WebPluginInfo* reader,
    PluginFinder* plugin_finder)
    : web_contents_(web_contents),
      reader_installed_(!!reader),
      reader_vulnerable_(false) {
  if (!reader_installed_) {
    content::RecordAction(
        UserMetricsAction("PDF_InstallReaderInfoBarShown"));
    return;
  }

  content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown"));
  reader_webplugininfo_ = *reader;

#if defined(ENABLE_PLUGIN_INSTALLATION)
  scoped_ptr<PluginMetadata> plugin_metadata(
      plugin_finder->GetPluginMetadata(reader_webplugininfo_));

  reader_vulnerable_ = plugin_metadata->GetSecurityStatus(*reader) !=
                       PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
#else
  NOTREACHED();
#endif
}

PDFUnsupportedFeaturePromptDelegate::~PDFUnsupportedFeaturePromptDelegate() {
}

base::string16 PDFUnsupportedFeaturePromptDelegate::GetMessageText() const {
  return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE);
}

base::string16 PDFUnsupportedFeaturePromptDelegate::GetAcceptButtonText()
    const {
#if defined(OS_WIN)
  if (base::win::IsMetroProcess())
    return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK);
#endif

  if (reader_installed_)
    return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_OPEN_IN_READER_LINK);

  return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_INSTALL_READER_LINK);
}

base::string16 PDFUnsupportedFeaturePromptDelegate::GetCancelButtonText()
    const {
  return l10n_util::GetStringUTF16(IDS_DONE);
}

bool PDFUnsupportedFeaturePromptDelegate::ShouldExpire(
    const content::LoadCommittedDetails& details) const {
  return !details.is_in_page;
}

void PDFUnsupportedFeaturePromptDelegate::Accept() {
#if defined(OS_WIN)
  if (base::win::IsMetroProcess()) {
    chrome::AttemptRestartWithModeSwitch();
    return;
  }
#endif

  if (!reader_installed_) {
    content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK"));
    OpenReaderUpdateURL(web_contents_);
    return;
  }

  content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK"));

  if (reader_vulnerable_) {
    new PDFUnsupportedFeatureInterstitial(web_contents_, reader_webplugininfo_);
    return;
  }

  Profile* profile =
      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
  OpenPDFInReaderPromptDelegate* delegate =
      new PDFEnableAdobeReaderPromptDelegate(profile);

  OpenUsingReader(web_contents_, reader_webplugininfo_, delegate);
}

void PDFUnsupportedFeaturePromptDelegate::Cancel() {
  content::RecordAction(reader_installed_ ?
                        UserMetricsAction("PDF_UseReaderInfoBarCancel") :
                        UserMetricsAction("PDF_InstallReaderInfoBarCancel"));
}

#if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
void GotPluginsCallback(int process_id,
                        int routing_id,
                        const std::vector<content::WebPluginInfo>& plugins) {
  WebContents* web_contents =
      tab_util::GetWebContentsByID(process_id, routing_id);
  if (!web_contents)
    return;

  const content::WebPluginInfo* reader = NULL;
  PluginFinder* plugin_finder = PluginFinder::GetInstance();
  for (size_t i = 0; i < plugins.size(); ++i) {
    scoped_ptr<PluginMetadata> plugin_metadata(
        plugin_finder->GetPluginMetadata(plugins[i]));
    if (plugin_metadata->identifier() != kAdobeReaderIdentifier)
      continue;

    DCHECK(!reader);
    reader = &plugins[i];
    // If the Reader plugin is disabled by policy, don't prompt them.
    Profile* profile =
        Profile::FromBrowserContext(web_contents->GetBrowserContext());
    PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile);
    if (plugin_prefs->PolicyStatusForPlugin(plugin_metadata->name()) ==
        PluginPrefs::POLICY_DISABLED) {
      return;
    }
    break;
  }

  scoped_ptr<OpenPDFInReaderPromptDelegate> prompt(
      new PDFUnsupportedFeaturePromptDelegate(
          web_contents, reader, plugin_finder));
  PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
  pdf_tab_helper->ShowOpenInReaderPrompt(prompt.Pass());
}
#endif  // defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)

}  // namespace

void PDFHasUnsupportedFeature(content::WebContents* web_contents) {
#if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
  // Only works for Windows for now.  For Mac, we'll have to launch the file
  // externally since Adobe Reader doesn't work inside Chrome.
  PluginService::GetInstance()->GetPlugins(base::Bind(&GotPluginsCallback,
      web_contents->GetRenderProcessHost()->GetID(),
      web_contents->GetRenderViewHost()->GetRoutingID()));
#endif
}

/* [<][>][^][v][top][bottom][index][help] */