NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / ui / app_list / extension_app_item.cc
blob690a3e8d6e8297ebe1ee328759129e8d35f58ff0
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/ui/app_list/extension_app_item.h"
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/extension_icon_set.h"
22 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
23 #include "chrome/common/extensions/manifest_url_handler.h"
24 #include "content/public/browser/user_metrics.h"
25 #include "extensions/browser/app_sorting.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/common/extension.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"
38 using extensions::Extension;
40 namespace {
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) {
49 virtual ~ShortcutOverlayImageSource() {}
51 private:
52 // gfx::CanvasImageSource overrides:
53 virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
54 canvas->DrawImageInt(icon_, 0, 0);
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());
62 gfx::ImageSkia icon_;
64 DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource);
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) {
74 virtual ~RoundedCornersImageSource() {}
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;
82 canvas->DrawImageInt(icon_, 0, 0);
84 scoped_ptr<gfx::Canvas> masking_canvas(
85 new gfx::Canvas(gfx::Size(icon_.width(), icon_.height()), 1.0f, false));
86 DCHECK(masking_canvas);
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);
95 SkPaint masking_paint;
96 masking_paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
97 canvas->DrawImageInt(
98 gfx::ImageSkia(masking_canvas->ExtractImageRep()), 0, 0, masking_paint);
101 gfx::ImageSkia icon_;
103 DISALLOW_COPY_AND_ASSIGN(RoundedCornersImageSource);
106 extensions::AppSorting* GetAppSorting(Profile* profile) {
107 ExtensionService* service =
108 extensions::ExtensionSystem::Get(profile)->extension_service();
109 return service->extension_prefs()->app_sorting();
112 const color_utils::HSL shift = {-1, 0, 0.6};
114 } // namespace
116 ExtensionAppItem::ExtensionAppItem(
117 Profile* profile,
118 const app_list::AppListSyncableService::SyncItem* sync_item,
119 const std::string& extension_id,
120 const std::string& extension_name,
121 const gfx::ImageSkia& installing_icon,
122 bool is_platform_app)
123 : app_list::AppListItem(extension_id),
124 profile_(profile),
125 extension_id_(extension_id),
126 extension_enable_flow_controller_(NULL),
127 extension_name_(extension_name),
128 installing_icon_(
129 gfx::ImageSkiaOperations::CreateHSLShiftedImage(installing_icon,
130 shift)),
131 is_platform_app_(is_platform_app),
132 has_overlay_(false) {
133 Reload();
134 if (sync_item && sync_item->item_ordinal.IsValid()) {
135 // An existing synced position exists, use that.
136 set_position(sync_item->item_ordinal);
137 if (title().empty())
138 SetTitleAndFullName(sync_item->item_name, sync_item->item_name);
139 return;
141 GetAppSorting(profile_)->EnsureValidOrdinals(extension_id_,
142 syncer::StringOrdinal());
143 UpdatePositionFromExtensionOrdering();
146 ExtensionAppItem::~ExtensionAppItem() {
149 bool ExtensionAppItem::NeedsOverlay() const {
150 // The overlay icon is disabled for hosted apps in windowed mode with
151 // streamlined hosted apps.
152 bool streamlined_hosted_apps = CommandLine::ForCurrentProcess()->
153 HasSwitch(switches::kEnableStreamlinedHostedApps);
154 #if defined(OS_CHROMEOS)
155 if (!streamlined_hosted_apps)
156 return false;
157 #endif
158 const ExtensionService* service =
159 extensions::ExtensionSystem::Get(profile_)->extension_service();
161 extensions::LaunchType launch_type = GetExtension()
162 ? extensions::GetLaunchType(service->extension_prefs(), GetExtension())
163 : extensions::LAUNCH_TYPE_WINDOW;
165 return !is_platform_app_ && extension_id_ != extension_misc::kChromeAppId &&
166 (!streamlined_hosted_apps ||
167 launch_type != extensions::LAUNCH_TYPE_WINDOW);
170 void ExtensionAppItem::Reload() {
171 const Extension* extension = GetExtension();
172 bool is_installing = !extension;
173 SetIsInstalling(is_installing);
174 if (is_installing) {
175 SetTitleAndFullName(extension_name_, extension_name_);
176 UpdateIcon();
177 return;
179 SetTitleAndFullName(extension->short_name(), extension->name());
180 LoadImage(extension);
183 void ExtensionAppItem::UpdateIcon() {
184 gfx::ImageSkia icon = installing_icon_;
186 // Use the app icon if the app exists. Turn the image greyscale if the app is
187 // not launchable.
188 if (GetExtension()) {
189 icon = icon_->image_skia();
190 const bool enabled = extensions::util::IsAppLaunchable(extension_id_,
191 profile_);
192 if (!enabled) {
193 const color_utils::HSL shift = {-1, 0, 0.6};
194 icon = gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon, shift);
197 if (GetExtension()->from_bookmark())
198 icon = gfx::ImageSkia(new RoundedCornersImageSource(icon), icon.size());
200 // Paint the shortcut overlay if necessary.
201 has_overlay_ = NeedsOverlay();
202 if (has_overlay_)
203 icon = gfx::ImageSkia(new ShortcutOverlayImageSource(icon), icon.size());
205 SetIcon(icon, true);
208 void ExtensionAppItem::UpdateIconOverlay() {
209 if (has_overlay_ != NeedsOverlay())
210 UpdateIcon();
213 void ExtensionAppItem::Move(const ExtensionAppItem* prev,
214 const ExtensionAppItem* next) {
215 if (!prev && !next)
216 return; // No reordering necessary
218 ExtensionService* service =
219 extensions::ExtensionSystem::Get(profile_)->extension_service();
220 extensions::AppSorting* sorting = service->extension_prefs()->app_sorting();
222 syncer::StringOrdinal page;
223 std::string prev_id, next_id;
224 if (!prev) {
225 next_id = next->extension_id();
226 page = sorting->GetPageOrdinal(next_id);
227 } else if (!next) {
228 prev_id = prev->extension_id();
229 page = sorting->GetPageOrdinal(prev_id);
230 } else {
231 prev_id = prev->extension_id();
232 page = sorting->GetPageOrdinal(prev_id);
233 // Only set |next_id| if on the same page, otherwise just insert after prev.
234 if (page.Equals(sorting->GetPageOrdinal(next->extension_id())))
235 next_id = next->extension_id();
237 service->extension_prefs()->SetAppDraggedByUser(extension_id_);
238 sorting->SetPageOrdinal(extension_id_, page);
239 service->OnExtensionMoved(extension_id_, prev_id, next_id);
240 UpdatePositionFromExtensionOrdering();
243 const Extension* ExtensionAppItem::GetExtension() const {
244 const ExtensionService* service =
245 extensions::ExtensionSystem::Get(profile_)->extension_service();
246 const Extension* extension = service->GetInstalledExtension(extension_id_);
247 return extension;
250 void ExtensionAppItem::LoadImage(const Extension* extension) {
251 icon_.reset(new extensions::IconImage(
252 profile_,
253 extension,
254 extensions::IconsInfo::GetIcons(extension),
255 extension_misc::EXTENSION_ICON_MEDIUM,
256 extensions::IconsInfo::GetDefaultAppIcon(),
257 this));
258 UpdateIcon();
261 bool ExtensionAppItem::RunExtensionEnableFlow() {
262 if (extensions::util::IsAppLaunchableWithoutEnabling(extension_id_, profile_))
263 return false;
265 if (!extension_enable_flow_) {
266 extension_enable_flow_controller_ = GetController();
267 extension_enable_flow_controller_->OnShowExtensionPrompt();
269 extension_enable_flow_.reset(new ExtensionEnableFlow(
270 profile_, extension_id_, this));
271 extension_enable_flow_->StartForNativeWindow(
272 extension_enable_flow_controller_->GetAppListWindow());
274 return true;
277 void ExtensionAppItem::Launch(int event_flags) {
278 // |extension| could be NULL when it is being unloaded for updating.
279 const Extension* extension = GetExtension();
280 if (!extension)
281 return;
283 if (RunExtensionEnableFlow())
284 return;
286 GetController()->LaunchApp(profile_,
287 extension,
288 AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
289 event_flags);
292 void ExtensionAppItem::OnExtensionIconImageChanged(
293 extensions::IconImage* image) {
294 DCHECK(icon_.get() == image);
295 UpdateIcon();
298 void ExtensionAppItem::ExtensionEnableFlowFinished() {
299 extension_enable_flow_.reset();
300 extension_enable_flow_controller_->OnCloseExtensionPrompt();
301 extension_enable_flow_controller_ = NULL;
303 // Automatically launch app after enabling.
304 Launch(ui::EF_NONE);
307 void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated) {
308 extension_enable_flow_.reset();
309 extension_enable_flow_controller_->OnCloseExtensionPrompt();
310 extension_enable_flow_controller_ = NULL;
313 void ExtensionAppItem::Activate(int event_flags) {
314 // |extension| could be NULL when it is being unloaded for updating.
315 const Extension* extension = GetExtension();
316 if (!extension)
317 return;
319 if (RunExtensionEnableFlow())
320 return;
322 content::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
323 CoreAppLauncherHandler::RecordAppListMainLaunch(extension);
324 GetController()->ActivateApp(profile_,
325 extension,
326 AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
327 event_flags);
330 ui::MenuModel* ExtensionAppItem::GetContextMenuModel() {
331 context_menu_.reset(new app_list::AppContextMenu(
332 this, profile_, extension_id_, GetController()));
333 context_menu_->set_is_platform_app(is_platform_app_);
334 if (IsInFolder())
335 context_menu_->set_is_in_folder(true);
336 return context_menu_->GetMenuModel();
339 // static
340 const char ExtensionAppItem::kItemType[] = "ExtensionAppItem";
342 const char* ExtensionAppItem::GetItemType() const {
343 return ExtensionAppItem::kItemType;
346 void ExtensionAppItem::ExecuteLaunchCommand(int event_flags) {
347 Launch(event_flags);
350 void ExtensionAppItem::UpdatePositionFromExtensionOrdering() {
351 const syncer::StringOrdinal& page =
352 GetAppSorting(profile_)->GetPageOrdinal(extension_id_);
353 const syncer::StringOrdinal& launch =
354 GetAppSorting(profile_)->GetAppLaunchOrdinal(extension_id_);
355 set_position(syncer::StringOrdinal(
356 page.ToInternalValue() + launch.ToInternalValue()));
359 AppListControllerDelegate* ExtensionAppItem::GetController() {
360 return AppListService::Get(chrome::GetActiveDesktop())->
361 GetControllerDelegate();