| // 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 |
| } |