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