blob: 644552db655680c3d96f4750cd8ff156cc3d036f [file] [log] [blame]
// 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 content::InterstitialPage;
using content::OpenURLParams;
using content::PluginService;
using content::Referrer;
using content::UserMetricsAction;
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, ASCIIToUTF16(PluginMetadata::kAdobeReaderGroupName));
plugin_prefs->EnablePluginGroup(
false, 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->GetRenderViewHost()->ReloadFrame();
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 {
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 = 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
}