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_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/user_metrics.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/common/page_transition_types.h"
36 #include "grit/browser_resources.h"
37 #include "grit/generated_resources.h"
38 #include "grit/theme_resources.h"
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/base/resource/resource_bundle.h"
41 #include "ui/base/webui/jstemplate_builder.h"
42 #include "ui/gfx/image/image.h"
45 #include "base/win/metro.h"
48 using base::UserMetricsAction
;
49 using content::InterstitialPage
;
50 using content::OpenURLParams
;
51 using content::PluginService
;
52 using content::Referrer
;
53 using content::WebContents
;
54 using content::WebPluginInfo
;
58 const char kAdobeReaderUpdateUrl
[] = "http://www.adobe.com/go/getreader_chrome";
60 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
61 const char kAdobeReaderIdentifier
[] = "adobe-reader";
64 // The prompt delegate used to ask the user if they want to use Adobe Reader
66 class PDFEnableAdobeReaderPromptDelegate
67 : public OpenPDFInReaderPromptDelegate
{
69 explicit PDFEnableAdobeReaderPromptDelegate(Profile
* profile
);
70 virtual ~PDFEnableAdobeReaderPromptDelegate();
72 // OpenPDFInReaderPromptDelegate
73 virtual base::string16
GetMessageText() const OVERRIDE
;
74 virtual base::string16
GetAcceptButtonText() const OVERRIDE
;
75 virtual base::string16
GetCancelButtonText() const OVERRIDE
;
76 virtual bool ShouldExpire(
77 const content::LoadCommittedDetails
& details
) const OVERRIDE
;
78 virtual void Accept() OVERRIDE
;
79 virtual void Cancel() OVERRIDE
;
87 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptDelegate
);
90 PDFEnableAdobeReaderPromptDelegate::PDFEnableAdobeReaderPromptDelegate(
93 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown"));
96 PDFEnableAdobeReaderPromptDelegate::~PDFEnableAdobeReaderPromptDelegate() {
99 bool PDFEnableAdobeReaderPromptDelegate::ShouldExpire(
100 const content::LoadCommittedDetails
& details
) const {
101 content::PageTransition transition
=
102 content::PageTransitionStripQualifier(details
.entry
->GetTransitionType());
103 // We don't want to expire on a reload, because that is how we open the PDF in
105 return !details
.is_in_page
&& transition
!= content::PAGE_TRANSITION_RELOAD
;
108 void PDFEnableAdobeReaderPromptDelegate::Accept() {
109 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarOK"));
110 PluginPrefs
* plugin_prefs
= PluginPrefs::GetForProfile(profile_
).get();
111 plugin_prefs
->EnablePluginGroup(
112 true, base::ASCIIToUTF16(PluginMetadata::kAdobeReaderGroupName
));
113 plugin_prefs
->EnablePluginGroup(
114 false, base::ASCIIToUTF16(ChromeContentClient::kPDFPluginName
));
117 void PDFEnableAdobeReaderPromptDelegate::Cancel() {
118 content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel"));
121 base::string16
PDFEnableAdobeReaderPromptDelegate::GetAcceptButtonText() const {
122 return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON
);
125 base::string16
PDFEnableAdobeReaderPromptDelegate::GetCancelButtonText() const {
126 return l10n_util::GetStringUTF16(IDS_DONE
);
129 base::string16
PDFEnableAdobeReaderPromptDelegate::GetMessageText() const {
130 return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER
);
133 // Launch the url to get the latest Adbobe Reader installer.
134 void OpenReaderUpdateURL(WebContents
* web_contents
) {
135 OpenURLParams
params(
136 GURL(kAdobeReaderUpdateUrl
), Referrer(), NEW_FOREGROUND_TAB
,
137 content::PAGE_TRANSITION_LINK
, false);
138 web_contents
->OpenURL(params
);
141 // Opens the PDF using Adobe Reader.
142 void OpenUsingReader(WebContents
* web_contents
,
143 const WebPluginInfo
& reader_plugin
,
144 OpenPDFInReaderPromptDelegate
* delegate
) {
145 ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
146 web_contents
->GetRenderProcessHost()->GetID(),
147 web_contents
->GetMainFrame()->GetRoutingID(),
148 web_contents
->GetURL(),
150 web_contents
->GetRenderViewHost()->ReloadFrame();
152 PDFTabHelper
* pdf_tab_helper
= PDFTabHelper::FromWebContents(web_contents
);
154 pdf_tab_helper
->ShowOpenInReaderPrompt(make_scoped_ptr(delegate
));
157 // An interstitial to be used when the user chooses to open a PDF using Adobe
158 // Reader, but it is out of date.
159 class PDFUnsupportedFeatureInterstitial
160 : public content::InterstitialPageDelegate
{
162 PDFUnsupportedFeatureInterstitial(
163 WebContents
* web_contents
,
164 const WebPluginInfo
& reader_webplugininfo
)
165 : web_contents_(web_contents
),
166 reader_webplugininfo_(reader_webplugininfo
) {
167 content::RecordAction(UserMetricsAction("PDF_ReaderInterstitialShown"));
168 interstitial_page_
= InterstitialPage::Create(
169 web_contents
, false, web_contents
->GetURL(), this);
170 interstitial_page_
->Show();
174 // InterstitialPageDelegate implementation.
175 virtual std::string
GetHTMLContents() OVERRIDE
{
176 base::DictionaryValue strings
;
179 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_TITLE
));
182 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_BODY
));
185 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_UPDATE
));
188 l10n_util::GetStringUTF16(
189 IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_PROCEED
));
192 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_OK
));
195 l10n_util::GetStringUTF16(IDS_READER_OUT_OF_DATE_BLOCKING_PAGE_CANCEL
));
197 base::StringPiece
html(ResourceBundle::GetSharedInstance().
198 GetRawDataResource(IDR_READER_OUT_OF_DATE_HTML
));
200 return webui::GetI18nTemplateHtml(html
, &strings
);
203 virtual void CommandReceived(const std::string
& command
) OVERRIDE
{
204 if (command
== "0") {
205 content::RecordAction(
206 UserMetricsAction("PDF_ReaderInterstitialCancel"));
207 interstitial_page_
->DontProceed();
211 if (command
== "1") {
212 content::RecordAction(
213 UserMetricsAction("PDF_ReaderInterstitialUpdate"));
214 OpenReaderUpdateURL(web_contents_
);
215 } else if (command
== "2") {
216 content::RecordAction(
217 UserMetricsAction("PDF_ReaderInterstitialIgnore"));
218 // Pretend that the plug-in is up-to-date so that we don't block it.
219 reader_webplugininfo_
.version
= base::ASCIIToUTF16("11.0.0.0");
220 OpenUsingReader(web_contents_
, reader_webplugininfo_
, NULL
);
224 interstitial_page_
->Proceed();
227 virtual void OverrideRendererPrefs(
228 content::RendererPreferences
* prefs
) OVERRIDE
{
230 Profile::FromBrowserContext(web_contents_
->GetBrowserContext());
231 renderer_preferences_util::UpdateFromSystemSettings(prefs
, profile
);
235 WebContents
* web_contents_
;
236 WebPluginInfo reader_webplugininfo_
;
237 InterstitialPage
* interstitial_page_
; // Owns us.
239 DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial
);
242 // The delegate for the bubble used to inform the user that we don't support a
243 // feature in the PDF.
244 class PDFUnsupportedFeaturePromptDelegate
245 : public OpenPDFInReaderPromptDelegate
{
247 // |reader| is NULL if Adobe Reader isn't installed.
248 PDFUnsupportedFeaturePromptDelegate(WebContents
* web_contents
,
249 const content::WebPluginInfo
* reader
,
250 PluginFinder
* plugin_finder
);
251 virtual ~PDFUnsupportedFeaturePromptDelegate();
253 // OpenPDFInReaderPromptDelegate:
254 virtual base::string16
GetMessageText() const OVERRIDE
;
255 virtual base::string16
GetAcceptButtonText() const OVERRIDE
;
256 virtual base::string16
GetCancelButtonText() const OVERRIDE
;
257 virtual bool ShouldExpire(
258 const content::LoadCommittedDetails
& details
) const OVERRIDE
;
259 virtual void Accept() OVERRIDE
;
260 virtual void Cancel() OVERRIDE
;
263 WebContents
* web_contents_
;
264 bool reader_installed_
;
265 bool reader_vulnerable_
;
266 WebPluginInfo reader_webplugininfo_
;
268 DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptDelegate
);
271 PDFUnsupportedFeaturePromptDelegate::PDFUnsupportedFeaturePromptDelegate(
272 WebContents
* web_contents
,
273 const content::WebPluginInfo
* reader
,
274 PluginFinder
* plugin_finder
)
275 : web_contents_(web_contents
),
276 reader_installed_(!!reader
),
277 reader_vulnerable_(false) {
278 if (!reader_installed_
) {
279 content::RecordAction(
280 UserMetricsAction("PDF_InstallReaderInfoBarShown"));
284 content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown"));
285 reader_webplugininfo_
= *reader
;
287 #if defined(ENABLE_PLUGIN_INSTALLATION)
288 scoped_ptr
<PluginMetadata
> plugin_metadata(
289 plugin_finder
->GetPluginMetadata(reader_webplugininfo_
));
291 reader_vulnerable_
= plugin_metadata
->GetSecurityStatus(*reader
) !=
292 PluginMetadata::SECURITY_STATUS_UP_TO_DATE
;
298 PDFUnsupportedFeaturePromptDelegate::~PDFUnsupportedFeaturePromptDelegate() {
301 base::string16
PDFUnsupportedFeaturePromptDelegate::GetMessageText() const {
302 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE
);
305 base::string16
PDFUnsupportedFeaturePromptDelegate::GetAcceptButtonText()
308 if (base::win::IsMetroProcess())
309 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK
);
312 if (reader_installed_
)
313 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_OPEN_IN_READER_LINK
);
315 return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_INSTALL_READER_LINK
);
318 base::string16
PDFUnsupportedFeaturePromptDelegate::GetCancelButtonText()
320 return l10n_util::GetStringUTF16(IDS_DONE
);
323 bool PDFUnsupportedFeaturePromptDelegate::ShouldExpire(
324 const content::LoadCommittedDetails
& details
) const {
325 return !details
.is_in_page
;
328 void PDFUnsupportedFeaturePromptDelegate::Accept() {
330 if (base::win::IsMetroProcess()) {
331 chrome::AttemptRestartWithModeSwitch();
336 if (!reader_installed_
) {
337 content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK"));
338 OpenReaderUpdateURL(web_contents_
);
342 content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK"));
344 if (reader_vulnerable_
) {
345 new PDFUnsupportedFeatureInterstitial(web_contents_
, reader_webplugininfo_
);
350 Profile::FromBrowserContext(web_contents_
->GetBrowserContext());
351 OpenPDFInReaderPromptDelegate
* delegate
=
352 new PDFEnableAdobeReaderPromptDelegate(profile
);
354 OpenUsingReader(web_contents_
, reader_webplugininfo_
, delegate
);
357 void PDFUnsupportedFeaturePromptDelegate::Cancel() {
358 content::RecordAction(reader_installed_
?
359 UserMetricsAction("PDF_UseReaderInfoBarCancel") :
360 UserMetricsAction("PDF_InstallReaderInfoBarCancel"));
363 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
364 void GotPluginsCallback(int process_id
,
366 const std::vector
<content::WebPluginInfo
>& plugins
) {
367 WebContents
* web_contents
=
368 tab_util::GetWebContentsByID(process_id
, routing_id
);
372 const content::WebPluginInfo
* reader
= NULL
;
373 PluginFinder
* plugin_finder
= PluginFinder::GetInstance();
374 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
375 scoped_ptr
<PluginMetadata
> plugin_metadata(
376 plugin_finder
->GetPluginMetadata(plugins
[i
]));
377 if (plugin_metadata
->identifier() != kAdobeReaderIdentifier
)
381 reader
= &plugins
[i
];
382 // If the Reader plugin is disabled by policy, don't prompt them.
384 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
385 PluginPrefs
* plugin_prefs
= PluginPrefs::GetForProfile(profile
);
386 if (plugin_prefs
->PolicyStatusForPlugin(plugin_metadata
->name()) ==
387 PluginPrefs::POLICY_DISABLED
) {
393 scoped_ptr
<OpenPDFInReaderPromptDelegate
> prompt(
394 new PDFUnsupportedFeaturePromptDelegate(
395 web_contents
, reader
, plugin_finder
));
396 PDFTabHelper
* pdf_tab_helper
= PDFTabHelper::FromWebContents(web_contents
);
397 pdf_tab_helper
->ShowOpenInReaderPrompt(prompt
.Pass());
399 #endif // defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
403 void PDFHasUnsupportedFeature(content::WebContents
* web_contents
) {
404 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
405 // Only works for Windows for now. For Mac, we'll have to launch the file
406 // externally since Adobe Reader doesn't work inside Chrome.
407 PluginService::GetInstance()->GetPlugins(base::Bind(&GotPluginsCallback
,
408 web_contents
->GetRenderProcessHost()->GetID(),
409 web_contents
->GetRenderViewHost()->GetRoutingID()));