| // Copyright (c) 2011 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/pdf_unsupported_feature.h" |
| |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "chrome/browser/metrics/user_metrics.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/tab_contents/confirm_infobar_delegate.h" |
| #include "chrome/common/chrome_content_client.h" |
| #include "chrome/common/jstemplate_builder.h" |
| #include "chrome/common/pref_names.h" |
| #include "content/browser/plugin_service.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/tab_contents/interstitial_page.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "grit/browser_resources.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "webkit/plugins/npapi/plugin_group.h" |
| #include "webkit/plugins/npapi/plugin_list.h" |
| #include "webkit/plugins/npapi/webplugininfo.h" |
| |
| using webkit::npapi::PluginGroup; |
| using webkit::npapi::PluginList; |
| using webkit::npapi::WebPluginInfo; |
| |
| namespace { |
| |
| // Only launch Adobe Reader X or later. |
| static const uint16 kMinReaderVersionToUse = 10; |
| |
| static const char kReaderUpdateUrl[] = |
| "http://www.adobe.com/go/getreader_chrome"; |
| |
| // The info bar delegate used to ask the user if they want to use Adobe Reader |
| // by default. We want the infobar to have [No][Yes], so we swap the text on |
| // the buttons, and the meaning of the delegate callbacks. |
| class PDFEnableAdobeReaderConfirmInfoBarDelegate |
| : public ConfirmInfoBarDelegate { |
| public: |
| PDFEnableAdobeReaderConfirmInfoBarDelegate( |
| TabContents* tab_contents) |
| : ConfirmInfoBarDelegate(tab_contents), |
| tab_contents_(tab_contents) { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_EnableReaderInfoBarShown")); |
| } |
| |
| // ConfirmInfoBarDelegate |
| virtual void InfoBarClosed() { |
| delete this; |
| } |
| |
| virtual void InfoBarDismissed() { |
| OnNo(); |
| } |
| |
| virtual Type GetInfoBarType() const { |
| return PAGE_ACTION_TYPE; |
| } |
| |
| virtual bool Accept() { |
| tab_contents_->profile()->GetPrefs()->SetBoolean( |
| prefs::kPluginsShowSetReaderDefaultInfobar, false); |
| return OnNo(); |
| } |
| |
| virtual bool Cancel() { |
| return OnYes(); |
| } |
| |
| virtual int GetButtons() const { |
| return BUTTON_OK | BUTTON_CANCEL; |
| } |
| |
| virtual string16 GetButtonLabel(InfoBarButton button) const { |
| switch (button) { |
| case BUTTON_OK: |
| return l10n_util::GetStringUTF16( |
| IDS_PDF_INFOBAR_NEVER_USE_READER_BUTTON); |
| case BUTTON_CANCEL: |
| return l10n_util::GetStringUTF16( |
| IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON); |
| default: |
| // All buttons are labeled above. |
| NOTREACHED() << "Bad button id " << button; |
| return string16(); |
| } |
| } |
| |
| virtual string16 GetMessageText() const { |
| return l10n_util::GetStringUTF16( |
| IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER); |
| } |
| |
| private: |
| bool OnYes() { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_EnableReaderInfoBarOK")); |
| webkit::npapi::PluginList::Singleton()->EnableGroup( |
| false, ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName)); |
| webkit::npapi::PluginList::Singleton()->EnableGroup( |
| true, ASCIIToUTF16(webkit::npapi::PluginGroup::kAdobeReaderGroupName)); |
| return true; |
| } |
| |
| bool OnNo() { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_EnableReaderInfoBarCancel")); |
| return true; |
| } |
| |
| TabContents* tab_contents_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderConfirmInfoBarDelegate); |
| }; |
| |
| // Launch the url to get the latest Adbobe Reader installer. |
| void OpenReaderUpdateURL(TabContents* tab) { |
| tab->OpenURL(GURL(kReaderUpdateUrl), GURL(), CURRENT_TAB, |
| PageTransition::LINK); |
| } |
| |
| // Opens the PDF using Adobe Reader. |
| void OpenUsingReader(TabContents* tab, |
| const WebPluginInfo& reader_plugin, |
| InfoBarDelegate* old_delegate, |
| InfoBarDelegate* new_delegate) { |
| PluginService::OverriddenPlugin plugin; |
| plugin.render_process_id = tab->GetRenderProcessHost()->id(); |
| plugin.render_view_id = tab->render_view_host()->routing_id(); |
| plugin.url = tab->GetURL(); |
| plugin.plugin = reader_plugin; |
| // The plugin is disabled, so enable it to get around the renderer check. |
| // Also give it a new version so that the renderer doesn't show the blocked |
| // plugin UI if it's vulnerable, since we already went through the |
| // interstitial. |
| plugin.plugin.enabled = WebPluginInfo::USER_ENABLED; |
| plugin.plugin.version = ASCIIToUTF16("11.0.0.0"); |
| |
| PluginService::GetInstance()->OverridePluginForTab(plugin); |
| tab->render_view_host()->ReloadFrame(); |
| |
| if (new_delegate) { |
| if (old_delegate) { |
| tab->ReplaceInfoBar(old_delegate, new_delegate); |
| } else { |
| tab->AddInfoBar(new_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 InterstitialPage { |
| public: |
| PDFUnsupportedFeatureInterstitial( |
| TabContents* tab, |
| const WebPluginInfo& reader_webplugininfo) |
| : InterstitialPage(tab, false, tab->GetURL()), |
| reader_webplugininfo_(reader_webplugininfo) { |
| UserMetrics::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown")); |
| } |
| |
| protected: |
| // InterstitialPage implementation. |
| virtual std::string GetHTMLContents() { |
| 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 jstemplate_builder::GetI18nTemplateHtml(html, &strings); |
| } |
| |
| virtual void CommandReceived(const std::string& command) { |
| if (command == "0") { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_ReaderInterstitialCancel")); |
| DontProceed(); |
| return; |
| } |
| |
| if (command == "1") { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_ReaderInterstitialUpdate")); |
| OpenReaderUpdateURL(tab()); |
| } else if (command == "2") { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_ReaderInterstitialIgnore")); |
| OpenUsingReader(tab(), reader_webplugininfo_, NULL, NULL); |
| } else { |
| NOTREACHED(); |
| } |
| Proceed(); |
| } |
| |
| private: |
| WebPluginInfo reader_webplugininfo_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial); |
| }; |
| |
| // The info bar delegate used to inform the user that we don't support a feature |
| // in the PDF. See the comment about how we swap buttons for |
| // PDFEnableAdobeReaderConfirmInfoBarDelegate. |
| class PDFUnsupportedFeatureConfirmInfoBarDelegate |
| : public ConfirmInfoBarDelegate { |
| public: |
| PDFUnsupportedFeatureConfirmInfoBarDelegate( |
| TabContents* tab_contents, |
| PluginGroup* reader_group) // NULL if Adobe Reader isn't installed. |
| : ConfirmInfoBarDelegate(tab_contents), |
| tab_contents_(tab_contents), |
| reader_installed_(!!reader_group), |
| reader_vulnerable_(false) { |
| if (reader_installed_) { |
| UserMetrics::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown")); |
| std::vector<WebPluginInfo> plugins = reader_group->web_plugin_infos(); |
| DCHECK_EQ(plugins.size(), 1u); |
| reader_webplugininfo_ = plugins[0]; |
| |
| reader_vulnerable_ = reader_group->IsVulnerable(); |
| if (!reader_vulnerable_) { |
| scoped_ptr<Version> version(PluginGroup::CreateVersionFromString( |
| reader_webplugininfo_.version)); |
| if (version.get()) { |
| if (version->components()[0] < kMinReaderVersionToUse) |
| reader_vulnerable_ = true; |
| } |
| } |
| } else { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_InstallReaderInfoBarShown")); |
| } |
| } |
| |
| // ConfirmInfoBarDelegate |
| virtual void InfoBarClosed() { |
| delete this; |
| } |
| |
| virtual void InfoBarDismissed() { |
| OnNo(); |
| } |
| |
| virtual Type GetInfoBarType() const { |
| return PAGE_ACTION_TYPE; |
| } |
| |
| virtual bool Accept() { |
| return OnNo(); |
| } |
| |
| virtual bool Cancel() { |
| return OnYes(); |
| } |
| |
| virtual int GetButtons() const { |
| return BUTTON_OK | BUTTON_CANCEL; |
| } |
| |
| virtual string16 GetButtonLabel(InfoBarButton button) const { |
| switch (button) { |
| case BUTTON_OK: |
| return l10n_util::GetStringUTF16( |
| IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL); |
| case BUTTON_CANCEL: |
| return l10n_util::GetStringUTF16( |
| IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL); |
| default: |
| // All buttons are labeled above. |
| NOTREACHED() << "Bad button id " << button; |
| return string16(); |
| } |
| } |
| |
| virtual string16 GetMessageText() const { |
| return l10n_util::GetStringUTF16(reader_installed_ ? |
| IDS_PDF_INFOBAR_QUESTION_READER_INSTALLED : |
| IDS_PDF_INFOBAR_QUESTION_READER_NOT_INSTALLED); |
| } |
| |
| private: |
| bool OnYes() { |
| if (!reader_installed_) { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_InstallReaderInfoBarOK")); |
| OpenReaderUpdateURL(tab_contents_); |
| return true; |
| } |
| |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_UseReaderInfoBarOK")); |
| |
| if (reader_vulnerable_) { |
| PDFUnsupportedFeatureInterstitial* interstitial = new |
| PDFUnsupportedFeatureInterstitial( |
| tab_contents_, reader_webplugininfo_); |
| interstitial->Show(); |
| return true; |
| } |
| |
| InfoBarDelegate* bar = NULL; |
| if (tab_contents_->profile()->GetPrefs()->GetBoolean( |
| prefs::kPluginsShowSetReaderDefaultInfobar)) { |
| bar = new PDFEnableAdobeReaderConfirmInfoBarDelegate(tab_contents_); |
| } |
| |
| if (bar) { |
| OpenUsingReader(tab_contents_, reader_webplugininfo_, this, bar); |
| return false; |
| } else { |
| OpenUsingReader(tab_contents_, reader_webplugininfo_, NULL, NULL); |
| return true; |
| } |
| } |
| |
| bool OnNo() { |
| if (reader_installed_) { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_UseReaderInfoBarCancel")); |
| } else { |
| UserMetrics::RecordAction( |
| UserMetricsAction("PDF_InstallReaderInfoBarCancel")); |
| } |
| return true; |
| } |
| |
| TabContents* tab_contents_; |
| bool reader_installed_; |
| bool reader_vulnerable_; |
| WebPluginInfo reader_webplugininfo_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeatureConfirmInfoBarDelegate); |
| }; |
| |
| } // namespace |
| |
| void PDFHasUnsupportedFeature(TabContents* tab) { |
| #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. |
| return; |
| #endif |
| string16 reader_group_name(ASCIIToUTF16(PluginGroup::kAdobeReaderGroupName)); |
| |
| // If the Reader plugin is disabled by policy, don't prompt them. |
| if (PluginGroup::IsPluginNameDisabledByPolicy(reader_group_name)) |
| return; |
| |
| PluginGroup* reader_group = NULL; |
| std::vector<PluginGroup> plugin_groups; |
| PluginList::Singleton()->GetPluginGroups( |
| false, &plugin_groups); |
| for (size_t i = 0; i < plugin_groups.size(); ++i) { |
| if (plugin_groups[i].GetGroupName() == reader_group_name) { |
| reader_group = &plugin_groups[i]; |
| break; |
| } |
| } |
| |
| tab->AddInfoBar(new PDFUnsupportedFeatureConfirmInfoBarDelegate( |
| tab, reader_group)); |
| } |