Make default apps cache multiprofile friendly
[chromium-blink-merge.git] / chrome / browser / extensions / external_install_ui.cc
blobd2771943e6da391566e271701d14dd8df5aa8690
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.
5 #include "chrome/browser/extensions/external_install_ui.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/lazy_instance.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/app/chrome_command_ids.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/extensions/extension_install_prompt.h"
19 #include "chrome/browser/extensions/extension_install_ui.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_finder.h"
25 #include "chrome/browser/ui/global_error/global_error.h"
26 #include "chrome/browser/ui/global_error/global_error_service.h"
27 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
28 #include "chrome/browser/ui/host_desktop.h"
29 #include "chrome/common/extensions/extension.h"
30 #include "chrome/common/extensions/extension_constants.h"
31 #include "chrome/common/extensions/manifest_url_handler.h"
32 #include "content/public/browser/notification_details.h"
33 #include "content/public/browser/notification_observer.h"
34 #include "content/public/browser/notification_registrar.h"
35 #include "content/public/browser/notification_source.h"
36 #include "grit/chromium_strings.h"
37 #include "grit/generated_resources.h"
38 #include "grit/theme_resources.h"
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/gfx/image/image.h"
41 #include "ui/gfx/image/image_skia_operations.h"
42 #include "ui/gfx/size.h"
44 namespace extensions {
46 namespace {
48 // Whether the external extension can use the streamlined bubble install flow.
49 bool UseBubbleInstall(const Extension* extension, bool is_new_profile) {
50 return ManifestURL::UpdatesFromGallery(extension) && !is_new_profile;
53 } // namespace
55 static const int kMenuCommandId = IDC_EXTERNAL_EXTENSION_ALERT;
57 class ExternalInstallGlobalError;
59 // TODO(mpcomplete): Get rid of the refcounting on this class, or document
60 // why it's necessary. Will do after refactoring to merge back with
61 // ExtensionDisabledDialogDelegate.
62 class ExternalInstallDialogDelegate
63 : public ExtensionInstallPrompt::Delegate,
64 public base::RefCountedThreadSafe<ExternalInstallDialogDelegate> {
65 public:
66 ExternalInstallDialogDelegate(Browser* browser,
67 ExtensionService* service,
68 const Extension* extension,
69 bool use_global_error);
71 Browser* browser() { return browser_; }
73 private:
74 friend class base::RefCountedThreadSafe<ExternalInstallDialogDelegate>;
75 friend class ExternalInstallGlobalError;
77 virtual ~ExternalInstallDialogDelegate();
79 // ExtensionInstallPrompt::Delegate:
80 virtual void InstallUIProceed() OVERRIDE;
81 virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
83 // The UI for showing the install dialog when enabling.
84 scoped_ptr<ExtensionInstallPrompt> install_ui_;
86 Browser* browser_;
87 base::WeakPtr<ExtensionService> service_weak_;
88 const std::string extension_id_;
91 // Only shows a menu item, no bubble. Clicking the menu item shows
92 // an external install dialog.
93 class ExternalInstallMenuAlert : public GlobalError,
94 public content::NotificationObserver {
95 public:
96 ExternalInstallMenuAlert(ExtensionService* service,
97 const Extension* extension);
98 virtual ~ExternalInstallMenuAlert();
100 const Extension* extension() const { return extension_; }
102 // GlobalError implementation.
103 virtual Severity GetSeverity() OVERRIDE;
104 virtual bool HasMenuItem() OVERRIDE;
105 virtual int MenuItemCommandID() OVERRIDE;
106 virtual string16 MenuItemLabel() OVERRIDE;
107 virtual void ExecuteMenuItem(Browser* browser) OVERRIDE;
108 virtual bool HasBubbleView() OVERRIDE;
109 virtual string16 GetBubbleViewTitle() OVERRIDE;
110 virtual std::vector<string16> GetBubbleViewMessages() OVERRIDE;
111 virtual string16 GetBubbleViewAcceptButtonLabel() OVERRIDE;
112 virtual string16 GetBubbleViewCancelButtonLabel() OVERRIDE;
113 virtual void OnBubbleViewDidClose(Browser* browser) OVERRIDE;
114 virtual void BubbleViewAcceptButtonPressed(Browser* browser) OVERRIDE;
115 virtual void BubbleViewCancelButtonPressed(Browser* browser) OVERRIDE;
117 // content::NotificationObserver implementation.
118 virtual void Observe(int type,
119 const content::NotificationSource& source,
120 const content::NotificationDetails& details) OVERRIDE;
122 protected:
123 ExtensionService* service_;
124 const Extension* extension_;
125 content::NotificationRegistrar registrar_;
128 // Shows a menu item and a global error bubble, replacing the install dialog.
129 class ExternalInstallGlobalError : public ExternalInstallMenuAlert {
130 public:
131 ExternalInstallGlobalError(ExtensionService* service,
132 const Extension* extension,
133 ExternalInstallDialogDelegate* delegate,
134 const ExtensionInstallPrompt::Prompt& prompt);
135 virtual ~ExternalInstallGlobalError();
137 virtual void ExecuteMenuItem(Browser* browser) OVERRIDE;
138 virtual bool HasBubbleView() OVERRIDE;
139 virtual gfx::Image GetBubbleViewIcon() OVERRIDE;
140 virtual string16 GetBubbleViewTitle() OVERRIDE;
141 virtual std::vector<string16> GetBubbleViewMessages() OVERRIDE;
142 virtual string16 GetBubbleViewAcceptButtonLabel() OVERRIDE;
143 virtual string16 GetBubbleViewCancelButtonLabel() OVERRIDE;
144 virtual void OnBubbleViewDidClose(Browser* browser) OVERRIDE;
145 virtual void BubbleViewAcceptButtonPressed(Browser* browser) OVERRIDE;
146 virtual void BubbleViewCancelButtonPressed(Browser* browser) OVERRIDE;
148 protected:
149 // Ref-counted, but needs to be disposed of if we are dismissed without
150 // having been clicked (perhaps because the user enabled the extension
151 // manually).
152 ExternalInstallDialogDelegate* delegate_;
153 const ExtensionInstallPrompt::Prompt* prompt_;
156 static void CreateExternalInstallGlobalError(
157 base::WeakPtr<ExtensionService> service,
158 const std::string& extension_id,
159 const ExtensionInstallPrompt::ShowParams& show_params,
160 ExtensionInstallPrompt::Delegate* prompt_delegate,
161 const ExtensionInstallPrompt::Prompt& prompt) {
162 if (!service.get())
163 return;
164 const Extension* extension = service->GetInstalledExtension(extension_id);
165 if (!extension)
166 return;
167 GlobalErrorService* error_service =
168 GlobalErrorServiceFactory::GetForProfile(service->profile());
169 if (error_service->GetGlobalErrorByMenuItemCommandID(kMenuCommandId))
170 return;
172 ExternalInstallDialogDelegate* delegate =
173 static_cast<ExternalInstallDialogDelegate*>(prompt_delegate);
174 ExternalInstallGlobalError* error_bubble = new ExternalInstallGlobalError(
175 service.get(), extension, delegate, prompt);
176 error_service->AddGlobalError(error_bubble);
177 // Show bubble immediately if possible.
178 if (delegate->browser())
179 error_bubble->ShowBubbleView(delegate->browser());
182 static void ShowExternalInstallDialog(
183 ExtensionService* service,
184 Browser* browser,
185 const Extension* extension) {
186 // This object manages its own lifetime.
187 new ExternalInstallDialogDelegate(browser, service, extension, false);
190 // ExternalInstallDialogDelegate --------------------------------------------
192 ExternalInstallDialogDelegate::ExternalInstallDialogDelegate(
193 Browser* browser,
194 ExtensionService* service,
195 const Extension* extension,
196 bool use_global_error)
197 : browser_(browser),
198 service_weak_(service->AsWeakPtr()),
199 extension_id_(extension->id()) {
200 AddRef(); // Balanced in Proceed or Abort.
202 install_ui_.reset(
203 ExtensionInstallUI::CreateInstallPromptWithBrowser(browser));
205 const ExtensionInstallPrompt::ShowDialogCallback callback =
206 use_global_error ?
207 base::Bind(&CreateExternalInstallGlobalError,
208 service_weak_, extension_id_) :
209 ExtensionInstallPrompt::GetDefaultShowDialogCallback();
210 install_ui_->ConfirmExternalInstall(this, extension, callback);
213 ExternalInstallDialogDelegate::~ExternalInstallDialogDelegate() {
216 void ExternalInstallDialogDelegate::InstallUIProceed() {
217 if (!service_weak_.get())
218 return;
219 const Extension* extension =
220 service_weak_->GetInstalledExtension(extension_id_);
221 if (!extension)
222 return;
223 service_weak_->GrantPermissionsAndEnableExtension(extension);
224 Release();
227 void ExternalInstallDialogDelegate::InstallUIAbort(bool user_initiated) {
228 if (!service_weak_.get())
229 return;
230 const Extension* extension =
231 service_weak_->GetInstalledExtension(extension_id_);
232 if (!extension)
233 return;
234 service_weak_->UninstallExtension(extension_id_, false, NULL);
235 Release();
238 // ExternalInstallMenuAlert -------------------------------------------------
240 ExternalInstallMenuAlert::ExternalInstallMenuAlert(
241 ExtensionService* service,
242 const Extension* extension)
243 : service_(service),
244 extension_(extension) {
245 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
246 content::Source<Profile>(service->profile()));
247 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_REMOVED,
248 content::Source<Profile>(service->profile()));
251 ExternalInstallMenuAlert::~ExternalInstallMenuAlert() {
254 GlobalError::Severity ExternalInstallMenuAlert::GetSeverity() {
255 return SEVERITY_LOW;
258 bool ExternalInstallMenuAlert::HasMenuItem() {
259 return true;
262 int ExternalInstallMenuAlert::MenuItemCommandID() {
263 return kMenuCommandId;
266 string16 ExternalInstallMenuAlert::MenuItemLabel() {
267 int id = -1;
268 if (extension_->is_app())
269 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_APP;
270 else if (extension_->is_theme())
271 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_THEME;
272 else
273 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_EXTENSION;
274 return l10n_util::GetStringFUTF16(id, UTF8ToUTF16(extension_->name()));
277 void ExternalInstallMenuAlert::ExecuteMenuItem(Browser* browser) {
278 ShowExternalInstallDialog(service_, browser, extension_);
281 bool ExternalInstallMenuAlert::HasBubbleView() {
282 return false;
284 string16 ExternalInstallMenuAlert::GetBubbleViewTitle() {
285 return string16();
288 std::vector<string16> ExternalInstallMenuAlert::GetBubbleViewMessages() {
289 return std::vector<string16>();
292 string16 ExternalInstallMenuAlert::GetBubbleViewAcceptButtonLabel() {
293 return string16();
296 string16 ExternalInstallMenuAlert::GetBubbleViewCancelButtonLabel() {
297 return string16();
300 void ExternalInstallMenuAlert::OnBubbleViewDidClose(Browser* browser) {
301 NOTREACHED();
304 void ExternalInstallMenuAlert::BubbleViewAcceptButtonPressed(
305 Browser* browser) {
306 NOTREACHED();
309 void ExternalInstallMenuAlert::BubbleViewCancelButtonPressed(
310 Browser* browser) {
311 NOTREACHED();
314 void ExternalInstallMenuAlert::Observe(
315 int type,
316 const content::NotificationSource& source,
317 const content::NotificationDetails& details) {
318 // The error is invalidated if the extension has been loaded or removed.
319 DCHECK(type == chrome::NOTIFICATION_EXTENSION_LOADED ||
320 type == chrome::NOTIFICATION_EXTENSION_REMOVED);
321 const Extension* extension = content::Details<const Extension>(details).ptr();
322 if (extension != extension_)
323 return;
324 GlobalErrorService* error_service =
325 GlobalErrorServiceFactory::GetForProfile(service_->profile());
326 error_service->RemoveGlobalError(this);
327 service_->AcknowledgeExternalExtension(extension_->id());
328 delete this;
331 // ExternalInstallGlobalError -----------------------------------------------
333 ExternalInstallGlobalError::ExternalInstallGlobalError(
334 ExtensionService* service,
335 const Extension* extension,
336 ExternalInstallDialogDelegate* delegate,
337 const ExtensionInstallPrompt::Prompt& prompt)
338 : ExternalInstallMenuAlert(service, extension),
339 delegate_(delegate),
340 prompt_(&prompt) {
343 ExternalInstallGlobalError::~ExternalInstallGlobalError() {
344 if (delegate_)
345 delegate_->Release();
348 void ExternalInstallGlobalError::ExecuteMenuItem(Browser* browser) {
349 ShowBubbleView(browser);
352 bool ExternalInstallGlobalError::HasBubbleView() {
353 return true;
356 gfx::Image ExternalInstallGlobalError::GetBubbleViewIcon() {
357 if (prompt_->icon().IsEmpty())
358 return GlobalError::GetBubbleViewIcon();
359 // Scale icon to a reasonable size.
360 return gfx::Image(gfx::ImageSkiaOperations::CreateResizedImage(
361 *prompt_->icon().ToImageSkia(),
362 skia::ImageOperations::RESIZE_BEST,
363 gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
364 extension_misc::EXTENSION_ICON_SMALL)));
367 string16 ExternalInstallGlobalError::GetBubbleViewTitle() {
368 return prompt_->GetDialogTitle();
371 std::vector<string16> ExternalInstallGlobalError::GetBubbleViewMessages() {
372 std::vector<string16> messages;
373 messages.push_back(prompt_->GetHeading());
374 if (prompt_->GetPermissionCount()) {
375 messages.push_back(prompt_->GetPermissionsHeading());
376 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) {
377 messages.push_back(l10n_util::GetStringFUTF16(
378 IDS_EXTENSION_PERMISSION_LINE,
379 prompt_->GetPermission(i)));
382 // TODO(yoz): OAuth issue advice?
383 return messages;
386 string16 ExternalInstallGlobalError::GetBubbleViewAcceptButtonLabel() {
387 return prompt_->GetAcceptButtonLabel();
390 string16 ExternalInstallGlobalError::GetBubbleViewCancelButtonLabel() {
391 return prompt_->GetAbortButtonLabel();
394 void ExternalInstallGlobalError::OnBubbleViewDidClose(Browser* browser) {
397 void ExternalInstallGlobalError::BubbleViewAcceptButtonPressed(
398 Browser* browser) {
399 ExternalInstallDialogDelegate* delegate = delegate_;
400 delegate_ = NULL;
401 delegate->InstallUIProceed();
404 void ExternalInstallGlobalError::BubbleViewCancelButtonPressed(
405 Browser* browser) {
406 ExternalInstallDialogDelegate* delegate = delegate_;
407 delegate_ = NULL;
408 delegate->InstallUIAbort(true);
411 // Public interface ---------------------------------------------------------
413 void AddExternalInstallError(ExtensionService* service,
414 const Extension* extension,
415 bool is_new_profile) {
416 GlobalErrorService* error_service =
417 GlobalErrorServiceFactory::GetForProfile(service->profile());
418 if (error_service->GetGlobalErrorByMenuItemCommandID(kMenuCommandId))
419 return;
421 if (UseBubbleInstall(extension, is_new_profile)) {
422 Browser* browser = NULL;
423 #if !defined(OS_ANDROID)
424 browser = chrome::FindTabbedBrowser(service->profile(),
425 true,
426 chrome::GetActiveDesktop());
427 #endif
428 new ExternalInstallDialogDelegate(browser, service, extension, true);
429 } else {
430 error_service->AddGlobalError(
431 new ExternalInstallMenuAlert(service, extension));
435 void RemoveExternalInstallError(ExtensionService* service) {
436 GlobalErrorService* error_service =
437 GlobalErrorServiceFactory::GetForProfile(service->profile());
438 GlobalError* error = error_service->GetGlobalErrorByMenuItemCommandID(
439 kMenuCommandId);
440 if (error) {
441 error_service->RemoveGlobalError(error);
442 delete error;
446 bool HasExternalInstallError(ExtensionService* service) {
447 GlobalErrorService* error_service =
448 GlobalErrorServiceFactory::GetForProfile(service->profile());
449 GlobalError* error = error_service->GetGlobalErrorByMenuItemCommandID(
450 kMenuCommandId);
451 return !!error;
454 bool HasExternalInstallBubble(ExtensionService* service) {
455 GlobalErrorService* error_service =
456 GlobalErrorServiceFactory::GetForProfile(service->profile());
457 GlobalError* error = error_service->GetGlobalErrorByMenuItemCommandID(
458 kMenuCommandId);
459 return error && error->HasBubbleView();
462 } // namespace extensions