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