• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_PROMPT_H_
6 #define CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_PROMPT_H_
7 
8 #include <string>
9 #include <vector>
10 
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/strings/string16.h"
18 #include "chrome/browser/extensions/crx_installer_error.h"
19 #include "chrome/browser/extensions/extension_install_prompt_experiment.h"
20 #include "extensions/common/url_pattern.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "ui/gfx/image/image.h"
23 #include "ui/gfx/image/image_skia.h"
24 #include "ui/gfx/native_widget_types.h"
25 
26 class Browser;
27 class ExtensionInstallUI;
28 class Profile;
29 
30 namespace base {
31 class DictionaryValue;
32 class MessageLoop;
33 }  // namespace base
34 
35 namespace content {
36 class PageNavigator;
37 class WebContents;
38 }
39 
40 namespace extensions {
41 class BundleInstaller;
42 class Extension;
43 class ExtensionWebstorePrivateApiTest;
44 class MockGetAuthTokenFunction;
45 class PermissionSet;
46 }  // namespace extensions
47 
48 namespace infobars {
49 class InfoBarDelegate;
50 }
51 
52 // Displays all the UI around extension installation.
53 class ExtensionInstallPrompt
54     : public base::SupportsWeakPtr<ExtensionInstallPrompt> {
55  public:
56   // A setting to cause extension/app installs from the webstore skip the normal
57   // confirmation dialog. This should only be used in tests.
58   enum AutoConfirmForTests {
59     NONE,    // The prompt will show normally.
60     ACCEPT,  // The prompt will always accept.
61     CANCEL,  // The prompt will always cancel.
62   };
63   static AutoConfirmForTests g_auto_confirm_for_tests;
64 
65   // This enum is associated with Extensions.InstallPrompt_Type UMA histogram.
66   // Do not modify existing values and add new values only to the end.
67   enum PromptType {
68     UNSET_PROMPT_TYPE = -1,
69     INSTALL_PROMPT = 0,
70     INLINE_INSTALL_PROMPT,
71     BUNDLE_INSTALL_PROMPT,
72     RE_ENABLE_PROMPT,
73     PERMISSIONS_PROMPT,
74     EXTERNAL_INSTALL_PROMPT,
75     POST_INSTALL_PERMISSIONS_PROMPT,
76     LAUNCH_PROMPT,
77     REMOTE_INSTALL_PROMPT,
78     REPAIR_PROMPT,
79     NUM_PROMPT_TYPES
80   };
81 
82   // Enumeration for permissions and retained files details.
83   enum DetailsType {
84     PERMISSIONS_DETAILS = 0,
85     WITHHELD_PERMISSIONS_DETAILS,
86     RETAINED_FILES_DETAILS,
87   };
88 
89   // This enum is used to differentiate regular and withheld permissions for
90   // segregation in the install prompt.
91   enum PermissionsType {
92     REGULAR_PERMISSIONS = 0,
93     WITHHELD_PERMISSIONS,
94     ALL_PERMISSIONS,
95   };
96 
97   static std::string PromptTypeToString(PromptType type);
98 
99   // Extra information needed to display an installation or uninstallation
100   // prompt. Gets populated with raw data and exposes getters for formatted
101   // strings so that the GTK/views/Cocoa install dialogs don't have to repeat
102   // that logic.
103   // Ref-counted because we pass around the prompt independent of the full
104   // ExtensionInstallPrompt.
105   class Prompt : public base::RefCountedThreadSafe<Prompt> {
106    public:
107     explicit Prompt(PromptType type);
108 
109     // Sets the permission list for this prompt.
110     void SetPermissions(const std::vector<base::string16>& permissions,
111                         PermissionsType permissions_type);
112     // Sets the permission list details for this prompt.
113     void SetPermissionsDetails(const std::vector<base::string16>& details,
114                                PermissionsType permissions_type);
115     void SetIsShowingDetails(DetailsType type,
116                              size_t index,
117                              bool is_showing_details);
118     void SetWebstoreData(const std::string& localized_user_count,
119                          bool show_user_count,
120                          double average_rating,
121                          int rating_count);
122     void SetUserNameFromProfile(Profile* profile);
123 
type()124     PromptType type() const { return type_; }
set_type(PromptType type)125     void set_type(PromptType type) { type_ = type; }
126 
127     // Getters for UI element labels.
128     base::string16 GetDialogTitle() const;
129     base::string16 GetHeading() const;
130     int GetDialogButtons() const;
131     bool HasAcceptButtonLabel() const;
132     base::string16 GetAcceptButtonLabel() const;
133     bool HasAbortButtonLabel() const;
134     base::string16 GetAbortButtonLabel() const;
135     base::string16 GetPermissionsHeading(
136         PermissionsType permissions_type) const;
137     base::string16 GetRetainedFilesHeading() const;
138 
139     bool ShouldShowPermissions() const;
140     bool ShouldShowExplanationText() const;
141 
142     // Getters for webstore metadata. Only populated when the type is
143     // INLINE_INSTALL_PROMPT.
144 
145     // The star display logic replicates the one used by the webstore (from
146     // components.ratingutils.setFractionalYellowStars). Callers pass in an
147     // "appender", which will be repeatedly called back with the star images
148     // that they append to the star display area.
149     typedef void(*StarAppender)(const gfx::ImageSkia*, void*);
150     void AppendRatingStars(StarAppender appender, void* data) const;
151     base::string16 GetRatingCount() const;
152     base::string16 GetUserCount() const;
153     size_t GetPermissionCount(PermissionsType permissions_type) const;
154     size_t GetPermissionsDetailsCount(PermissionsType permissions_type) const;
155     base::string16 GetPermission(size_t index,
156                                  PermissionsType permissions_type) const;
157     base::string16 GetPermissionsDetails(
158         size_t index,
159         PermissionsType permissions_type) const;
160     bool GetIsShowingDetails(DetailsType type, size_t index) const;
161     size_t GetRetainedFileCount() const;
162     base::string16 GetRetainedFile(size_t index) const;
163 
164     // Populated for BUNDLE_INSTALL_PROMPT.
bundle()165     const extensions::BundleInstaller* bundle() const { return bundle_; }
set_bundle(const extensions::BundleInstaller * bundle)166     void set_bundle(const extensions::BundleInstaller* bundle) {
167       bundle_ = bundle;
168     }
169 
170     // Populated for all other types.
extension()171     const extensions::Extension* extension() const { return extension_; }
set_extension(const extensions::Extension * extension)172     void set_extension(const extensions::Extension* extension) {
173       extension_ = extension;
174     }
175 
176     // May be populated for POST_INSTALL_PERMISSIONS_PROMPT.
set_retained_files(const std::vector<base::FilePath> & retained_files)177     void set_retained_files(const std::vector<base::FilePath>& retained_files) {
178       retained_files_ = retained_files;
179     }
180 
icon()181     const gfx::Image& icon() const { return icon_; }
set_icon(const gfx::Image & icon)182     void set_icon(const gfx::Image& icon) { icon_ = icon; }
183 
has_webstore_data()184     bool has_webstore_data() const { return has_webstore_data_; }
185 
experiment()186     const ExtensionInstallPromptExperiment* experiment() const {
187       return experiment_.get();
188     }
set_experiment(ExtensionInstallPromptExperiment * experiment)189     void set_experiment(ExtensionInstallPromptExperiment* experiment) {
190       experiment_ = experiment;
191     }
192 
193    private:
194     friend class base::RefCountedThreadSafe<Prompt>;
195 
196     struct InstallPromptPermissions {
197       InstallPromptPermissions();
198       ~InstallPromptPermissions();
199 
200       std::vector<base::string16> permissions;
201       std::vector<base::string16> details;
202       std::vector<bool> is_showing_details;
203     };
204 
205     virtual ~Prompt();
206 
207     // Returns the InstallPromptPermissions corresponding to
208     // |permissions_type|.
209     InstallPromptPermissions& GetPermissionsForType(
210         PermissionsType permissions_type);
211     const InstallPromptPermissions& GetPermissionsForType(
212         PermissionsType permissions_type) const;
213 
214     bool ShouldDisplayRevokeFilesButton() const;
215 
216     PromptType type_;
217 
218     // Permissions that are being requested (may not be all of an extension's
219     // permissions if only additional ones are being requested)
220     InstallPromptPermissions prompt_permissions_;
221     // Permissions that will be withheld upon install.
222     InstallPromptPermissions withheld_prompt_permissions_;
223 
224     bool is_showing_details_for_retained_files_;
225 
226     // The extension or bundle being installed.
227     const extensions::Extension* extension_;
228     const extensions::BundleInstaller* bundle_;
229 
230     // The icon to be displayed.
231     gfx::Image icon_;
232 
233     // These fields are populated only when the prompt type is
234     // INLINE_INSTALL_PROMPT
235     // Already formatted to be locale-specific.
236     std::string localized_user_count_;
237     // Range is kMinExtensionRating to kMaxExtensionRating
238     double average_rating_;
239     int rating_count_;
240 
241     // Whether we should display the user count (we anticipate this will be
242     // false if localized_user_count_ represents the number zero).
243     bool show_user_count_;
244 
245     // Whether or not this prompt has been populated with data from the
246     // webstore.
247     bool has_webstore_data_;
248 
249     std::vector<base::FilePath> retained_files_;
250 
251     scoped_refptr<ExtensionInstallPromptExperiment> experiment_;
252 
253     DISALLOW_COPY_AND_ASSIGN(Prompt);
254   };
255 
256   static const int kMinExtensionRating = 0;
257   static const int kMaxExtensionRating = 5;
258 
259   class Delegate {
260    public:
261     // We call this method to signal that the installation should continue.
262     virtual void InstallUIProceed() = 0;
263 
264     // We call this method to signal that the installation should stop, with
265     // |user_initiated| true if the installation was stopped by the user.
266     virtual void InstallUIAbort(bool user_initiated) = 0;
267 
268    protected:
~Delegate()269     virtual ~Delegate() {}
270   };
271 
272   // Parameters to show a prompt dialog. Two sets of the
273   // parameters are supported: either use a parent WebContents or use a
274   // parent NativeWindow + a PageNavigator.
275   struct ShowParams {
276     explicit ShowParams(content::WebContents* contents);
277     ShowParams(gfx::NativeWindow window, content::PageNavigator* navigator);
278 
279     // Parent web contents of the install UI dialog. This can be NULL.
280     content::WebContents* parent_web_contents;
281 
282     // NativeWindow parent and navigator. If initialized using a parent web
283     // contents, these are derived from it.
284     gfx::NativeWindow parent_window;
285     content::PageNavigator* navigator;
286   };
287 
288   typedef base::Callback<void(const ExtensionInstallPrompt::ShowParams&,
289                               ExtensionInstallPrompt::Delegate*,
290                               scoped_refptr<ExtensionInstallPrompt::Prompt>)>
291       ShowDialogCallback;
292 
293   // Callback to show the default extension install dialog.
294   // The implementations of this function are platform-specific.
295   static ShowDialogCallback GetDefaultShowDialogCallback();
296 
297   // Creates a dummy extension from the |manifest|, replacing the name and
298   // description with the localizations if provided.
299   static scoped_refptr<extensions::Extension> GetLocalizedExtensionForDisplay(
300       const base::DictionaryValue* manifest,
301       int flags,  // Extension::InitFromValueFlags
302       const std::string& id,
303       const std::string& localized_name,
304       const std::string& localized_description,
305       std::string* error);
306 
307   // Creates a prompt with a parent web content.
308   explicit ExtensionInstallPrompt(content::WebContents* contents);
309 
310   // Creates a prompt with a profile, a native window and a page navigator.
311   ExtensionInstallPrompt(Profile* profile,
312                          gfx::NativeWindow native_window,
313                          content::PageNavigator* navigator);
314 
315   virtual ~ExtensionInstallPrompt();
316 
install_ui()317   ExtensionInstallUI* install_ui() const { return install_ui_.get(); }
318 
parent_web_contents()319   content::WebContents* parent_web_contents() const {
320     return show_params_.parent_web_contents;
321   }
322 
323   // This is called by the bundle installer to verify whether the bundle
324   // should be installed.
325   //
326   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
327   virtual void ConfirmBundleInstall(
328       extensions::BundleInstaller* bundle,
329       const extensions::PermissionSet* permissions);
330 
331   // This is called by the standalone installer to verify whether the install
332   // from the webstore should proceed.
333   //
334   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
335   virtual void ConfirmStandaloneInstall(Delegate* delegate,
336                                         const extensions::Extension* extension,
337                                         SkBitmap* icon,
338                                         scoped_refptr<Prompt> prompt);
339 
340   // This is called by the installer to verify whether the installation from
341   // the webstore should proceed. |show_dialog_callback| is optional and can be
342   // NULL.
343   //
344   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
345   virtual void ConfirmWebstoreInstall(
346       Delegate* delegate,
347       const extensions::Extension* extension,
348       const SkBitmap* icon,
349       const ShowDialogCallback& show_dialog_callback);
350 
351   // This is called by the installer to verify whether the installation should
352   // proceed. This is declared virtual for testing. |show_dialog_callback| is
353   // optional and can be NULL.
354   //
355   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
356   virtual void ConfirmInstall(Delegate* delegate,
357                               const extensions::Extension* extension,
358                               const ShowDialogCallback& show_dialog_callback);
359 
360   // This is called by the app handler launcher to verify whether the app
361   // should be re-enabled. This is declared virtual for testing.
362   //
363   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
364   virtual void ConfirmReEnable(Delegate* delegate,
365                                const extensions::Extension* extension);
366 
367   // This is called by the external install alert UI to verify whether the
368   // extension should be enabled (external extensions are installed disabled).
369   //
370   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
371   virtual void ConfirmExternalInstall(
372       Delegate* delegate,
373       const extensions::Extension* extension,
374       const ShowDialogCallback& show_dialog_callback,
375       scoped_refptr<Prompt> prompt);
376 
377   // This is called by the extension permissions API to verify whether an
378   // extension may be granted additional permissions.
379   //
380   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
381   virtual void ConfirmPermissions(Delegate* delegate,
382                                   const extensions::Extension* extension,
383                                   const extensions::PermissionSet* permissions);
384 
385   // This is called by the app handler launcher to review what permissions the
386   // extension or app currently has.
387   //
388   // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
389   virtual void ReviewPermissions(
390       Delegate* delegate,
391       const extensions::Extension* extension,
392       const std::vector<base::FilePath>& retained_file_paths);
393 
394   // Installation was successful. This is declared virtual for testing.
395   virtual void OnInstallSuccess(const extensions::Extension* extension,
396                                 SkBitmap* icon);
397 
398   // Installation failed. This is declared virtual for testing.
399   virtual void OnInstallFailure(const extensions::CrxInstallerError& error);
400 
set_callback_for_test(const ShowDialogCallback & show_dialog_callback)401   void set_callback_for_test(const ShowDialogCallback& show_dialog_callback) {
402     show_dialog_callback_ = show_dialog_callback;
403   }
404 
405  protected:
406   friend class extensions::ExtensionWebstorePrivateApiTest;
407   friend class WebstoreStartupInstallUnpackFailureTest;
408 
409   // Whether or not we should record the oauth2 grant upon successful install.
410   bool record_oauth2_grant_;
411 
412  private:
413   friend class GalleryInstallApiTestObserver;
414 
415   // Sets the icon that will be used in any UI. If |icon| is NULL, or contains
416   // an empty bitmap, then a default icon will be used instead.
417   void SetIcon(const SkBitmap* icon);
418 
419   // ImageLoader callback.
420   void OnImageLoaded(const gfx::Image& image);
421 
422   // Starts the process of showing a confirmation UI, which is split into two.
423   // 1) Set off a 'load icon' task.
424   // 2) Handle the load icon response and show the UI (OnImageLoaded).
425   void LoadImageIfNeeded();
426 
427   // Shows the actual UI (the icon should already be loaded).
428   void ShowConfirmation();
429 
430   base::MessageLoop* ui_loop_;
431 
432   // The extensions installation icon.
433   SkBitmap icon_;
434 
435   // The extension we are showing the UI for, if type is not
436   // BUNDLE_INSTALL_PROMPT.
437   const extensions::Extension* extension_;
438 
439   // The bundle we are showing the UI for, if type BUNDLE_INSTALL_PROMPT.
440   const extensions::BundleInstaller* bundle_;
441 
442   // A custom set of permissions to show in the install prompt instead of the
443   // extension's active permissions.
444   scoped_refptr<const extensions::PermissionSet> custom_permissions_;
445 
446   // The object responsible for doing the UI specific actions.
447   scoped_ptr<ExtensionInstallUI> install_ui_;
448 
449   // Parameters to show the confirmation UI.
450   ShowParams show_params_;
451 
452   // The delegate we will call Proceed/Abort on after confirmation UI.
453   Delegate* delegate_;
454 
455   // A pre-filled prompt.
456   scoped_refptr<Prompt> prompt_;
457 
458   // Used to show the confirm dialog.
459   ShowDialogCallback show_dialog_callback_;
460 };
461 
462 #endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_PROMPT_H_
463