extension_app_item.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/prefs/pref_service.h" 8#include "chrome/browser/extensions/extension_prefs.h" 9#include "chrome/browser/extensions/extension_service.h" 10#include "chrome/browser/extensions/extension_sorting.h" 11#include "chrome/browser/extensions/extension_system.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/extensions/extension_enable_flow.h" 16#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" 17#include "chrome/common/extensions/extension.h" 18#include "chrome/common/extensions/extension_constants.h" 19#include "chrome/common/extensions/extension_icon_set.h" 20#include "chrome/common/extensions/manifest_handlers/icons_handler.h" 21#include "chrome/common/extensions/manifest_url_handler.h" 22#include "grit/theme_resources.h" 23#include "ui/base/resource/resource_bundle.h" 24#include "ui/gfx/canvas.h" 25#include "ui/gfx/color_utils.h" 26#include "ui/gfx/image/canvas_image_source.h" 27#include "ui/gfx/image/image_skia_operations.h" 28 29using extensions::Extension; 30 31namespace { 32 33// Overlays a shortcut icon over the bottom left corner of a given image. 34class ShortcutOverlayImageSource : public gfx::CanvasImageSource { 35 public: 36 explicit ShortcutOverlayImageSource(const gfx::ImageSkia& icon) 37 : gfx::CanvasImageSource(icon.size(), false), 38 icon_(icon) { 39 } 40 virtual ~ShortcutOverlayImageSource() {} 41 42 private: 43 // gfx::CanvasImageSource overrides: 44 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { 45 canvas->DrawImageInt(icon_, 0, 0); 46 47 // Draw the overlay in the bottom left corner of the icon. 48 const gfx::ImageSkia& overlay = *ui::ResourceBundle::GetSharedInstance(). 49 GetImageSkiaNamed(IDR_APP_LIST_TAB_OVERLAY); 50 canvas->DrawImageInt(overlay, 0, icon_.height() - overlay.height()); 51 } 52 53 gfx::ImageSkia icon_; 54 55 DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource); 56}; 57 58ExtensionSorting* GetExtensionSorting(Profile* profile) { 59 ExtensionService* service = 60 extensions::ExtensionSystem::Get(profile)->extension_service(); 61 return service->extension_prefs()->extension_sorting(); 62} 63 64const color_utils::HSL shift = {-1, 0, 0.6}; 65 66} // namespace 67 68ExtensionAppItem::ExtensionAppItem(Profile* profile, 69 const std::string& extension_id, 70 AppListControllerDelegate* controller, 71 const std::string& extension_name, 72 const gfx::ImageSkia& installing_icon, 73 bool is_platform_app) 74 : ChromeAppListItem(TYPE_APP), 75 profile_(profile), 76 extension_id_(extension_id), 77 controller_(controller), 78 extension_name_(extension_name), 79 installing_icon_( 80 gfx::ImageSkiaOperations::CreateHSLShiftedImage(installing_icon, 81 shift)), 82 is_platform_app_(is_platform_app) { 83 Reload(); 84 GetExtensionSorting(profile_)->EnsureValidOrdinals(extension_id_, 85 syncer::StringOrdinal()); 86} 87 88ExtensionAppItem::~ExtensionAppItem() { 89} 90 91bool ExtensionAppItem::HasOverlay() const { 92#if defined(OS_CHROMEOS) 93 return false; 94#else 95 return !is_platform_app_ && extension_id_ != extension_misc::kChromeAppId; 96#endif 97} 98 99void ExtensionAppItem::Reload() { 100 const Extension* extension = GetExtension(); 101 bool is_installing = !extension; 102 SetIsInstalling(is_installing); 103 set_app_id(extension_id_); 104 if (is_installing) { 105 SetTitle(extension_name_); 106 UpdateIcon(); 107 return; 108 } 109 SetTitle(extension->name()); 110 LoadImage(extension); 111} 112 113syncer::StringOrdinal ExtensionAppItem::GetPageOrdinal() const { 114 return GetExtensionSorting(profile_)->GetPageOrdinal(extension_id_); 115} 116 117syncer::StringOrdinal ExtensionAppItem::GetAppLaunchOrdinal() const { 118 return GetExtensionSorting(profile_)->GetAppLaunchOrdinal(extension_id_); 119} 120 121void ExtensionAppItem::Move(const ExtensionAppItem* prev, 122 const ExtensionAppItem* next) { 123 // Does nothing if no predecessor nor successor. 124 if (!prev && !next) 125 return; 126 127 ExtensionService* service = 128 extensions::ExtensionSystem::Get(profile_)->extension_service(); 129 service->extension_prefs()->SetAppDraggedByUser(extension_id_); 130 131 // Handles only predecessor or only successor case. 132 if (!prev || !next) { 133 syncer::StringOrdinal page = prev ? prev->GetPageOrdinal() : 134 next->GetPageOrdinal(); 135 GetExtensionSorting(profile_)->SetPageOrdinal(extension_id_, page); 136 service->OnExtensionMoved(extension_id_, 137 prev ? prev->extension_id() : std::string(), 138 next ? next->extension_id() : std::string()); 139 return; 140 } 141 142 // Handles both predecessor and successor are on the same page. 143 syncer::StringOrdinal prev_page = prev->GetPageOrdinal(); 144 syncer::StringOrdinal next_page = next->GetPageOrdinal(); 145 if (prev_page.Equals(next_page)) { 146 GetExtensionSorting(profile_)->SetPageOrdinal(extension_id_, prev_page); 147 service->OnExtensionMoved(extension_id_, 148 prev->extension_id(), 149 next->extension_id()); 150 return; 151 } 152 153 // Otherwise, go with |next|. This is okay because app list does not split 154 // page based ntp page ordinal. 155 // TODO(xiyuan): Revisit this when implementing paging support. 156 GetExtensionSorting(profile_)->SetPageOrdinal(extension_id_, prev_page); 157 service->OnExtensionMoved(extension_id_, 158 prev->extension_id(), 159 std::string()); 160} 161 162void ExtensionAppItem::UpdateIcon() { 163 if (!GetExtension()) { 164 gfx::ImageSkia icon = installing_icon_; 165 if (HasOverlay()) 166 icon = gfx::ImageSkia(new ShortcutOverlayImageSource(icon), icon.size()); 167 SetIcon(icon, !HasOverlay()); 168 return; 169 } 170 gfx::ImageSkia icon = icon_->image_skia(); 171 172 const ExtensionService* service = 173 extensions::ExtensionSystem::Get(profile_)->extension_service(); 174 const bool enabled = service->IsExtensionEnabledForLauncher(extension_id_); 175 if (!enabled) { 176 const color_utils::HSL shift = {-1, 0, 0.6}; 177 icon = gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon, shift); 178 } 179 180 if (HasOverlay()) 181 icon = gfx::ImageSkia(new ShortcutOverlayImageSource(icon), icon.size()); 182 183 SetIcon(icon, !HasOverlay()); 184} 185 186const Extension* ExtensionAppItem::GetExtension() const { 187 const ExtensionService* service = 188 extensions::ExtensionSystem::Get(profile_)->extension_service(); 189 const Extension* extension = service->GetInstalledExtension(extension_id_); 190 return extension; 191} 192 193void ExtensionAppItem::LoadImage(const Extension* extension) { 194 icon_.reset(new extensions::IconImage( 195 profile_, 196 extension, 197 extensions::IconsInfo::GetIcons(extension), 198 extension_misc::EXTENSION_ICON_MEDIUM, 199 extensions::IconsInfo::GetDefaultAppIcon(), 200 this)); 201 UpdateIcon(); 202} 203 204bool ExtensionAppItem::RunExtensionEnableFlow() { 205 const ExtensionService* service = 206 extensions::ExtensionSystem::Get(profile_)->extension_service(); 207 if (service->IsExtensionEnabledForLauncher(extension_id_)) 208 return false; 209 210 if (!extension_enable_flow_) { 211 controller_->OnShowExtensionPrompt(); 212 213 extension_enable_flow_.reset(new ExtensionEnableFlow( 214 profile_, extension_id_, this)); 215 extension_enable_flow_->StartForNativeWindow( 216 controller_->GetAppListWindow()); 217 } 218 return true; 219} 220 221void ExtensionAppItem::Launch(int event_flags) { 222 // |extension| could be NULL when it is being unloaded for updating. 223 const Extension* extension = GetExtension(); 224 if (!extension) 225 return; 226 227 if (RunExtensionEnableFlow()) 228 return; 229 230 controller_->LaunchApp(profile_, extension, event_flags); 231} 232 233void ExtensionAppItem::OnExtensionIconImageChanged( 234 extensions::IconImage* image) { 235 DCHECK(icon_.get() == image); 236 UpdateIcon(); 237} 238 239void ExtensionAppItem::ExtensionEnableFlowFinished() { 240 extension_enable_flow_.reset(); 241 controller_->OnCloseExtensionPrompt(); 242 243 // Automatically launch app after enabling. 244 Launch(ui::EF_NONE); 245} 246 247void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated) { 248 extension_enable_flow_.reset(); 249 controller_->OnCloseExtensionPrompt(); 250} 251 252void ExtensionAppItem::Activate(int event_flags) { 253 // |extension| could be NULL when it is being unloaded for updating. 254 const Extension* extension = GetExtension(); 255 if (!extension) 256 return; 257 258 if (RunExtensionEnableFlow()) 259 return; 260 261 AppLauncherHandler::RecordAppListMainLaunch(extension); 262 controller_->ActivateApp(profile_, extension, event_flags); 263} 264 265ui::MenuModel* ExtensionAppItem::GetContextMenuModel() { 266 if (!context_menu_) { 267 context_menu_.reset(new app_list::AppContextMenu( 268 this, profile_, extension_id_, controller_, is_platform_app_)); 269 } 270 271 return context_menu_->GetMenuModel(); 272} 273 274void ExtensionAppItem::ExecuteLaunchCommand(int event_flags) { 275 Launch(event_flags); 276} 277