Home | History | Annotate | Download | only in extensions
      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 
    124     PromptType type() const { return 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.
    165     const extensions::BundleInstaller* bundle() const { return bundle_; }
    166     void set_bundle(const extensions::BundleInstaller* bundle) {
    167       bundle_ = bundle;
    168     }
    169 
    170     // Populated for all other types.
    171     const extensions::Extension* extension() const { return extension_; }
    172     void set_extension(const extensions::Extension* extension) {
    173       extension_ = extension;
    174     }
    175 
    176     // May be populated for POST_INSTALL_PERMISSIONS_PROMPT.
    177     void set_retained_files(const std::vector<base::FilePath>& retained_files) {
    178       retained_files_ = retained_files;
    179     }
    180 
    181     const gfx::Image& icon() const { return icon_; }
    182     void set_icon(const gfx::Image& icon) { icon_ = icon; }
    183 
    184     bool has_webstore_data() const { return has_webstore_data_; }
    185 
    186     const ExtensionInstallPromptExperiment* experiment() const {
    187       return experiment_.get();
    188     }
    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:
    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 
    317   ExtensionInstallUI* install_ui() const { return install_ui_.get(); }
    318 
    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 
    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