Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / extension_app_item.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/ui/app_list/extension_app_item.h"
6
7 #include "base/command_line.h"
8 #include "base/prefs/pref_service.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/extensions/launch_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/app_list/app_context_menu.h"
14 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
15 #include "chrome/browser/ui/app_list/app_list_service.h"
16 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
17 #include "chrome/browser/ui/host_desktop.h"
18 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/extensions/extension_constants.h"
21 #include "chrome/common/extensions/manifest_url_handler.h"
22 #include "content/public/browser/user_metrics.h"
23 #include "extensions/browser/app_sorting.h"
24 #include "extensions/browser/extension_prefs.h"
25 #include "extensions/browser/extension_system.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_icon_set.h"
28 #include "extensions/common/manifest_handlers/icons_handler.h"
29 #include "grit/theme_resources.h"
30 #include "sync/api/string_ordinal.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/gfx/canvas.h"
33 #include "ui/gfx/color_utils.h"
34 #include "ui/gfx/image/canvas_image_source.h"
35 #include "ui/gfx/image/image_skia_operations.h"
36 #include "ui/gfx/rect.h"
37
38 using extensions::Extension;
39
40 namespace {
41
42 // Overlays a shortcut icon over the bottom left corner of a given image.
43 class ShortcutOverlayImageSource : public gfx::CanvasImageSource {
44  public:
45   explicit ShortcutOverlayImageSource(const gfx::ImageSkia& icon)
46       : gfx::CanvasImageSource(icon.size(), false),
47         icon_(icon) {
48   }
49   virtual ~ShortcutOverlayImageSource() {}
50
51  private:
52   // gfx::CanvasImageSource overrides:
53   virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
54     canvas->DrawImageInt(icon_, 0, 0);
55
56     // Draw the overlay in the bottom left corner of the icon.
57     const gfx::ImageSkia& overlay = *ui::ResourceBundle::GetSharedInstance().
58         GetImageSkiaNamed(IDR_APP_LIST_TAB_OVERLAY);
59     canvas->DrawImageInt(overlay, 0, icon_.height() - overlay.height());
60   }
61
62   gfx::ImageSkia icon_;
63
64   DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource);
65 };
66
67 // Rounds the corners of a given image.
68 class RoundedCornersImageSource : public gfx::CanvasImageSource {
69  public:
70   explicit RoundedCornersImageSource(const gfx::ImageSkia& icon)
71       : gfx::CanvasImageSource(icon.size(), false),
72         icon_(icon) {
73   }
74   virtual ~RoundedCornersImageSource() {}
75
76  private:
77   // gfx::CanvasImageSource overrides:
78   virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
79     // The radius used to round the app icon.
80     const size_t kRoundingRadius = 2;
81
82     canvas->DrawImageInt(icon_, 0, 0);
83
84     scoped_ptr<gfx::Canvas> masking_canvas(
85         new gfx::Canvas(gfx::Size(icon_.width(), icon_.height()), 1.0f, false));
86     DCHECK(masking_canvas);
87
88     SkPaint opaque_paint;
89     opaque_paint.setColor(SK_ColorWHITE);
90     opaque_paint.setFlags(SkPaint::kAntiAlias_Flag);
91     masking_canvas->DrawRoundRect(
92         gfx::Rect(icon_.width(), icon_.height()),
93         kRoundingRadius, opaque_paint);
94
95     SkPaint masking_paint;
96     masking_paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
97     canvas->DrawImageInt(
98         gfx::ImageSkia(masking_canvas->ExtractImageRep()), 0, 0, masking_paint);
99   }
100
101   gfx::ImageSkia icon_;
102
103   DISALLOW_COPY_AND_ASSIGN(RoundedCornersImageSource);
104 };
105
106 extensions::AppSorting* GetAppSorting(Profile* profile) {
107   return extensions::ExtensionPrefs::Get(profile)->app_sorting();
108 }
109
110 const color_utils::HSL shift = {-1, 0, 0.6};
111
112 }  // namespace
113
114 ExtensionAppItem::ExtensionAppItem(
115     Profile* profile,
116     const app_list::AppListSyncableService::SyncItem* sync_item,
117     const std::string& extension_id,
118     const std::string& extension_name,
119     const gfx::ImageSkia& installing_icon,
120     bool is_platform_app)
121     : app_list::AppListItem(extension_id),
122       profile_(profile),
123       extension_id_(extension_id),
124       extension_enable_flow_controller_(NULL),
125       extension_name_(extension_name),
126       installing_icon_(
127           gfx::ImageSkiaOperations::CreateHSLShiftedImage(installing_icon,
128                                                           shift)),
129       is_platform_app_(is_platform_app),
130       has_overlay_(false) {
131   Reload();
132   if (sync_item && sync_item->item_ordinal.IsValid()) {
133     // An existing synced position exists, use that.
134     set_position(sync_item->item_ordinal);
135     // Only set the name from the sync item if it is empty.
136     if (name().empty())
137       SetName(sync_item->item_name);
138     return;
139   }
140   GetAppSorting(profile_)->EnsureValidOrdinals(extension_id_,
141                                                syncer::StringOrdinal());
142   UpdatePositionFromExtensionOrdering();
143 }
144
145 ExtensionAppItem::~ExtensionAppItem() {
146 }
147
148 bool ExtensionAppItem::NeedsOverlay() const {
149   // The overlay icon is disabled for hosted apps in windowed mode with
150   // streamlined hosted apps.
151   bool streamlined_hosted_apps = CommandLine::ForCurrentProcess()->
152       HasSwitch(switches::kEnableStreamlinedHostedApps);
153 #if defined(OS_CHROMEOS)
154   if (!streamlined_hosted_apps)
155     return false;
156 #endif
157   extensions::LaunchType launch_type =
158       GetExtension()
159           ? extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
160                                       GetExtension())
161           : extensions::LAUNCH_TYPE_WINDOW;
162
163   return !is_platform_app_ && extension_id_ != extension_misc::kChromeAppId &&
164       (!streamlined_hosted_apps ||
165        launch_type != extensions::LAUNCH_TYPE_WINDOW);
166 }
167
168 void ExtensionAppItem::Reload() {
169   const Extension* extension = GetExtension();
170   bool is_installing = !extension;
171   SetIsInstalling(is_installing);
172   if (is_installing) {
173     SetName(extension_name_);
174     UpdateIcon();
175     return;
176   }
177   SetNameAndShortName(extension->name(), extension->short_name());
178   LoadImage(extension);
179 }
180
181 void ExtensionAppItem::UpdateIcon() {
182   gfx::ImageSkia icon = installing_icon_;
183
184   // Use the app icon if the app exists. Turn the image greyscale if the app is
185   // not launchable.
186   if (GetExtension()) {
187     icon = icon_->image_skia();
188     const bool enabled = extensions::util::IsAppLaunchable(extension_id_,
189                                                            profile_);
190     if (!enabled) {
191       const color_utils::HSL shift = {-1, 0, 0.6};
192       icon = gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon, shift);
193     }
194
195     if (GetExtension()->from_bookmark())
196       icon = gfx::ImageSkia(new RoundedCornersImageSource(icon), icon.size());
197   }
198   // Paint the shortcut overlay if necessary.
199   has_overlay_ = NeedsOverlay();
200   if (has_overlay_)
201     icon = gfx::ImageSkia(new ShortcutOverlayImageSource(icon), icon.size());
202
203   SetIcon(icon, true);
204 }
205
206 void ExtensionAppItem::Move(const ExtensionAppItem* prev,
207                             const ExtensionAppItem* next) {
208   if (!prev && !next)
209     return;  // No reordering necessary
210
211   extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
212   extensions::AppSorting* sorting = GetAppSorting(profile_);
213
214   syncer::StringOrdinal page;
215   std::string prev_id, next_id;
216   if (!prev) {
217     next_id = next->extension_id();
218     page = sorting->GetPageOrdinal(next_id);
219   } else if (!next) {
220     prev_id = prev->extension_id();
221     page = sorting->GetPageOrdinal(prev_id);
222   } else {
223     prev_id = prev->extension_id();
224     page = sorting->GetPageOrdinal(prev_id);
225     // Only set |next_id| if on the same page, otherwise just insert after prev.
226     if (page.Equals(sorting->GetPageOrdinal(next->extension_id())))
227       next_id = next->extension_id();
228   }
229   prefs->SetAppDraggedByUser(extension_id_);
230   sorting->SetPageOrdinal(extension_id_, page);
231   sorting->OnExtensionMoved(extension_id_, prev_id, next_id);
232   UpdatePositionFromExtensionOrdering();
233 }
234
235 const Extension* ExtensionAppItem::GetExtension() const {
236   const ExtensionService* service =
237       extensions::ExtensionSystem::Get(profile_)->extension_service();
238   const Extension* extension = service->GetInstalledExtension(extension_id_);
239   return extension;
240 }
241
242 void ExtensionAppItem::LoadImage(const Extension* extension) {
243   icon_.reset(new extensions::IconImage(
244       profile_,
245       extension,
246       extensions::IconsInfo::GetIcons(extension),
247       extension_misc::EXTENSION_ICON_MEDIUM,
248       extensions::util::GetDefaultAppIcon(),
249       this));
250   UpdateIcon();
251 }
252
253 bool ExtensionAppItem::RunExtensionEnableFlow() {
254   if (extensions::util::IsAppLaunchableWithoutEnabling(extension_id_, profile_))
255     return false;
256
257   if (!extension_enable_flow_) {
258     extension_enable_flow_controller_ = GetController();
259     extension_enable_flow_controller_->OnShowExtensionPrompt();
260
261     extension_enable_flow_.reset(new ExtensionEnableFlow(
262         profile_, extension_id_, this));
263     extension_enable_flow_->StartForNativeWindow(
264         extension_enable_flow_controller_->GetAppListWindow());
265   }
266   return true;
267 }
268
269 void ExtensionAppItem::Launch(int event_flags) {
270   // |extension| could be NULL when it is being unloaded for updating.
271   const Extension* extension = GetExtension();
272   if (!extension)
273     return;
274
275   if (RunExtensionEnableFlow())
276     return;
277
278   GetController()->LaunchApp(profile_,
279                              extension,
280                              AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
281                              event_flags);
282 }
283
284 void ExtensionAppItem::OnExtensionIconImageChanged(
285     extensions::IconImage* image) {
286   DCHECK(icon_.get() == image);
287   UpdateIcon();
288 }
289
290 void ExtensionAppItem::ExtensionEnableFlowFinished() {
291   extension_enable_flow_.reset();
292   extension_enable_flow_controller_->OnCloseExtensionPrompt();
293   extension_enable_flow_controller_ = NULL;
294
295   // Automatically launch app after enabling.
296   Launch(ui::EF_NONE);
297 }
298
299 void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated) {
300   extension_enable_flow_.reset();
301   extension_enable_flow_controller_->OnCloseExtensionPrompt();
302   extension_enable_flow_controller_ = NULL;
303 }
304
305 void ExtensionAppItem::Activate(int event_flags) {
306   // |extension| could be NULL when it is being unloaded for updating.
307   const Extension* extension = GetExtension();
308   if (!extension)
309     return;
310
311   if (RunExtensionEnableFlow())
312     return;
313
314   content::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
315   CoreAppLauncherHandler::RecordAppListMainLaunch(extension);
316   GetController()->ActivateApp(profile_,
317                                extension,
318                                AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
319                                event_flags);
320 }
321
322 ui::MenuModel* ExtensionAppItem::GetContextMenuModel() {
323   context_menu_.reset(new app_list::AppContextMenu(
324       this, profile_, extension_id_, GetController()));
325   context_menu_->set_is_platform_app(is_platform_app_);
326   if (IsInFolder())
327     context_menu_->set_is_in_folder(true);
328   return context_menu_->GetMenuModel();
329 }
330
331 void ExtensionAppItem::OnExtensionPreferenceChanged() {
332   if (has_overlay_ != NeedsOverlay())
333     UpdateIcon();
334 }
335
336 // static
337 const char ExtensionAppItem::kItemType[] = "ExtensionAppItem";
338
339 const char* ExtensionAppItem::GetItemType() const {
340   return ExtensionAppItem::kItemType;
341 }
342
343 void ExtensionAppItem::ExecuteLaunchCommand(int event_flags) {
344   Launch(event_flags);
345 }
346
347 void ExtensionAppItem::UpdatePositionFromExtensionOrdering() {
348   const syncer::StringOrdinal& page =
349       GetAppSorting(profile_)->GetPageOrdinal(extension_id_);
350   const syncer::StringOrdinal& launch =
351      GetAppSorting(profile_)->GetAppLaunchOrdinal(extension_id_);
352   set_position(syncer::StringOrdinal(
353       page.ToInternalValue() + launch.ToInternalValue()));
354 }
355
356 AppListControllerDelegate* ExtensionAppItem::GetController() {
357   return AppListService::Get(chrome::GetActiveDesktop())->
358       GetControllerDelegate();
359 }