- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / pdf / pdf_unsupported_feature.cc
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/base/webui/jstemplate_builder.h"
41 #include "ui/gfx/image/image.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 const char kAdobeReaderUpdateUrl[] = "http://www.adobe.com/go/getreader_chrome";
58
59 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
60 const char kAdobeReaderIdentifier[] = "adobe-reader";
61 #endif
62
63 // The prompt delegate used to ask the user if they want to use Adobe Reader
64 // by default.
65 class PDFEnableAdobeReaderPromptDelegate
66     : public OpenPDFInReaderPromptDelegate {
67  public:
68   explicit PDFEnableAdobeReaderPromptDelegate(Profile* profile);
69   virtual ~PDFEnableAdobeReaderPromptDelegate();
70
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;
79
80  private:
81   void OnYes();
82   void OnNo();
83
84   Profile* profile_;
85
86   DISALLOW_IMPLICIT_CONSTRUCTORS(PDFEnableAdobeReaderPromptDelegate);
87 };
88
89 PDFEnableAdobeReaderPromptDelegate::PDFEnableAdobeReaderPromptDelegate(
90     Profile* profile)
91     : profile_(profile) {
92   content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarShown"));
93 }
94
95 PDFEnableAdobeReaderPromptDelegate::~PDFEnableAdobeReaderPromptDelegate() {
96 }
97
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
103   // Reader.
104   return !details.is_in_page && transition != content::PAGE_TRANSITION_RELOAD;
105 }
106
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));
114 }
115
116 void PDFEnableAdobeReaderPromptDelegate::Cancel() {
117   content::RecordAction(UserMetricsAction("PDF_EnableReaderInfoBarCancel"));
118 }
119
120 string16 PDFEnableAdobeReaderPromptDelegate::GetAcceptButtonText() const {
121   return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON);
122 }
123
124 string16 PDFEnableAdobeReaderPromptDelegate::GetCancelButtonText() const {
125   return l10n_util::GetStringUTF16(IDS_DONE);
126 }
127
128 string16 PDFEnableAdobeReaderPromptDelegate::GetMessageText() const {
129   return l10n_util::GetStringUTF16(IDS_PDF_INFOBAR_QUESTION_ALWAYS_USE_READER);
130 }
131
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);
138 }
139
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(),
148       reader_plugin);
149   web_contents->GetRenderViewHost()->ReloadFrame();
150
151   PDFTabHelper* pdf_tab_helper = PDFTabHelper::FromWebContents(web_contents);
152   if (delegate)
153     pdf_tab_helper->ShowOpenInReaderPrompt(make_scoped_ptr(delegate));
154 }
155
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 {
160  public:
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();
170   }
171
172  protected:
173   // InterstitialPageDelegate implementation.
174   virtual std::string GetHTMLContents() OVERRIDE {
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 webui::GetI18nTemplateHtml(html, &strings);
200   }
201
202   virtual void CommandReceived(const std::string& command) OVERRIDE {
203     if (command == "0") {
204       content::RecordAction(
205           UserMetricsAction("PDF_ReaderInterstitialCancel"));
206       interstitial_page_->DontProceed();
207       return;
208     }
209
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);
220     } else {
221       NOTREACHED();
222     }
223     interstitial_page_->Proceed();
224   }
225
226   virtual void OverrideRendererPrefs(
227       content::RendererPreferences* prefs) OVERRIDE {
228     Profile* profile =
229         Profile::FromBrowserContext(web_contents_->GetBrowserContext());
230     renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
231   }
232
233  private:
234   WebContents* web_contents_;
235   WebPluginInfo reader_webplugininfo_;
236   InterstitialPage* interstitial_page_;  // Owns us.
237
238   DISALLOW_COPY_AND_ASSIGN(PDFUnsupportedFeatureInterstitial);
239 };
240
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 {
245  public:
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();
251
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;
260
261  private:
262   WebContents* web_contents_;
263   bool reader_installed_;
264   bool reader_vulnerable_;
265   WebPluginInfo reader_webplugininfo_;
266
267   DISALLOW_IMPLICIT_CONSTRUCTORS(PDFUnsupportedFeaturePromptDelegate);
268 };
269
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"));
280     return;
281   }
282
283   content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarShown"));
284   reader_webplugininfo_ = *reader;
285
286 #if defined(ENABLE_PLUGIN_INSTALLATION)
287   scoped_ptr<PluginMetadata> plugin_metadata(
288       plugin_finder->GetPluginMetadata(reader_webplugininfo_));
289
290   reader_vulnerable_ = plugin_metadata->GetSecurityStatus(*reader) !=
291                        PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
292 #else
293   NOTREACHED();
294 #endif
295 }
296
297 PDFUnsupportedFeaturePromptDelegate::~PDFUnsupportedFeaturePromptDelegate() {
298 }
299
300 string16 PDFUnsupportedFeaturePromptDelegate::GetMessageText() const {
301   return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_MESSAGE);
302 }
303
304 string16 PDFUnsupportedFeaturePromptDelegate::GetAcceptButtonText() const {
305 #if defined(OS_WIN)
306   if (base::win::IsMetroProcess())
307     return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_METRO_MODE_LINK);
308 #endif
309
310   if (reader_installed_)
311     return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_OPEN_IN_READER_LINK);
312
313   return l10n_util::GetStringUTF16(IDS_PDF_BUBBLE_INSTALL_READER_LINK);
314 }
315
316 string16 PDFUnsupportedFeaturePromptDelegate::GetCancelButtonText() const {
317   return l10n_util::GetStringUTF16(IDS_DONE);
318 }
319
320 bool PDFUnsupportedFeaturePromptDelegate::ShouldExpire(
321     const content::LoadCommittedDetails& details) const {
322   return !details.is_in_page;
323 }
324
325 void PDFUnsupportedFeaturePromptDelegate::Accept() {
326 #if defined(OS_WIN)
327   if (base::win::IsMetroProcess()) {
328     chrome::AttemptRestartWithModeSwitch();
329     return;
330   }
331 #endif
332
333   if (!reader_installed_) {
334     content::RecordAction(UserMetricsAction("PDF_InstallReaderInfoBarOK"));
335     OpenReaderUpdateURL(web_contents_);
336     return;
337   }
338
339   content::RecordAction(UserMetricsAction("PDF_UseReaderInfoBarOK"));
340
341   if (reader_vulnerable_) {
342     new PDFUnsupportedFeatureInterstitial(web_contents_, reader_webplugininfo_);
343     return;
344   }
345
346   Profile* profile =
347       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
348   OpenPDFInReaderPromptDelegate* delegate =
349       new PDFEnableAdobeReaderPromptDelegate(profile);
350
351   OpenUsingReader(web_contents_, reader_webplugininfo_, delegate);
352 }
353
354 void PDFUnsupportedFeaturePromptDelegate::Cancel() {
355   content::RecordAction(reader_installed_ ?
356                         UserMetricsAction("PDF_UseReaderInfoBarCancel") :
357                         UserMetricsAction("PDF_InstallReaderInfoBarCancel"));
358 }
359
360 #if defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
361 void GotPluginsCallback(int process_id,
362                         int routing_id,
363                         const std::vector<content::WebPluginInfo>& plugins) {
364   WebContents* web_contents =
365       tab_util::GetWebContentsByID(process_id, routing_id);
366   if (!web_contents)
367     return;
368
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)
375       continue;
376
377     DCHECK(!reader);
378     reader = &plugins[i];
379     // If the Reader plugin is disabled by policy, don't prompt them.
380     Profile* profile =
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) {
385       return;
386     }
387     break;
388   }
389
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());
395 }
396 #endif  // defined(OS_WIN) && defined(ENABLE_PLUGIN_INSTALLATION)
397
398 }  // namespace
399
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()));
407 #endif
408 }