Update To 11.40.268.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 <bitset>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/app/chrome_command_ids.h"
19 #include "chrome/browser/extensions/extension_install_prompt.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/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_window.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 "chrome/grit/chromium_strings.h"
31 #include "chrome/grit/generated_resources.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 "extensions/browser/extension_util.h"
37 #include "extensions/browser/image_loader.h"
38 #include "extensions/browser/notification_types.h"
39 #include "extensions/browser/uninstall_reason.h"
40 #include "extensions/common/constants.h"
41 #include "extensions/common/extension.h"
42 #include "extensions/common/extension_icon_set.h"
43 #include "extensions/common/manifest_handlers/icons_handler.h"
44 #include "extensions/common/permissions/permission_message_provider.h"
45 #include "extensions/common/permissions/permission_set.h"
46 #include "extensions/common/permissions/permissions_data.h"
47 #include "ui/base/l10n/l10n_util.h"
48 #include "ui/gfx/image/image.h"
49 #include "ui/gfx/image/image_skia_operations.h"
50 #include "ui/gfx/size.h"
51
52 using extensions::Extension;
53
54 namespace {
55
56 static const int kIconSize = extension_misc::EXTENSION_ICON_SMALL;
57
58 static base::LazyInstance<
59     std::bitset<IDC_EXTENSION_DISABLED_LAST -
60                 IDC_EXTENSION_DISABLED_FIRST + 1> >
61     menu_command_ids = LAZY_INSTANCE_INITIALIZER;
62
63 // Get an available menu ID.
64 int GetMenuCommandID() {
65   int id;
66   for (id = IDC_EXTENSION_DISABLED_FIRST;
67        id <= IDC_EXTENSION_DISABLED_LAST; ++id) {
68     if (!menu_command_ids.Get()[id - IDC_EXTENSION_DISABLED_FIRST]) {
69       menu_command_ids.Get().set(id - IDC_EXTENSION_DISABLED_FIRST);
70       return id;
71     }
72   }
73   // This should not happen.
74   DCHECK(id <= IDC_EXTENSION_DISABLED_LAST) <<
75       "No available menu command IDs for ExtensionDisabledGlobalError";
76   return IDC_EXTENSION_DISABLED_LAST;
77 }
78
79 // Make a menu ID available when it is no longer used.
80 void ReleaseMenuCommandID(int id) {
81   menu_command_ids.Get().reset(id - IDC_EXTENSION_DISABLED_FIRST);
82 }
83
84 }  // namespace
85
86 // ExtensionDisabledDialogDelegate --------------------------------------------
87
88 class ExtensionDisabledDialogDelegate
89     : public ExtensionInstallPrompt::Delegate,
90       public base::RefCountedThreadSafe<ExtensionDisabledDialogDelegate> {
91  public:
92   ExtensionDisabledDialogDelegate(ExtensionService* service,
93                                   scoped_ptr<ExtensionInstallPrompt> install_ui,
94                                   const Extension* extension);
95
96  private:
97   friend class base::RefCountedThreadSafe<ExtensionDisabledDialogDelegate>;
98
99   ~ExtensionDisabledDialogDelegate() override;
100
101   // ExtensionInstallPrompt::Delegate:
102   void InstallUIProceed() override;
103   void InstallUIAbort(bool user_initiated) override;
104
105   // The UI for showing the install dialog when enabling.
106   scoped_ptr<ExtensionInstallPrompt> install_ui_;
107
108   ExtensionService* service_;
109   const Extension* extension_;
110 };
111
112 ExtensionDisabledDialogDelegate::ExtensionDisabledDialogDelegate(
113     ExtensionService* service,
114     scoped_ptr<ExtensionInstallPrompt> install_ui,
115     const Extension* extension)
116     : install_ui_(install_ui.Pass()),
117       service_(service),
118       extension_(extension) {
119   AddRef();  // Balanced in Proceed or Abort.
120   install_ui_->ConfirmReEnable(this, extension_);
121 }
122
123 ExtensionDisabledDialogDelegate::~ExtensionDisabledDialogDelegate() {
124 }
125
126 void ExtensionDisabledDialogDelegate::InstallUIProceed() {
127   service_->GrantPermissionsAndEnableExtension(extension_);
128   Release();
129 }
130
131 void ExtensionDisabledDialogDelegate::InstallUIAbort(bool user_initiated) {
132   std::string histogram_name = user_initiated
133                                    ? "Extensions.Permissions_ReEnableCancel2"
134                                    : "Extensions.Permissions_ReEnableAbort2";
135   ExtensionService::RecordPermissionMessagesHistogram(
136       extension_, histogram_name.c_str());
137
138   // Do nothing. The extension will remain disabled.
139   Release();
140 }
141
142 // ExtensionDisabledGlobalError -----------------------------------------------
143
144 class ExtensionDisabledGlobalError
145     : public GlobalErrorWithStandardBubble,
146       public content::NotificationObserver,
147       public extensions::ExtensionUninstallDialog::Delegate {
148  public:
149   ExtensionDisabledGlobalError(ExtensionService* service,
150                                const Extension* extension,
151                                bool is_remote_install,
152                                const gfx::Image& icon);
153   ~ExtensionDisabledGlobalError() override;
154
155   // GlobalError implementation.
156   Severity GetSeverity() override;
157   bool HasMenuItem() override;
158   int MenuItemCommandID() override;
159   base::string16 MenuItemLabel() override;
160   void ExecuteMenuItem(Browser* browser) override;
161   gfx::Image GetBubbleViewIcon() override;
162   base::string16 GetBubbleViewTitle() override;
163   std::vector<base::string16> GetBubbleViewMessages() override;
164   base::string16 GetBubbleViewAcceptButtonLabel() override;
165   base::string16 GetBubbleViewCancelButtonLabel() override;
166   void OnBubbleViewDidClose(Browser* browser) override;
167   void BubbleViewAcceptButtonPressed(Browser* browser) override;
168   void BubbleViewCancelButtonPressed(Browser* browser) override;
169   bool ShouldCloseOnDeactivate() const override;
170
171   // ExtensionUninstallDialog::Delegate implementation.
172   void ExtensionUninstallAccepted() override;
173   void ExtensionUninstallCanceled() override;
174
175   // content::NotificationObserver implementation.
176   void Observe(int type,
177                const content::NotificationSource& source,
178                const content::NotificationDetails& details) override;
179
180  private:
181   ExtensionService* service_;
182   const Extension* extension_;
183   bool is_remote_install_;
184   gfx::Image icon_;
185
186   // How the user responded to the error; used for metrics.
187   enum UserResponse {
188     IGNORED,
189     REENABLE,
190     UNINSTALL,
191     EXTENSION_DISABLED_UI_BUCKET_BOUNDARY
192   };
193   UserResponse user_response_;
194
195   scoped_ptr<extensions::ExtensionUninstallDialog> uninstall_dialog_;
196
197   // Menu command ID assigned for this extension's error.
198   int menu_command_id_;
199
200   content::NotificationRegistrar registrar_;
201 };
202
203 // TODO(yoz): create error at startup for disabled extensions.
204 ExtensionDisabledGlobalError::ExtensionDisabledGlobalError(
205     ExtensionService* service,
206     const Extension* extension,
207     bool is_remote_install,
208     const gfx::Image& icon)
209     : service_(service),
210       extension_(extension),
211       is_remote_install_(is_remote_install),
212       icon_(icon),
213       user_response_(IGNORED),
214       menu_command_id_(GetMenuCommandID()) {
215   if (icon_.IsEmpty()) {
216     icon_ = gfx::Image(
217         gfx::ImageSkiaOperations::CreateResizedImage(
218             extension_->is_app() ?
219                 extensions::util::GetDefaultAppIcon() :
220                 extensions::util::GetDefaultExtensionIcon(),
221             skia::ImageOperations::RESIZE_BEST,
222             gfx::Size(kIconSize, kIconSize)));
223   }
224   registrar_.Add(this,
225                  extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
226                  content::Source<Profile>(service->profile()));
227   registrar_.Add(this,
228                  extensions::NOTIFICATION_EXTENSION_REMOVED,
229                  content::Source<Profile>(service->profile()));
230 }
231
232 ExtensionDisabledGlobalError::~ExtensionDisabledGlobalError() {
233   ReleaseMenuCommandID(menu_command_id_);
234   if (is_remote_install_) {
235     UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponseRemoteInstall",
236                               user_response_,
237                               EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
238   } else {
239     UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponse",
240                               user_response_,
241                               EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
242   }
243 }
244
245 GlobalError::Severity ExtensionDisabledGlobalError::GetSeverity() {
246   return SEVERITY_LOW;
247 }
248
249 bool ExtensionDisabledGlobalError::HasMenuItem() {
250   return true;
251 }
252
253 int ExtensionDisabledGlobalError::MenuItemCommandID() {
254   return menu_command_id_;
255 }
256
257 base::string16 ExtensionDisabledGlobalError::MenuItemLabel() {
258   std::string extension_name = extension_->name();
259   // Ampersands need to be escaped to avoid being treated like
260   // mnemonics in the menu.
261   base::ReplaceChars(extension_name, "&", "&&", &extension_name);
262
263   if (is_remote_install_) {
264     return l10n_util::GetStringFUTF16(
265         IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_TITLE,
266         base::UTF8ToUTF16(extension_name));
267   } else {
268     return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
269                                       base::UTF8ToUTF16(extension_name));
270   }
271 }
272
273 void ExtensionDisabledGlobalError::ExecuteMenuItem(Browser* browser) {
274   ShowBubbleView(browser);
275 }
276
277 gfx::Image ExtensionDisabledGlobalError::GetBubbleViewIcon() {
278   return icon_;
279 }
280
281 base::string16 ExtensionDisabledGlobalError::GetBubbleViewTitle() {
282   if (is_remote_install_) {
283     return l10n_util::GetStringFUTF16(
284         IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_TITLE,
285         base::UTF8ToUTF16(extension_->name()));
286   } else {
287     return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
288                                       base::UTF8ToUTF16(extension_->name()));
289   }
290 }
291
292 std::vector<base::string16>
293 ExtensionDisabledGlobalError::GetBubbleViewMessages() {
294   std::vector<base::string16> messages;
295   std::vector<base::string16> permission_warnings =
296       extensions::PermissionMessageProvider::Get()->GetWarningMessages(
297           extension_->permissions_data()->active_permissions().get(),
298           extension_->GetType());
299   if (is_remote_install_) {
300     messages.push_back(l10n_util::GetStringFUTF16(
301         extension_->is_app()
302             ? IDS_APP_DISABLED_REMOTE_INSTALL_ERROR_LABEL
303             : IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_LABEL,
304         base::UTF8ToUTF16(extension_->name())));
305     if (!permission_warnings.empty())
306       messages.push_back(
307           l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO));
308   } else {
309     messages.push_back(l10n_util::GetStringFUTF16(
310         extension_->is_app() ? IDS_APP_DISABLED_ERROR_LABEL
311                              : IDS_EXTENSION_DISABLED_ERROR_LABEL,
312         base::UTF8ToUTF16(extension_->name())));
313     messages.push_back(l10n_util::GetStringUTF16(
314         IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO));
315   }
316   for (size_t i = 0; i < permission_warnings.size(); ++i) {
317     messages.push_back(l10n_util::GetStringFUTF16(
318         IDS_EXTENSION_PERMISSION_LINE, permission_warnings[i]));
319   }
320   return messages;
321 }
322
323 base::string16 ExtensionDisabledGlobalError::GetBubbleViewAcceptButtonLabel() {
324   if (is_remote_install_) {
325     return l10n_util::GetStringUTF16(
326         IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON);
327   } else {
328     return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON);
329   }
330 }
331
332 base::string16 ExtensionDisabledGlobalError::GetBubbleViewCancelButtonLabel() {
333   return l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL);
334 }
335
336 void ExtensionDisabledGlobalError::OnBubbleViewDidClose(Browser* browser) {
337 }
338
339 void ExtensionDisabledGlobalError::BubbleViewAcceptButtonPressed(
340     Browser* browser) {
341   // Delay extension reenabling so this bubble closes properly.
342   base::MessageLoop::current()->PostTask(FROM_HERE,
343       base::Bind(&ExtensionService::GrantPermissionsAndEnableExtension,
344                  service_->AsWeakPtr(), extension_));
345 }
346
347 void ExtensionDisabledGlobalError::BubbleViewCancelButtonPressed(
348     Browser* browser) {
349   uninstall_dialog_.reset(extensions::ExtensionUninstallDialog::Create(
350       service_->profile(), browser->window()->GetNativeWindow(), this));
351   // Delay showing the uninstall dialog, so that this function returns
352   // immediately, to close the bubble properly. See crbug.com/121544.
353   base::MessageLoop::current()->PostTask(
354       FROM_HERE,
355       base::Bind(&extensions::ExtensionUninstallDialog::ConfirmUninstall,
356                  uninstall_dialog_->AsWeakPtr(),
357                  extension_));
358 }
359
360 bool ExtensionDisabledGlobalError::ShouldCloseOnDeactivate() const {
361   // Since this indicates that an extension was disabled, we should definitely
362   // have the user acknowledge it, rather than having the bubble disappear when
363   // a new window pops up.
364   return false;
365 }
366
367 void ExtensionDisabledGlobalError::ExtensionUninstallAccepted() {
368   service_->UninstallExtension(extension_->id(),
369                                extensions::UNINSTALL_REASON_EXTENSION_DISABLED,
370                                base::Bind(&base::DoNothing),
371                                NULL);
372 }
373
374 void ExtensionDisabledGlobalError::ExtensionUninstallCanceled() {
375   // Nothing happens, and the error is still there.
376 }
377
378 void ExtensionDisabledGlobalError::Observe(
379     int type,
380     const content::NotificationSource& source,
381     const content::NotificationDetails& details) {
382   // The error is invalidated if the extension has been loaded or removed.
383   DCHECK(type == extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED ||
384          type == extensions::NOTIFICATION_EXTENSION_REMOVED);
385   const Extension* extension = content::Details<const Extension>(details).ptr();
386   if (extension != extension_)
387     return;
388   GlobalErrorServiceFactory::GetForProfile(service_->profile())->
389       RemoveGlobalError(this);
390
391   if (type == extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED)
392     user_response_ = REENABLE;
393   else if (type == extensions::NOTIFICATION_EXTENSION_REMOVED)
394     user_response_ = UNINSTALL;
395   delete this;
396 }
397
398 // Globals --------------------------------------------------------------------
399
400 namespace extensions {
401
402 void AddExtensionDisabledErrorWithIcon(base::WeakPtr<ExtensionService> service,
403                                        const std::string& extension_id,
404                                        bool is_remote_install,
405                                        const gfx::Image& icon) {
406   if (!service.get())
407     return;
408   const Extension* extension = service->GetInstalledExtension(extension_id);
409   if (extension) {
410     GlobalErrorServiceFactory::GetForProfile(service->profile())
411         ->AddGlobalError(new ExtensionDisabledGlobalError(
412             service.get(), extension, is_remote_install, icon));
413   }
414 }
415
416 void AddExtensionDisabledError(ExtensionService* service,
417                                const Extension* extension,
418                                bool is_remote_install) {
419   // Do not display notifications for ephemeral apps that have been disabled.
420   // Instead, a prompt will be shown the next time the app is launched.
421   if (util::IsEphemeralApp(extension->id(), service->profile()))
422     return;
423
424   extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
425       extension, kIconSize, ExtensionIconSet::MATCH_BIGGER);
426   gfx::Size size(kIconSize, kIconSize);
427   ImageLoader::Get(service->profile())
428       ->LoadImageAsync(extension,
429                        image,
430                        size,
431                        base::Bind(&AddExtensionDisabledErrorWithIcon,
432                                   service->AsWeakPtr(),
433                                   extension->id(),
434                                   is_remote_install));
435 }
436
437 void ShowExtensionDisabledDialog(ExtensionService* service,
438                                  content::WebContents* web_contents,
439                                  const Extension* extension) {
440   scoped_ptr<ExtensionInstallPrompt> install_ui(
441       new ExtensionInstallPrompt(web_contents));
442   // This object manages its own lifetime.
443   new ExtensionDisabledDialogDelegate(service, install_ui.Pass(), extension);
444 }
445
446 }  // namespace extensions