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