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