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_uninstall_dialog.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/extensions/image_loader.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/common/extensions/extension_constants.h"
15#include "chrome/common/extensions/extension_icon_set.h"
16#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
17#include "content/public/browser/notification_service.h"
18#include "content/public/browser/notification_source.h"
19#include "extensions/common/extension.h"
20#include "extensions/common/extension_resource.h"
21#include "grit/generated_resources.h"
22#include "grit/theme_resources.h"
23#include "ui/base/resource/resource_bundle.h"
24#include "ui/gfx/image/image.h"
25#include "ui/gfx/image/image_skia.h"
26
27namespace {
28
29// Returns pixel size under maximal scale factor for the icon whose device
30// independent size is |size_in_dip|
31int GetSizeForMaxScaleFactor(int size_in_dip) {
32  float max_scale_factor_scale = gfx::ImageSkia::GetMaxSupportedScale();
33
34  return static_cast<int>(size_in_dip * max_scale_factor_scale);
35}
36
37// Returns bitmap for the default icon with size equal to the default icon's
38// pixel size under maximal supported scale factor.
39SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
40  const gfx::ImageSkia& image = is_app ?
41      extensions::IconsInfo::GetDefaultAppIcon() :
42      extensions::IconsInfo::GetDefaultExtensionIcon();
43  return image.GetRepresentation(
44      gfx::ImageSkia::GetMaxSupportedScale()).sk_bitmap();
45}
46
47}  // namespace
48
49// Size of extension icon in top left of dialog.
50static const int kIconSize = 69;
51
52ExtensionUninstallDialog::ExtensionUninstallDialog(
53    Profile* profile,
54    Browser* browser,
55    ExtensionUninstallDialog::Delegate* delegate)
56    : profile_(profile),
57      browser_(browser),
58      delegate_(delegate),
59      extension_(NULL),
60      state_(kImageIsLoading),
61      ui_loop_(base::MessageLoop::current()) {
62  if (browser) {
63    registrar_.Add(this,
64                   chrome::NOTIFICATION_BROWSER_CLOSED,
65                   content::Source<Browser>(browser));
66  }
67}
68
69ExtensionUninstallDialog::~ExtensionUninstallDialog() {
70}
71
72void ExtensionUninstallDialog::ConfirmUninstall(
73    const extensions::Extension* extension) {
74  DCHECK(ui_loop_ == base::MessageLoop::current());
75  extension_ = extension;
76  extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
77      extension_,
78      extension_misc::EXTENSION_ICON_LARGE,
79      ExtensionIconSet::MATCH_BIGGER);
80  // Load the icon whose pixel size is large enough to be displayed under
81  // maximal supported scale factor. UI code will scale the icon down if needed.
82  int pixel_size = GetSizeForMaxScaleFactor(kIconSize);
83
84  // Load the image asynchronously. The response will be sent to OnImageLoaded.
85  state_ = kImageIsLoading;
86  extensions::ImageLoader* loader =
87      extensions::ImageLoader::Get(profile_);
88  loader->LoadImageAsync(extension_, image,
89                         gfx::Size(pixel_size, pixel_size),
90                         base::Bind(&ExtensionUninstallDialog::OnImageLoaded,
91                                    AsWeakPtr()));
92}
93
94void ExtensionUninstallDialog::SetIcon(const gfx::Image& image) {
95  if (image.IsEmpty()) {
96    // Let's set default icon bitmap whose size is equal to the default icon's
97    // pixel size under maximal supported scale factor. If the bitmap is larger
98    // than the one we need, it will be scaled down by the ui code.
99    // TODO(tbarzic): We should use IconImage here and load the required bitmap
100    //     lazily.
101    icon_ = gfx::ImageSkia::CreateFrom1xBitmap(
102        GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app()));
103  } else {
104    icon_ = *image.ToImageSkia();
105  }
106}
107
108void ExtensionUninstallDialog::OnImageLoaded(const gfx::Image& image) {
109  SetIcon(image);
110
111  // Show the dialog unless the browser has been closed while we were waiting
112  // for the image.
113  DCHECK(state_ == kImageIsLoading || state_ == kBrowserIsClosing);
114  if (state_ == kImageIsLoading) {
115    state_ = kDialogIsShowing;
116    Show();
117  }
118}
119
120void ExtensionUninstallDialog::Observe(
121    int type,
122    const content::NotificationSource& source,
123    const content::NotificationDetails& details) {
124  DCHECK(type == chrome::NOTIFICATION_BROWSER_CLOSED);
125
126  browser_ = NULL;
127  // If the browser is closed while waiting for the image, we need to send a
128  // "cancel" event here, because there will not be another opportunity to
129  // notify the delegate of the cancellation as we won't open the dialog.
130  if (state_ == kImageIsLoading) {
131    state_ = kBrowserIsClosing;
132    delegate_->ExtensionUninstallCanceled();
133  }
134}
135