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.
5 #include "chrome/browser/ui/pdf/pdf_unsupported_feature.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/base/webui/jstemplate_builder.h"
41 #include "ui/gfx/image/image.h"
44 #include "base/win/metro.h"
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;
57 const char kAdobeReaderUpdateUrl[] = "http://www.adobe.com/go/getreader_chrome";
59 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
60 const char kAdobeReaderIdentifier[] = "adobe-reader";
63 // The prompt delegate used to ask the user if they want to use Adobe Reader
65 class PDFEnableAdobeReaderPromptDelegate
66 : public OpenPDFInReaderPromptDelegate {
68 explicit PDFEnableAdobeReaderPromptDelegate(Profile* profile);
69 virtual ~PDFEnableAdobeReaderPromptDelegate();
71 // OpenPDFInReaderPromptDelegate
72 virtual string16 GetMessageText() const OVERRIDE;
73 virtual string16 GetAcceptButtonText() const OVERRIDE;
74 virtual string16 GetCancelButtonText() const OVERRIDE;
75 virtual bool ShouldExpire(
76 const content::LoadCommittedDetails& details) const OVERRIDE;
77 virtual void Accept() OVERRIDE;
78 virtual void Cancel() OVERRIDE;
86 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptDelegate);
89 PDFEnableAdobeReaderPromptDelegate::PDFEnableAdobeReaderPromptDelegate(
92 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown"));
95 PDFEnableAdobeReaderPromptDelegate::~PDFEnableAdobeReaderPromptDelegate() {
98 bool PDFEnableAdobeReaderPromptDelegate::ShouldExpire(
99 const content::LoadCommittedDetails& details) const {
100 content::PageTransition transition =
101 content::PageTransitionStripQualifier(details.entry->GetTransitionType());
102 // We don't want to expire on a reload, because that is how we open the PDF in
104 return !details.is_in_page && transition != content::PAGE_TRANSITION_RELOAD;
107 void PDFEnableAdobeReaderPromptDelegate::Accept() {
108 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarOK"));
109 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get();
110 plugin_prefs->EnablePluginGroup(
111 true, ASCIIToUTF16(PluginMetadata::kAdobeReaderGroupName));
112 plugin_prefs->EnablePluginGroup(
113 false, ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName));
116 void PDFEnableAdobeReaderPromptDelegate::Cancel() {
117 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel"));
120 string16 PDFEnableAdobeReaderPromptDelegate::GetAcceptButtonText() const {
121 return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON);
124 string16 PDFEnableAdobeReaderPromptDelegate::GetCancelButtonText() const {
125 return l10n_util::GetStringUTF16(IDS_DONE);
128 string16 PDFEnableAdobeReaderPromptDelegate::GetMessageText() const {
129 return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER);
132 // Launch the url to get the latest Adbobe Reader installer.
133 void OpenReaderUpdateURL(WebContents* web_contents) {
134 OpenURLParams params(
135 GURL(kAdobeReaderUpdateUrl), Referrer(), NEW_FOREGROUND_TAB,
136 content::PAGE_TRANSITION_LINK, false);
137 web_contents->OpenURL(params);
140 // Opens the PDF using Adobe Reader.
141 void OpenUsingReader(WebContents* web_contents,
142 const WebPluginInfo& reader_plugin,
143 OpenPDFInReaderPromptDelegate* delegate) {
144 ChromePluginServiceFilter::GetInstance()->OverridePluginForTab(
145 web_contents->GetRenderProcessHost()->GetID(),
146 web_contents->GetRenderViewHost()->GetRoutingID(),
147 web_contents->GetURL(),
149 web_contents->GetRenderViewHost()->ReloadFrame();
151 PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
153 pdf_tab_helper->ShowOpenInReaderPrompt(make_scoped_ptr(delegate));
156 // An interstitial to be used when the user chooses to open a PDF using Adobe
157 // Reader, but it is out of date.
158 class PDFUnsupportedFeatureInterstitial
159 : public content::InterstitialPageDelegate {
161 PDFUnsupportedFeatureInterstitial(
162 WebContents* web_contents,
163 const WebPluginInfo& reader_webplugininfo)
164 : web_contents_(web_contents),
165 reader_webplugininfo_(reader_webplugininfo) {
166 content::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown"));
167 interstitial_page_ = InterstitialPage::Create(
168 web_contents, false, web_contents->GetURL(), this);
169 interstitial_page_->Show();
173 // InterstitialPageDelegate implementation.
174 virtual std::string GetHTMLContents() OVERRIDE {
175 DictionaryValue strings;
178 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE));
181 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY));
184 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE));
187 l10n_util::GetStringUTF16(
188 IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED));
191 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK));
194 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL));
196 base::StringPiece html(ResourceBundle::GetSharedInstance().
197 GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML));
199 return webui::GetI18nTemplateHtml(html, &strings);
202 virtual void CommandReceived(const std::string& command) OVERRIDE {
203 if (command == "0") {
204 content::RecordAction(
205 UserMetricsAction("PDF_ReaderInterstitialCancel"));
206 interstitial_page_->DontProceed();
210 if (command == "1") {
211 content::RecordAction(
212 UserMetricsAction("PDF_ReaderInterstitialUpdate"));
213 OpenReaderUpdateURL(web_contents_);
214 } else if (command == "2") {
215 content::RecordAction(
216 UserMetricsAction("PDF_ReaderInterstitialIgnore"));
217 // Pretend that the plug-in is up-to-date so that we don't block it.
218 reader_webplugininfo_.version = ASCIIToUTF16("11.0.0.0");
219 OpenUsingReader(web_contents_, reader_webplugininfo_, NULL);
223 interstitial_page_->Proceed();
226 virtual void OverrideRendererPrefs(
227 content::RendererPreferences* prefs) OVERRIDE {
229 Profile::FromBrowserContext(web_contents_->GetBrowserContext());
230 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
234 WebContents* web_contents_;
235 WebPluginInfo reader_webplugininfo_;
236 InterstitialPage* interstitial_page_; // Owns us.
238 DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial);
241 // The delegate for the bubble used to inform the user that we don't support a
242 // feature in the PDF.
243 class PDFUnsupportedFeaturePromptDelegate
244 : public OpenPDFInReaderPromptDelegate {
246 // |reader| is NULL if Adobe Reader isn't installed.
247 PDFUnsupportedFeaturePromptDelegate(WebContents* web_contents,
248 const content::WebPluginInfo* reader,
249 PluginFinder* plugin_finder);
250 virtual ~PDFUnsupportedFeaturePromptDelegate();
252 // OpenPDFInReaderPromptDelegate:
253 virtual string16 GetMessageText() const OVERRIDE;
254 virtual string16 GetAcceptButtonText() const OVERRIDE;
255 virtual string16 GetCancelButtonText() const OVERRIDE;
256 virtual bool ShouldExpire(
257 const content::LoadCommittedDetails& details) const OVERRIDE;
258 virtual void Accept() OVERRIDE;
259 virtual void Cancel() OVERRIDE;
262 WebContents* web_contents_;
263 bool reader_installed_;
264 bool reader_vulnerable_;
265 WebPluginInfo reader_webplugininfo_;
267 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptDelegate);
270 PDFUnsupportedFeaturePromptDelegate::PDFUnsupportedFeaturePromptDelegate(
271 WebContents* web_contents,
272 const content::WebPluginInfo* reader,
273 PluginFinder* plugin_finder)
274 : web_contents_(web_contents),
275 reader_installed_(!!reader),
276 reader_vulnerable_(false) {
277 if (!reader_installed_) {
278 content::RecordAction(
279 UserMetricsAction("PDF_InstallReaderInfoBarShown"));
283 content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown"));
284 reader_webplugininfo_ = *reader;
286 #if defined(ENABLE_PLUGIN_INSTALLATION)
287 scoped_ptr<PluginMetadata> plugin_metadata(
288 plugin_finder->GetPluginMetadata(reader_webplugininfo_));
290 reader_vulnerable_ = plugin_metadata->GetSecurityStatus(*reader) !=
291 PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
297 PDFUnsupportedFeaturePromptDelegate::~PDFUnsupportedFeaturePromptDelegate() {
300 string16 PDFUnsupportedFeaturePromptDelegate::GetMessageText() const {
301 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE);
304 string16 PDFUnsupportedFeaturePromptDelegate::GetAcceptButtonText() const {
306 if (base::win::IsMetroProcess())
307 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK);
310 if (reader_installed_)
311 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_OPEN_IN_READER_LINK);
313 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_INSTALL_READER_LINK);
316 string16 PDFUnsupportedFeaturePromptDelegate::GetCancelButtonText() const {
317 return l10n_util::GetStringUTF16(IDS_DONE);
320 bool PDFUnsupportedFeaturePromptDelegate::ShouldExpire(
321 const content::LoadCommittedDetails& details) const {
322 return !details.is_in_page;
325 void PDFUnsupportedFeaturePromptDelegate::Accept() {
327 if (base::win::IsMetroProcess()) {
328 chrome::AttemptRestartWithModeSwitch();
333 if (!reader_installed_) {
334 content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK"));
335 OpenReaderUpdateURL(web_contents_);
339 content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK"));
341 if (reader_vulnerable_) {
342 new PDFUnsupportedFeatureInterstitial(web_contents_, reader_webplugininfo_);
347 Profile::FromBrowserContext(web_contents_->GetBrowserContext());
348 OpenPDFInReaderPromptDelegate* delegate =
349 new PDFEnableAdobeReaderPromptDelegate(profile);
351 OpenUsingReader(web_contents_, reader_webplugininfo_, delegate);
354 void PDFUnsupportedFeaturePromptDelegate::Cancel() {
355 content::RecordAction(reader_installed_ ?
356 UserMetricsAction("PDF_UseReaderInfoBarCancel") :
357 UserMetricsAction("PDF_InstallReaderInfoBarCancel"));
360 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
361 void GotPluginsCallback(int process_id,
363 const std::vector<content::WebPluginInfo>& plugins) {
364 WebContents* web_contents =
365 tab_util::GetWebContentsByID(process_id, routing_id);
369 const content::WebPluginInfo* reader = NULL;
370 PluginFinder* plugin_finder = PluginFinder::GetInstance();
371 for (size_t i = 0; i < plugins.size(); ++i) {
372 scoped_ptr<PluginMetadata> plugin_metadata(
373 plugin_finder->GetPluginMetadata(plugins[i]));
374 if (plugin_metadata->identifier() != kAdobeReaderIdentifier)
378 reader = &plugins[i];
379 // If the Reader plugin is disabled by policy, don't prompt them.
381 Profile::FromBrowserContext(web_contents->GetBrowserContext());
382 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile);
383 if (plugin_prefs->PolicyStatusForPlugin(plugin_metadata->name()) ==
384 PluginPrefs::POLICY_DISABLED) {
390 scoped_ptr<OpenPDFInReaderPromptDelegate> prompt(
391 new PDFUnsupportedFeaturePromptDelegate(
392 web_contents, reader, plugin_finder));
393 PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
394 pdf_tab_helper->ShowOpenInReaderPrompt(prompt.Pass());
396 #endif // defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
400 void PDFHasUnsupportedFeature(content::WebContents* web_contents) {
401 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
402 // Only works for Windows for now. For Mac, we'll have to launch the file
403 // externally since Adobe Reader doesn't work inside Chrome.
404 PluginService::GetInstance()->GetPlugins(base::Bind(&GotPluginsCallback,
405 web_contents->GetRenderProcessHost()->GetID(),
406 web_contents->GetRenderViewHost()->GetRoutingID()));