| // 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/strings/utf_string_conversions.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/plugins/chrome_plugin_service_filter.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/common/chrome_content_client.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/pdf/browser/open_pdf_in_reader_prompt_client.h" |
| #include "components/pdf/browser/pdf_web_contents_helper.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/page_navigator.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/renderer_preferences.h" |
| #include "grit/browser_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/base/webui/jstemplate_builder.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/metro.h" |
| #include "chrome/browser/ui/pdf/adobe_reader_info_win.h" |
| #endif |
| |
| using base::UserMetricsAction; |
| using content::InterstitialPage; |
| using content::OpenURLParams; |
| using content::Referrer; |
| using content::WebContents; |
| using content::WebPluginInfo; |
| |
| #if defined(OS_WIN) |
| namespace { |
| |
| const char kAdobeReaderUpdateUrl[] = "http://www.adobe.com/go/getreader_chrome"; |
| |
| // The prompt delegate used to ask the user if they want to use Adobe Reader |
| // by default. |
| class PDFEnableAdobeReaderPromptClient |
| : public pdf::OpenPDFInReaderPromptClient { |
| public: |
| explicit PDFEnableAdobeReaderPromptClient(Profile* profile); |
| ~PDFEnableAdobeReaderPromptClient() override; |
| |
| // pdf::OpenPDFInReaderPromptClient |
| base::string16 GetMessageText() const override; |
| base::string16 GetAcceptButtonText() const override; |
| base::string16 GetCancelButtonText() const override; |
| bool ShouldExpire( |
| const content::LoadCommittedDetails& details) const override; |
| void Accept() override; |
| void Cancel() override; |
| |
| private: |
| void OnYes(); |
| void OnNo(); |
| |
| Profile* profile_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptClient); |
| }; |
| |
| PDFEnableAdobeReaderPromptClient::PDFEnableAdobeReaderPromptClient( |
| Profile* profile) |
| : profile_(profile) { |
| content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown")); |
| } |
| |
| PDFEnableAdobeReaderPromptClient::~PDFEnableAdobeReaderPromptClient() { |
| } |
| |
| bool PDFEnableAdobeReaderPromptClient::ShouldExpire( |
| const content::LoadCommittedDetails& details) const { |
| ui::PageTransition transition = |
| ui::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 != ui::PAGE_TRANSITION_RELOAD; |
| } |
| |
| void PDFEnableAdobeReaderPromptClient::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 PDFEnableAdobeReaderPromptClient::Cancel() { |
| content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel")); |
| } |
| |
| base::string16 PDFEnableAdobeReaderPromptClient::GetAcceptButtonText() const { |
| return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON); |
| } |
| |
| base::string16 PDFEnableAdobeReaderPromptClient::GetCancelButtonText() const { |
| return l10n_util::GetStringUTF16(IDS_DONE); |
| } |
| |
| base::string16 PDFEnableAdobeReaderPromptClient::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, |
| ui::PAGE_TRANSITION_LINK, false); |
| web_contents->OpenURL(params); |
| } |
| |
| // Opens the PDF using Adobe Reader. |
| void OpenUsingReader(WebContents* web_contents, |
| const WebPluginInfo& reader_plugin, |
| pdf::OpenPDFInReaderPromptClient* client) { |
| ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame( |
| web_contents->GetRenderProcessHost()->GetID(), |
| web_contents->GetMainFrame()->GetRoutingID(), |
| web_contents->GetURL(), |
| reader_plugin); |
| web_contents->ReloadFocusedFrame(false); |
| |
| pdf::PDFWebContentsHelper* pdf_tab_helper = |
| pdf::PDFWebContentsHelper::FromWebContents(web_contents); |
| if (client) |
| pdf_tab_helper->ShowOpenInReaderPrompt(make_scoped_ptr(client)); |
| } |
| |
| // 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. |
| 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); |
| } |
| |
| 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 plugin 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(); |
| } |
| |
| void OverrideRendererPrefs(content::RendererPreferences* prefs) override { |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents_->GetBrowserContext()); |
| renderer_preferences_util::UpdateFromSystemSettings( |
| prefs, profile, web_contents_); |
| } |
| |
| 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 PDFUnsupportedFeaturePromptClient |
| : public pdf::OpenPDFInReaderPromptClient { |
| public: |
| PDFUnsupportedFeaturePromptClient(WebContents* web_contents, |
| const AdobeReaderPluginInfo& reader_info); |
| ~PDFUnsupportedFeaturePromptClient() override; |
| |
| // pdf::OpenPDFInReaderPromptClient: |
| base::string16 GetMessageText() const override; |
| base::string16 GetAcceptButtonText() const override; |
| base::string16 GetCancelButtonText() const override; |
| bool ShouldExpire( |
| const content::LoadCommittedDetails& details) const override; |
| void Accept() override; |
| void Cancel() override; |
| |
| private: |
| WebContents* web_contents_; |
| const AdobeReaderPluginInfo reader_info_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptClient); |
| }; |
| |
| PDFUnsupportedFeaturePromptClient::PDFUnsupportedFeaturePromptClient( |
| WebContents* web_contents, |
| const AdobeReaderPluginInfo& reader_info) |
| : web_contents_(web_contents), reader_info_(reader_info) { |
| content::RecordAction(reader_info_.is_installed ? |
| UserMetricsAction("PDF_UseReaderInfoBarShown") : |
| UserMetricsAction("PDF_InstallReaderInfoBarShown")); |
| } |
| |
| PDFUnsupportedFeaturePromptClient::~PDFUnsupportedFeaturePromptClient() { |
| } |
| |
| base::string16 PDFUnsupportedFeaturePromptClient::GetMessageText() const { |
| return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE); |
| } |
| |
| base::string16 PDFUnsupportedFeaturePromptClient::GetAcceptButtonText() const { |
| if (base::win::IsMetroProcess()) |
| return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK); |
| |
| return l10n_util::GetStringUTF16( |
| reader_info_.is_installed ? IDS_PDF_BUBBLE_OPEN_IN_READER_LINK |
| : IDS_PDF_BUBBLE_INSTALL_READER_LINK); |
| } |
| |
| base::string16 PDFUnsupportedFeaturePromptClient::GetCancelButtonText() const { |
| return l10n_util::GetStringUTF16(IDS_DONE); |
| } |
| |
| bool PDFUnsupportedFeaturePromptClient::ShouldExpire( |
| const content::LoadCommittedDetails& details) const { |
| return !details.is_in_page; |
| } |
| |
| void PDFUnsupportedFeaturePromptClient::Accept() { |
| if (base::win::IsMetroProcess()) { |
| chrome::AttemptRestartToDesktopMode(); |
| return; |
| } |
| |
| if (!reader_info_.is_installed) { |
| content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK")); |
| OpenReaderUpdateURL(web_contents_); |
| return; |
| } |
| |
| content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK")); |
| |
| if (!reader_info_.is_secure) { |
| new PDFUnsupportedFeatureInterstitial(web_contents_, |
| reader_info_.plugin_info); |
| return; |
| } |
| |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents_->GetBrowserContext()); |
| pdf::OpenPDFInReaderPromptClient* client = |
| new PDFEnableAdobeReaderPromptClient(profile); |
| |
| OpenUsingReader(web_contents_, reader_info_.plugin_info, client); |
| } |
| |
| void PDFUnsupportedFeaturePromptClient::Cancel() { |
| content::RecordAction(reader_info_.is_installed ? |
| UserMetricsAction("PDF_UseReaderInfoBarCancel") : |
| UserMetricsAction("PDF_InstallReaderInfoBarCancel")); |
| } |
| |
| void MaybeShowOpenPDFInReaderPrompt(WebContents* web_contents, |
| const AdobeReaderPluginInfo& reader_info) { |
| // If the Reader plugin is disabled by policy, don't prompt them. |
| if (!reader_info.is_installed || !reader_info.is_enabled) |
| return; |
| |
| scoped_ptr<pdf::OpenPDFInReaderPromptClient> prompt( |
| new PDFUnsupportedFeaturePromptClient(web_contents, reader_info)); |
| pdf::PDFWebContentsHelper* pdf_tab_helper = |
| pdf::PDFWebContentsHelper::FromWebContents(web_contents); |
| pdf_tab_helper->ShowOpenInReaderPrompt(prompt.Pass()); |
| } |
| |
| void GotPluginsCallback(int process_id, |
| int routing_id, |
| const AdobeReaderPluginInfo& reader_info) { |
| WebContents* web_contents = |
| tab_util::GetWebContentsByID(process_id, routing_id); |
| if (web_contents) |
| MaybeShowOpenPDFInReaderPrompt(web_contents, reader_info); |
| } |
| |
| } // namespace |
| #endif // defined(OS_WIN) |
| |
| void PDFHasUnsupportedFeature(WebContents* web_contents) { |
| #if defined(OS_WIN) |
| // Only works for Windows for now. For Mac, we'll have to launch the file |
| // externally since Adobe Reader doesn't work inside Chrome. |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| AdobeReaderPluginInfo reader_info; |
| if (GetAdobeReaderPluginInfo(profile, &reader_info)) { |
| MaybeShowOpenPDFInReaderPrompt(web_contents, reader_info); |
| return; |
| } |
| GetAdobeReaderPluginInfoAsync( |
| profile, |
| base::Bind(&GotPluginsCallback, |
| web_contents->GetRenderProcessHost()->GetID(), |
| web_contents->GetRenderViewHost()->GetRoutingID())); |
| #endif // defined(OS_WIN) |
| } |