Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_disabled_ui.cc
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 #include "chrome/browser/extensions/extension_disabled_ui.h"
6
7 #include <string>
8
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/extensions/extension_util.h"
23 #include "chrome/browser/extensions/image_loader.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/global_error/global_error.h"
27 #include "chrome/browser/ui/global_error/global_error_service.h"
28 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
29 #include "chrome/browser/ui/tabs/tab_strip_model.h"
30 #include "content/public/browser/notification_details.h"
31 #include "content/public/browser/notification_observer.h"
32 #include "content/public/browser/notification_registrar.h"
33 #include "content/public/browser/notification_source.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/extension_icon_set.h"
36 #include "extensions/common/manifest_handlers/icons_handler.h"
37 #include "extensions/common/permissions/permission_message_provider.h"
38 #include "extensions/common/permissions/permission_set.h"
39 #include "grit/chromium_strings.h"
40 #include "grit/generated_resources.h"
41 #include "grit/theme_resources.h"
42 #include "ui/base/l10n/l10n_util.h"
43 #include "ui/gfx/image/image.h"
44 #include "ui/gfx/image/image_skia_operations.h"
45 #include "ui/gfx/size.h"
46
47 using extensions::Extension;
48
49 namespace {
50
51 static const int kIconSize = extension_misc::EXTENSION_ICON_SMALL;
52
53 static base::LazyInstance<
54     std::bitset<IDC_EXTENSION_DISABLED_LAST -
55                 IDC_EXTENSION_DISABLED_FIRST + 1> >
56     menu_command_ids = LAZY_INSTANCE_INITIALIZER;
57
58 // Get an available menu ID.
59 int GetMenuCommandID() {
60   int id;
61   for (id = IDC_EXTENSION_DISABLED_FIRST;
62        id <= IDC_EXTENSION_DISABLED_LAST; ++id) {
63     if (!menu_command_ids.Get()[id - IDC_EXTENSION_DISABLED_FIRST]) {
64       menu_command_ids.Get().set(id - IDC_EXTENSION_DISABLED_FIRST);
65       return id;
66     }
67   }
68   // This should not happen.
69   DCHECK(id <= IDC_EXTENSION_DISABLED_LAST) <<
70       "No available menu command IDs for ExtensionDisabledGlobalError";
71   return IDC_EXTENSION_DISABLED_LAST;
72 }
73
74 // Make a menu ID available when it is no longer used.
75 void ReleaseMenuCommandID(int id) {
76   menu_command_ids.Get().reset(id - IDC_EXTENSION_DISABLED_FIRST);
77 }
78
79 }  // namespace
80
81 // ExtensionDisabledDialogDelegate --------------------------------------------
82
83 class ExtensionDisabledDialogDelegate
84     : public ExtensionInstallPrompt::Delegate,
85       public base::RefCountedThreadSafe<ExtensionDisabledDialogDelegate> {
86  public:
87   ExtensionDisabledDialogDelegate(ExtensionService* service,
88                                   scoped_ptr<ExtensionInstallPrompt> install_ui,
89                                   const Extension* extension);
90
91  private:
92   friend class base::RefCountedThreadSafe<ExtensionDisabledDialogDelegate>;
93
94   virtual ~ExtensionDisabledDialogDelegate();
95
96   // ExtensionInstallPrompt::Delegate:
97   virtual void InstallUIProceed() OVERRIDE;
98   virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
99
100   // The UI for showing the install dialog when enabling.
101   scoped_ptr<ExtensionInstallPrompt> install_ui_;
102
103   ExtensionService* service_;
104   const Extension* extension_;
105 };
106
107 ExtensionDisabledDialogDelegate::ExtensionDisabledDialogDelegate(
108     ExtensionService* service,
109     scoped_ptr<ExtensionInstallPrompt> install_ui,
110     const Extension* extension)
111     : install_ui_(install_ui.Pass()),
112       service_(service),
113       extension_(extension) {
114   AddRef();  // Balanced in Proceed or Abort.
115   install_ui_->ConfirmReEnable(this, extension_);
116 }
117
118 ExtensionDisabledDialogDelegate::~ExtensionDisabledDialogDelegate() {
119 }
120
121 void ExtensionDisabledDialogDelegate::InstallUIProceed() {
122   service_->GrantPermissionsAndEnableExtension(extension_);
123   Release();
124 }
125
126 void ExtensionDisabledDialogDelegate::InstallUIAbort(bool user_initiated) {
127   std::string histogram_name = user_initiated ?
128       "Extensions.Permissions_ReEnableCancel" :
129       "Extensions.Permissions_ReEnableAbort";
130   ExtensionService::RecordPermissionMessagesHistogram(
131       extension_, histogram_name.c_str());
132
133   // Do nothing. The extension will remain disabled.
134   Release();
135 }
136
137 // ExtensionDisabledGlobalError -----------------------------------------------
138
139 class ExtensionDisabledGlobalError : public GlobalErrorWithStandardBubble,
140                                      public content::NotificationObserver,
141                                      public ExtensionUninstallDialog::Delegate {
142  public:
143   ExtensionDisabledGlobalError(ExtensionService* service,
144                                const Extension* extension,
145                                const gfx::Image& icon);
146   virtual ~ExtensionDisabledGlobalError();
147
148   // GlobalError implementation.
149   virtual Severity GetSeverity() OVERRIDE;
150   virtual bool HasMenuItem() OVERRIDE;
151   virtual int MenuItemCommandID() OVERRIDE;
152   virtual base::string16 MenuItemLabel() OVERRIDE;
153   virtual void ExecuteMenuItem(Browser* browser) OVERRIDE;
154   virtual gfx::Image GetBubbleViewIcon() OVERRIDE;
155   virtual base::string16 GetBubbleViewTitle() OVERRIDE;
156   virtual std::vector<base::string16> GetBubbleViewMessages() OVERRIDE;
157   virtual base::string16 GetBubbleViewAcceptButtonLabel() OVERRIDE;
158   virtual base::string16 GetBubbleViewCancelButtonLabel() OVERRIDE;
159   virtual void OnBubbleViewDidClose(Browser* browser) OVERRIDE;
160   virtual void BubbleViewAcceptButtonPressed(Browser* browser) OVERRIDE;
161   virtual void BubbleViewCancelButtonPressed(Browser* browser) OVERRIDE;
162
163   // ExtensionUninstallDialog::Delegate implementation.
164   virtual void ExtensionUninstallAccepted() OVERRIDE;
165   virtual void ExtensionUninstallCanceled() OVERRIDE;
166
167   // content::NotificationObserver implementation.
168   virtual void Observe(int type,
169                        const content::NotificationSource& source,
170                        const content::NotificationDetails& details) OVERRIDE;
171
172  private:
173   ExtensionService* service_;
174   const Extension* extension_;
175   gfx::Image icon_;
176
177   // How the user responded to the error; used for metrics.
178   enum UserResponse {
179     IGNORED,
180     REENABLE,
181     UNINSTALL,
182     EXTENSION_DISABLED_UI_BUCKET_BOUNDARY
183   };
184   UserResponse user_response_;
185
186   scoped_ptr<ExtensionUninstallDialog> uninstall_dialog_;
187
188   // Menu command ID assigned for this extension's error.
189   int menu_command_id_;
190
191   content::NotificationRegistrar registrar_;
192 };
193
194 // TODO(yoz): create error at startup for disabled extensions.
195 ExtensionDisabledGlobalError::ExtensionDisabledGlobalError(
196     ExtensionService* service,
197     const Extension* extension,
198     const gfx::Image& icon)
199     : service_(service),
200       extension_(extension),
201       icon_(icon),
202       user_response_(IGNORED),
203       menu_command_id_(GetMenuCommandID()) {
204   if (icon_.IsEmpty()) {
205     icon_ = gfx::Image(
206         gfx::ImageSkiaOperations::CreateResizedImage(
207             extension_->is_app() ?
208                 extensions::util::GetDefaultAppIcon() :
209                 extensions::util::GetDefaultExtensionIcon(),
210             skia::ImageOperations::RESIZE_BEST,
211             gfx::Size(kIconSize, kIconSize)));
212   }
213   registrar_.Add(this,
214                  chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
215                  content::Source<Profile>(service->profile()));
216   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_REMOVED,
217                  content::Source<Profile>(service->profile()));
218 }
219
220 ExtensionDisabledGlobalError::~ExtensionDisabledGlobalError() {
221   ReleaseMenuCommandID(menu_command_id_);
222   UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponse",
223                             user_response_,
224                             EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
225 }
226
227 GlobalError::Severity ExtensionDisabledGlobalError::GetSeverity() {
228   return SEVERITY_LOW;
229 }
230
231 bool ExtensionDisabledGlobalError::HasMenuItem() {
232   return true;
233 }
234
235 int ExtensionDisabledGlobalError::MenuItemCommandID() {
236   return menu_command_id_;
237 }
238
239 base::string16 ExtensionDisabledGlobalError::MenuItemLabel() {
240   return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
241                                     base::UTF8ToUTF16(extension_->name()));
242 }
243
244 void ExtensionDisabledGlobalError::ExecuteMenuItem(Browser* browser) {
245   ShowBubbleView(browser);
246 }
247
248 gfx::Image ExtensionDisabledGlobalError::GetBubbleViewIcon() {
249   return icon_;
250 }
251
252 base::string16 ExtensionDisabledGlobalError::GetBubbleViewTitle() {
253   return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
254                                     base::UTF8ToUTF16(extension_->name()));
255 }
256
257 std::vector<base::string16>
258 ExtensionDisabledGlobalError::GetBubbleViewMessages() {
259   std::vector<base::string16> messages;
260   messages.push_back(l10n_util::GetStringFUTF16(
261       extension_->is_app() ?
262       IDS_APP_DISABLED_ERROR_LABEL : IDS_EXTENSION_DISABLED_ERROR_LABEL,
263       base::UTF8ToUTF16(extension_->name())));
264   messages.push_back(l10n_util::GetStringUTF16(
265       IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO));
266   std::vector<base::string16> permission_warnings =
267       extensions::PermissionMessageProvider::Get()->GetWarningMessages(
268           extension_->GetActivePermissions(), extension_->GetType());
269   for (size_t i = 0; i < permission_warnings.size(); ++i) {
270     messages.push_back(l10n_util::GetStringFUTF16(
271         IDS_EXTENSION_PERMISSION_LINE, permission_warnings[i]));
272   }
273   return messages;
274 }
275
276 base::string16 ExtensionDisabledGlobalError::GetBubbleViewAcceptButtonLabel() {
277   return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON);
278 }
279
280 base::string16 ExtensionDisabledGlobalError::GetBubbleViewCancelButtonLabel() {
281   return l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL);
282 }
283
284 void ExtensionDisabledGlobalError::OnBubbleViewDidClose(Browser* browser) {
285 }
286
287 void ExtensionDisabledGlobalError::BubbleViewAcceptButtonPressed(
288     Browser* browser) {
289   // Delay extension reenabling so this bubble closes properly.
290   base::MessageLoop::current()->PostTask(FROM_HERE,
291       base::Bind(&ExtensionService::GrantPermissionsAndEnableExtension,
292                  service_->AsWeakPtr(), extension_));
293 }
294
295 void ExtensionDisabledGlobalError::BubbleViewCancelButtonPressed(
296     Browser* browser) {
297 #if !defined(OS_ANDROID)
298   uninstall_dialog_.reset(
299       ExtensionUninstallDialog::Create(service_->profile(), browser, this));
300   // Delay showing the uninstall dialog, so that this function returns
301   // immediately, to close the bubble properly. See crbug.com/121544.
302   base::MessageLoop::current()->PostTask(FROM_HERE,
303       base::Bind(&ExtensionUninstallDialog::ConfirmUninstall,
304                  uninstall_dialog_->AsWeakPtr(), extension_));
305 #endif  // !defined(OS_ANDROID)
306 }
307
308 void ExtensionDisabledGlobalError::ExtensionUninstallAccepted() {
309   service_->UninstallExtension(extension_->id(), false, NULL);
310 }
311
312 void ExtensionDisabledGlobalError::ExtensionUninstallCanceled() {
313   // Nothing happens, and the error is still there.
314 }
315
316 void ExtensionDisabledGlobalError::Observe(
317     int type,
318     const content::NotificationSource& source,
319     const content::NotificationDetails& details) {
320   // The error is invalidated if the extension has been loaded or removed.
321   DCHECK(type == chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED ||
322          type == chrome::NOTIFICATION_EXTENSION_REMOVED);
323   const Extension* extension = content::Details<const Extension>(details).ptr();
324   if (extension != extension_)
325     return;
326   GlobalErrorServiceFactory::GetForProfile(service_->profile())->
327       RemoveGlobalError(this);
328
329   if (type == chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED)
330     user_response_ = REENABLE;
331   else if (type == chrome::NOTIFICATION_EXTENSION_REMOVED)
332     user_response_ = UNINSTALL;
333   delete this;
334 }
335
336 // Globals --------------------------------------------------------------------
337
338 namespace extensions {
339
340 void AddExtensionDisabledErrorWithIcon(base::WeakPtr<ExtensionService> service,
341                                        const std::string& extension_id,
342                                        const gfx::Image& icon) {
343   if (!service.get())
344     return;
345   const Extension* extension = service->GetInstalledExtension(extension_id);
346   if (extension) {
347     GlobalErrorServiceFactory::GetForProfile(service->profile())
348         ->AddGlobalError(
349               new ExtensionDisabledGlobalError(service.get(), extension, icon));
350   }
351 }
352
353 void AddExtensionDisabledError(ExtensionService* service,
354                                const Extension* extension) {
355   // Do not display notifications for ephemeral apps that have been disabled.
356   // Instead, a prompt will be shown the next time the app is launched.
357   if (extension->is_ephemeral())
358     return;
359
360   extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
361       extension, kIconSize, ExtensionIconSet::MATCH_BIGGER);
362   gfx::Size size(kIconSize, kIconSize);
363   ImageLoader::Get(service->profile())->LoadImageAsync(
364       extension, image, size,
365       base::Bind(&AddExtensionDisabledErrorWithIcon,
366                  service->AsWeakPtr(), extension->id()));
367 }
368
369 void ShowExtensionDisabledDialog(ExtensionService* service,
370                                  content::WebContents* web_contents,
371                                  const Extension* extension) {
372   scoped_ptr<ExtensionInstallPrompt> install_ui(
373       new ExtensionInstallPrompt(web_contents));
374   // This object manages its own lifetime.
375   new ExtensionDisabledDialogDelegate(service, install_ui.Pass(), extension);
376 }
377
378 }  // namespace extensions