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