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 type()96 PromptType type() const { return type_; } set_type(PromptType 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. bundle()135 const extensions::BundleInstaller* bundle() const { return bundle_; } set_bundle(const extensions::BundleInstaller * bundle)136 void set_bundle(const extensions::BundleInstaller* bundle) { 137 bundle_ = bundle; 138 } 139 140 // Populated for all other types. extension()141 const extensions::Extension* extension() const { return extension_; } set_extension(const extensions::Extension * extension)142 void set_extension(const extensions::Extension* extension) { 143 extension_ = extension; 144 } 145 146 // May be populated for POST_INSTALL_PERMISSIONS_PROMPT. set_retained_files(const std::vector<base::FilePath> & retained_files)147 void set_retained_files(const std::vector<base::FilePath>& retained_files) { 148 retained_files_ = retained_files; 149 } 150 icon()151 const gfx::Image& icon() const { return icon_; } set_icon(const gfx::Image & 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: ~Delegate()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 install_ui()257 ExtensionInstallUI* install_ui() const { return install_ui_.get(); } 258 record_oauth2_grant()259 bool record_oauth2_grant() const { return record_oauth2_grant_; } 260 parent_web_contents()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