• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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