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