1 // Copyright (c) 2011 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/pdf_unsupported_feature.h" 6 7 #include "base/utf_string_conversions.h" 8 #include "base/values.h" 9 #include "base/version.h" 10 #include "chrome/browser/metrics/user_metrics.h" 11 #include "chrome/browser/prefs/pref_service.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h" 14 #include "chrome/common/chrome_content_client.h" 15 #include "chrome/common/jstemplate_builder.h" 16 #include "chrome/common/pref_names.h" 17 #include "content/browser/plugin_service.h" 18 #include "content/browser/renderer_host/render_process_host.h" 19 #include "content/browser/renderer_host/render_view_host.h" 20 #include "content/browser/tab_contents/interstitial_page.h" 21 #include "content/browser/tab_contents/tab_contents.h" 22 #include "grit/browser_resources.h" 23 #include "grit/generated_resources.h" 24 #include "ui/base/l10n/l10n_util.h" 25 #include "ui/base/resource/resource_bundle.h" 26 #include "webkit/plugins/npapi/plugin_group.h" 27 #include "webkit/plugins/npapi/plugin_list.h" 28 #include "webkit/plugins/npapi/webplugininfo.h" 29 30 using webkit::npapi::PluginGroup; 31 using webkit::npapi::PluginList; 32 using webkit::npapi::WebPluginInfo; 33 34 namespace { 35 36 // Only launch Adobe Reader X or later. 37 static const uint16 kMinReaderVersionToUse = 10; 38 39 static const char kReaderUpdateUrl[] = 40 "http://www.adobe.com/go/getreader_chrome"; 41 42 // The info bar delegate used to ask the user if they want to use Adobe Reader 43 // by default. We want the infobar to have [No][Yes], so we swap the text on 44 // the buttons, and the meaning of the delegate callbacks. 45 class PDFEnableAdobeReaderConfirmInfoBarDelegate 46 : public ConfirmInfoBarDelegate { 47 public: 48 PDFEnableAdobeReaderConfirmInfoBarDelegate( 49 TabContents* tab_contents) 50 : ConfirmInfoBarDelegate(tab_contents), 51 tab_contents_(tab_contents) { 52 UserMetrics::RecordAction( 53 UserMetricsAction("PDF_EnableReaderInfoBarShown")); 54 } 55 56 // ConfirmInfoBarDelegate 57 virtual void InfoBarClosed() { 58 delete this; 59 } 60 61 virtual void InfoBarDismissed() { 62 OnNo(); 63 } 64 65 virtual Type GetInfoBarType() const { 66 return PAGE_ACTION_TYPE; 67 } 68 69 virtual bool Accept() { 70 tab_contents_->profile()->GetPrefs()->SetBoolean( 71 prefs::kPluginsShowSetReaderDefaultInfobar, false); 72 return OnNo(); 73 } 74 75 virtual bool Cancel() { 76 return OnYes(); 77 } 78 79 virtual int GetButtons() const { 80 return BUTTON_OK | BUTTON_CANCEL; 81 } 82 83 virtual string16 GetButtonLabel(InfoBarButton button) const { 84 switch (button) { 85 case BUTTON_OK: 86 return l10n_util::GetStringUTF16( 87 IDS_PDF_INFOBAR_NEVER_USE_READER_BUTTON); 88 case BUTTON_CANCEL: 89 return l10n_util::GetStringUTF16( 90 IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON); 91 default: 92 // All buttons are labeled above. 93 NOTREACHED() << "Bad button id " << button; 94 return string16(); 95 } 96 } 97 98 virtual string16 GetMessageText() const { 99 return l10n_util::GetStringUTF16( 100 IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER); 101 } 102 103 private: 104 bool OnYes() { 105 UserMetrics::RecordAction( 106 UserMetricsAction("PDF_EnableReaderInfoBarOK")); 107 webkit::npapi::PluginList::Singleton()->EnableGroup( 108 false, ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName)); 109 webkit::npapi::PluginList::Singleton()->EnableGroup( 110 true, ASCIIToUTF16(webkit::npapi::PluginGroup::kAdobeReaderGroupName)); 111 return true; 112 } 113 114 bool OnNo() { 115 UserMetrics::RecordAction( 116 UserMetricsAction("PDF_EnableReaderInfoBarCancel")); 117 return true; 118 } 119 120 TabContents* tab_contents_; 121 122 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderConfirmInfoBarDelegate); 123 }; 124 125 // Launch the url to get the latest Adbobe Reader installer. 126 void OpenReaderUpdateURL(TabContents* tab) { 127 tab->OpenURL(GURL(kReaderUpdateUrl), GURL(), CURRENT_TAB, 128 PageTransition::LINK); 129 } 130 131 // Opens the PDF using Adobe Reader. 132 void OpenUsingReader(TabContents* tab, 133 const WebPluginInfo& reader_plugin, 134 InfoBarDelegate* old_delegate, 135 InfoBarDelegate* new_delegate) { 136 PluginService::OverriddenPlugin plugin; 137 plugin.render_process_id = tab->GetRenderProcessHost()->id(); 138 plugin.render_view_id = tab->render_view_host()->routing_id(); 139 plugin.url = tab->GetURL(); 140 plugin.plugin = reader_plugin; 141 // The plugin is disabled, so enable it to get around the renderer check. 142 // Also give it a new version so that the renderer doesn't show the blocked 143 // plugin UI if it's vulnerable, since we already went through the 144 // interstitial. 145 plugin.plugin.enabled = WebPluginInfo::USER_ENABLED; 146 plugin.plugin.version = ASCIIToUTF16("11.0.0.0"); 147 148 PluginService::GetInstance()->OverridePluginForTab(plugin); 149 tab->render_view_host()->ReloadFrame(); 150 151 if (new_delegate) { 152 if (old_delegate) { 153 tab->ReplaceInfoBar(old_delegate, new_delegate); 154 } else { 155 tab->AddInfoBar(new_delegate); 156 } 157 } 158 } 159 160 // An interstitial to be used when the user chooses to open a PDF using Adobe 161 // Reader, but it is out of date. 162 class PDFUnsupportedFeatureInterstitial : public InterstitialPage { 163 public: 164 PDFUnsupportedFeatureInterstitial( 165 TabContents* tab, 166 const WebPluginInfo& reader_webplugininfo) 167 : InterstitialPage(tab, false, tab->GetURL()), 168 reader_webplugininfo_(reader_webplugininfo) { 169 UserMetrics::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown")); 170 } 171 172 protected: 173 // InterstitialPage implementation. 174 virtual std::string GetHTMLContents() { 175 DictionaryValue strings; 176 strings.SetString( 177 "title", 178 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE)); 179 strings.SetString( 180 "headLine", 181 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY)); 182 strings.SetString( 183 "update", 184 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE)); 185 strings.SetString( 186 "open_with_reader", 187 l10n_util::GetStringUTF16( 188 IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED)); 189 strings.SetString( 190 "ok", 191 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK)); 192 strings.SetString( 193 "cancel", 194 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL)); 195 196 base::StringPiece html(ResourceBundle::GetSharedInstance(). 197 GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML)); 198 199 return jstemplate_builder::GetI18nTemplateHtml(html, &strings); 200 } 201 202 virtual void CommandReceived(const std::string& command) { 203 if (command == "0") { 204 UserMetrics::RecordAction( 205 UserMetricsAction("PDF_ReaderInterstitialCancel")); 206 DontProceed(); 207 return; 208 } 209 210 if (command == "1") { 211 UserMetrics::RecordAction( 212 UserMetricsAction("PDF_ReaderInterstitialUpdate")); 213 OpenReaderUpdateURL(tab()); 214 } else if (command == "2") { 215 UserMetrics::RecordAction( 216 UserMetricsAction("PDF_ReaderInterstitialIgnore")); 217 OpenUsingReader(tab(), reader_webplugininfo_, NULL, NULL); 218 } else { 219 NOTREACHED(); 220 } 221 Proceed(); 222 } 223 224 private: 225 WebPluginInfo reader_webplugininfo_; 226 227 DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial); 228 }; 229 230 // The info bar delegate used to inform the user that we don't support a feature 231 // in the PDF. See the comment about how we swap buttons for 232 // PDFEnableAdobeReaderConfirmInfoBarDelegate. 233 class PDFUnsupportedFeatureConfirmInfoBarDelegate 234 : public ConfirmInfoBarDelegate { 235 public: 236 PDFUnsupportedFeatureConfirmInfoBarDelegate( 237 TabContents* tab_contents, 238 PluginGroup* reader_group) // NULL if Adobe Reader isn't installed. 239 : ConfirmInfoBarDelegate(tab_contents), 240 tab_contents_(tab_contents), 241 reader_installed_(!!reader_group), 242 reader_vulnerable_(false) { 243 if (reader_installed_) { 244 UserMetrics::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown")); 245 std::vector<WebPluginInfo> plugins = reader_group->web_plugin_infos(); 246 DCHECK_EQ(plugins.size(), 1u); 247 reader_webplugininfo_ = plugins[0]; 248 249 reader_vulnerable_ = reader_group->IsVulnerable(); 250 if (!reader_vulnerable_) { 251 scoped_ptr<Version> version(PluginGroup::CreateVersionFromString( 252 reader_webplugininfo_.version)); 253 if (version.get()) { 254 if (version->components()[0] < kMinReaderVersionToUse) 255 reader_vulnerable_ = true; 256 } 257 } 258 } else { 259 UserMetrics::RecordAction( 260 UserMetricsAction("PDF_InstallReaderInfoBarShown")); 261 } 262 } 263 264 // ConfirmInfoBarDelegate 265 virtual void InfoBarClosed() { 266 delete this; 267 } 268 269 virtual void InfoBarDismissed() { 270 OnNo(); 271 } 272 273 virtual Type GetInfoBarType() const { 274 return PAGE_ACTION_TYPE; 275 } 276 277 virtual bool Accept() { 278 return OnNo(); 279 } 280 281 virtual bool Cancel() { 282 return OnYes(); 283 } 284 285 virtual int GetButtons() const { 286 return BUTTON_OK | BUTTON_CANCEL; 287 } 288 289 virtual string16 GetButtonLabel(InfoBarButton button) const { 290 switch (button) { 291 case BUTTON_OK: 292 return l10n_util::GetStringUTF16( 293 IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL); 294 case BUTTON_CANCEL: 295 return l10n_util::GetStringUTF16( 296 IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL); 297 default: 298 // All buttons are labeled above. 299 NOTREACHED() << "Bad button id " << button; 300 return string16(); 301 } 302 } 303 304 virtual string16 GetMessageText() const { 305 return l10n_util::GetStringUTF16(reader_installed_ ? 306 IDS_PDF_INFOBAR_QUESTION_READER_INSTALLED : 307 IDS_PDF_INFOBAR_QUESTION_READER_NOT_INSTALLED); 308 } 309 310 private: 311 bool OnYes() { 312 if (!reader_installed_) { 313 UserMetrics::RecordAction( 314 UserMetricsAction("PDF_InstallReaderInfoBarOK")); 315 OpenReaderUpdateURL(tab_contents_); 316 return true; 317 } 318 319 UserMetrics::RecordAction( 320 UserMetricsAction("PDF_UseReaderInfoBarOK")); 321 322 if (reader_vulnerable_) { 323 PDFUnsupportedFeatureInterstitial* interstitial = new 324 PDFUnsupportedFeatureInterstitial( 325 tab_contents_, reader_webplugininfo_); 326 interstitial->Show(); 327 return true; 328 } 329 330 InfoBarDelegate* bar = NULL; 331 if (tab_contents_->profile()->GetPrefs()->GetBoolean( 332 prefs::kPluginsShowSetReaderDefaultInfobar)) { 333 bar = new PDFEnableAdobeReaderConfirmInfoBarDelegate(tab_contents_); 334 } 335 336 if (bar) { 337 OpenUsingReader(tab_contents_, reader_webplugininfo_, this, bar); 338 return false; 339 } else { 340 OpenUsingReader(tab_contents_, reader_webplugininfo_, NULL, NULL); 341 return true; 342 } 343 } 344 345 bool OnNo() { 346 if (reader_installed_) { 347 UserMetrics::RecordAction( 348 UserMetricsAction("PDF_UseReaderInfoBarCancel")); 349 } else { 350 UserMetrics::RecordAction( 351 UserMetricsAction("PDF_InstallReaderInfoBarCancel")); 352 } 353 return true; 354 } 355 356 TabContents* tab_contents_; 357 bool reader_installed_; 358 bool reader_vulnerable_; 359 WebPluginInfo reader_webplugininfo_; 360 361 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeatureConfirmInfoBarDelegate); 362 }; 363 364 } // namespace 365 366 void PDFHasUnsupportedFeature(TabContents* tab) { 367 #if !defined(OS_WIN) 368 // Only works for Windows for now. For Mac, we'll have to launch the file 369 // externally since Adobe Reader doesn't work inside Chrome. 370 return; 371 #endif 372 string16 reader_group_name(ASCIIToUTF16(PluginGroup::kAdobeReaderGroupName)); 373 374 // If the Reader plugin is disabled by policy, don't prompt them. 375 if (PluginGroup::IsPluginNameDisabledByPolicy(reader_group_name)) 376 return; 377 378 PluginGroup* reader_group = NULL; 379 std::vector<PluginGroup> plugin_groups; 380 PluginList::Singleton()->GetPluginGroups( 381 false, &plugin_groups); 382 for (size_t i = 0; i < plugin_groups.size(); ++i) { 383 if (plugin_groups[i].GetGroupName() == reader_group_name) { 384 reader_group = &plugin_groups[i]; 385 break; 386 } 387 } 388 389 tab->AddInfoBar(new PDFUnsupportedFeatureConfirmInfoBarDelegate( 390 tab, reader_group)); 391 } 392