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