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
26class Browser;
27class ExtensionInstallUI;
28class Profile;
29
30namespace base {
31class DictionaryValue;
32class MessageLoop;
33}  // namespace base
34
35namespace content {
36class PageNavigator;
37class WebContents;
38}
39
40namespace extensions {
41class BundleInstaller;
42class Extension;
43class ExtensionWebstorePrivateApiTest;
44class MockGetAuthTokenFunction;
45class PermissionSet;
46}  // namespace extensions
47
48namespace infobars {
49class InfoBarDelegate;
50}
51
52// Displays all the UI around extension installation.
53class 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