1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/pdf/pdf_unsupported_feature.h" 6 7 #include "base/bind.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "chrome/browser/lifetime/application_lifetime.h" 11 #include "chrome/browser/plugins/chrome_plugin_service_filter.h" 12 #include "chrome/browser/plugins/plugin_metadata.h" 13 #include "chrome/browser/plugins/plugin_prefs.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/renderer_preferences_util.h" 16 #include "chrome/browser/tab_contents/tab_util.h" 17 #include "chrome/browser/ui/pdf/open_pdf_in_reader_prompt_delegate.h" 18 #include "chrome/browser/ui/pdf/pdf_tab_helper.h" 19 #include "chrome/common/chrome_content_client.h" 20 #include "content/public/browser/interstitial_page.h" 21 #include "content/public/browser/interstitial_page_delegate.h" 22 #include "content/public/browser/navigation_details.h" 23 #include "content/public/browser/navigation_entry.h" 24 #include "content/public/browser/page_navigator.h" 25 #include "content/public/browser/render_frame_host.h" 26 #include "content/public/browser/render_process_host.h" 27 #include "content/public/browser/render_view_host.h" 28 #include "content/public/browser/user_metrics.h" 29 #include "content/public/browser/web_contents.h" 30 #include "grit/browser_resources.h" 31 #include "grit/generated_resources.h" 32 #include "ui/base/l10n/l10n_util.h" 33 #include "ui/base/resource/resource_bundle.h" 34 #include "ui/base/webui/jstemplate_builder.h" 35 36 #if defined(OS_WIN) 37 #include "base/win/metro.h" 38 #include "chrome/browser/ui/pdf/adobe_reader_info_win.h" 39 #endif 40 41 using base::UserMetricsAction; 42 using content::InterstitialPage; 43 using content::OpenURLParams; 44 using content::Referrer; 45 using content::WebContents; 46 using content::WebPluginInfo; 47 48 #if defined(OS_WIN) 49 namespace { 50 51 const char kAdobeReaderUpdateUrl[] = "http://www.adobe.com/go/getreader_chrome"; 52 53 // The prompt delegate used to ask the user if they want to use Adobe Reader 54 // by default. 55 class PDFEnableAdobeReaderPromptDelegate 56 : public OpenPDFInReaderPromptDelegate { 57 public: 58 explicit PDFEnableAdobeReaderPromptDelegate(Profile* profile); 59 virtual ~PDFEnableAdobeReaderPromptDelegate(); 60 61 // OpenPDFInReaderPromptDelegate 62 virtual base::string16 GetMessageText() const OVERRIDE; 63 virtual base::string16 GetAcceptButtonText() const OVERRIDE; 64 virtual base::string16 GetCancelButtonText() const OVERRIDE; 65 virtual bool ShouldExpire( 66 const content::LoadCommittedDetails& details) const OVERRIDE; 67 virtual void Accept() OVERRIDE; 68 virtual void Cancel() OVERRIDE; 69 70 private: 71 void OnYes(); 72 void OnNo(); 73 74 Profile* profile_; 75 76 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptDelegate); 77 }; 78 79 PDFEnableAdobeReaderPromptDelegate::PDFEnableAdobeReaderPromptDelegate( 80 Profile* profile) 81 : profile_(profile) { 82 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown")); 83 } 84 85 PDFEnableAdobeReaderPromptDelegate::~PDFEnableAdobeReaderPromptDelegate() { 86 } 87 88 bool PDFEnableAdobeReaderPromptDelegate::ShouldExpire( 89 const content::LoadCommittedDetails& details) const { 90 content::PageTransition transition = 91 content::PageTransitionStripQualifier(details.entry->GetTransitionType()); 92 // We don't want to expire on a reload, because that is how we open the PDF in 93 // Reader. 94 return !details.is_in_page && transition != content::PAGE_TRANSITION_RELOAD; 95 } 96 97 void PDFEnableAdobeReaderPromptDelegate::Accept() { 98 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarOK")); 99 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get(); 100 plugin_prefs->EnablePluginGroup( 101 true, base::ASCIIToUTF16(PluginMetadata::kAdobeReaderGroupName)); 102 plugin_prefs->EnablePluginGroup( 103 false, base::ASCIIToUTF16(ChromeContentClient::kPDFPluginName)); 104 } 105 106 void PDFEnableAdobeReaderPromptDelegate::Cancel() { 107 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel")); 108 } 109 110 base::string16 PDFEnableAdobeReaderPromptDelegate::GetAcceptButtonText() const { 111 return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON); 112 } 113 114 base::string16 PDFEnableAdobeReaderPromptDelegate::GetCancelButtonText() const { 115 return l10n_util::GetStringUTF16(IDS_DONE); 116 } 117 118 base::string16 PDFEnableAdobeReaderPromptDelegate::GetMessageText() const { 119 return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER); 120 } 121 122 // Launch the url to get the latest Adbobe Reader installer. 123 void OpenReaderUpdateURL(WebContents* web_contents) { 124 OpenURLParams params( 125 GURL(kAdobeReaderUpdateUrl), Referrer(), NEW_FOREGROUND_TAB, 126 content::PAGE_TRANSITION_LINK, false); 127 web_contents->OpenURL(params); 128 } 129 130 // Opens the PDF using Adobe Reader. 131 void OpenUsingReader(WebContents* web_contents, 132 const WebPluginInfo& reader_plugin, 133 OpenPDFInReaderPromptDelegate* delegate) { 134 ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame( 135 web_contents->GetRenderProcessHost()->GetID(), 136 web_contents->GetMainFrame()->GetRoutingID(), 137 web_contents->GetURL(), 138 reader_plugin); 139 web_contents->ReloadFocusedFrame(false); 140 141 PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents); 142 if (delegate) 143 pdf_tab_helper->ShowOpenInReaderPrompt(make_scoped_ptr(delegate)); 144 } 145 146 // An interstitial to be used when the user chooses to open a PDF using Adobe 147 // Reader, but it is out of date. 148 class PDFUnsupportedFeatureInterstitial 149 : public content::InterstitialPageDelegate { 150 public: 151 PDFUnsupportedFeatureInterstitial( 152 WebContents* web_contents, 153 const WebPluginInfo& reader_webplugininfo) 154 : web_contents_(web_contents), 155 reader_webplugininfo_(reader_webplugininfo) { 156 content::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown")); 157 interstitial_page_ = InterstitialPage::Create( 158 web_contents, false, web_contents->GetURL(), this); 159 interstitial_page_->Show(); 160 } 161 162 protected: 163 // InterstitialPageDelegate implementation. 164 virtual std::string GetHTMLContents() OVERRIDE { 165 base::DictionaryValue strings; 166 strings.SetString( 167 "title", 168 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE)); 169 strings.SetString( 170 "headLine", 171 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY)); 172 strings.SetString( 173 "update", 174 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE)); 175 strings.SetString( 176 "open_with_reader", 177 l10n_util::GetStringUTF16( 178 IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED)); 179 strings.SetString( 180 "ok", 181 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK)); 182 strings.SetString( 183 "cancel", 184 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL)); 185 186 base::StringPiece html(ResourceBundle::GetSharedInstance(). 187 GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML)); 188 189 return webui::GetI18nTemplateHtml(html, &strings); 190 } 191 192 virtual void CommandReceived(const std::string& command) OVERRIDE { 193 if (command == "0") { 194 content::RecordAction( 195 UserMetricsAction("PDF_ReaderInterstitialCancel")); 196 interstitial_page_->DontProceed(); 197 return; 198 } 199 200 if (command == "1") { 201 content::RecordAction( 202 UserMetricsAction("PDF_ReaderInterstitialUpdate")); 203 OpenReaderUpdateURL(web_contents_); 204 } else if (command == "2") { 205 content::RecordAction( 206 UserMetricsAction("PDF_ReaderInterstitialIgnore")); 207 // Pretend that the plug-in is up-to-date so that we don't block it. 208 reader_webplugininfo_.version = base::ASCIIToUTF16("11.0.0.0"); 209 OpenUsingReader(web_contents_, reader_webplugininfo_, NULL); 210 } else { 211 NOTREACHED(); 212 } 213 interstitial_page_->Proceed(); 214 } 215 216 virtual void OverrideRendererPrefs( 217 content::RendererPreferences* prefs) OVERRIDE { 218 Profile* profile = 219 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); 220 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile); 221 } 222 223 private: 224 WebContents* web_contents_; 225 WebPluginInfo reader_webplugininfo_; 226 InterstitialPage* interstitial_page_; // Owns us. 227 228 DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial); 229 }; 230 231 // The delegate for the bubble used to inform the user that we don't support a 232 // feature in the PDF. 233 class PDFUnsupportedFeaturePromptDelegate 234 : public OpenPDFInReaderPromptDelegate { 235 public: 236 PDFUnsupportedFeaturePromptDelegate( 237 WebContents* web_contents, 238 const AdobeReaderPluginInfo& reader_info); 239 virtual ~PDFUnsupportedFeaturePromptDelegate(); 240 241 // OpenPDFInReaderPromptDelegate: 242 virtual base::string16 GetMessageText() const OVERRIDE; 243 virtual base::string16 GetAcceptButtonText() const OVERRIDE; 244 virtual base::string16 GetCancelButtonText() const OVERRIDE; 245 virtual bool ShouldExpire( 246 const content::LoadCommittedDetails& details) const OVERRIDE; 247 virtual void Accept() OVERRIDE; 248 virtual void Cancel() OVERRIDE; 249 250 private: 251 WebContents* web_contents_; 252 const AdobeReaderPluginInfo reader_info_; 253 254 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptDelegate); 255 }; 256 257 PDFUnsupportedFeaturePromptDelegate::PDFUnsupportedFeaturePromptDelegate( 258 WebContents* web_contents, 259 const AdobeReaderPluginInfo& reader_info) 260 : web_contents_(web_contents), 261 reader_info_(reader_info) { 262 content::RecordAction(reader_info_.is_installed ? 263 UserMetricsAction("PDF_UseReaderInfoBarShown") : 264 UserMetricsAction("PDF_InstallReaderInfoBarShown")); 265 } 266 267 PDFUnsupportedFeaturePromptDelegate::~PDFUnsupportedFeaturePromptDelegate() { 268 } 269 270 base::string16 PDFUnsupportedFeaturePromptDelegate::GetMessageText() const { 271 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE); 272 } 273 274 base::string16 PDFUnsupportedFeaturePromptDelegate::GetAcceptButtonText() 275 const { 276 if (base::win::IsMetroProcess()) 277 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK); 278 279 return l10n_util::GetStringUTF16( 280 reader_info_.is_installed ? IDS_PDF_BUBBLE_OPEN_IN_READER_LINK 281 : IDS_PDF_BUBBLE_INSTALL_READER_LINK); 282 } 283 284 base::string16 PDFUnsupportedFeaturePromptDelegate::GetCancelButtonText() 285 const { 286 return l10n_util::GetStringUTF16(IDS_DONE); 287 } 288 289 bool PDFUnsupportedFeaturePromptDelegate::ShouldExpire( 290 const content::LoadCommittedDetails& details) const { 291 return !details.is_in_page; 292 } 293 294 void PDFUnsupportedFeaturePromptDelegate::Accept() { 295 if (base::win::IsMetroProcess()) { 296 chrome::AttemptRestartWithModeSwitch(); 297 return; 298 } 299 300 if (!reader_info_.is_installed) { 301 content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK")); 302 OpenReaderUpdateURL(web_contents_); 303 return; 304 } 305 306 content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK")); 307 308 if (!reader_info_.is_secure) { 309 new PDFUnsupportedFeatureInterstitial(web_contents_, 310 reader_info_.plugin_info); 311 return; 312 } 313 314 Profile* profile = 315 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); 316 OpenPDFInReaderPromptDelegate* delegate = 317 new PDFEnableAdobeReaderPromptDelegate(profile); 318 319 OpenUsingReader(web_contents_, reader_info_.plugin_info, delegate); 320 } 321 322 void PDFUnsupportedFeaturePromptDelegate::Cancel() { 323 content::RecordAction(reader_info_.is_installed ? 324 UserMetricsAction("PDF_UseReaderInfoBarCancel") : 325 UserMetricsAction("PDF_InstallReaderInfoBarCancel")); 326 } 327 328 void MaybeShowOpenPDFInReaderPrompt(WebContents* web_contents, 329 const AdobeReaderPluginInfo& reader_info) { 330 // If the Reader plugin is disabled by policy, don't prompt them. 331 if (!reader_info.is_installed || !reader_info.is_enabled) 332 return; 333 334 scoped_ptr<OpenPDFInReaderPromptDelegate> prompt( 335 new PDFUnsupportedFeaturePromptDelegate(web_contents, reader_info)); 336 PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents); 337 pdf_tab_helper->ShowOpenInReaderPrompt(prompt.Pass()); 338 } 339 340 void GotPluginsCallback(int process_id, 341 int routing_id, 342 const AdobeReaderPluginInfo& reader_info) { 343 WebContents* web_contents = 344 tab_util::GetWebContentsByID(process_id, routing_id); 345 if (web_contents) 346 MaybeShowOpenPDFInReaderPrompt(web_contents, reader_info); 347 } 348 349 } // namespace 350 #endif // defined(OS_WIN) 351 352 void PDFHasUnsupportedFeature(WebContents* web_contents) { 353 #if defined(OS_WIN) 354 // Only works for Windows for now. For Mac, we'll have to launch the file 355 // externally since Adobe Reader doesn't work inside Chrome. 356 Profile* profile = 357 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 358 AdobeReaderPluginInfo reader_info; 359 if (GetAdobeReaderPluginInfo(profile, &reader_info)) { 360 MaybeShowOpenPDFInReaderPrompt(web_contents, reader_info); 361 return; 362 } 363 GetAdobeReaderPluginInfoAsync( 364 profile, 365 base::Bind(&GotPluginsCallback, 366 web_contents->GetRenderProcessHost()->GetID(), 367 web_contents->GetRenderViewHost()->GetRoutingID())); 368 #endif // defined(OS_WIN) 369 } 370