| OLD | NEW |
| (Empty) |
| 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/ash/launcher/chrome_launcher_controller_per_browser.
h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "ash/ash_switches.h" | |
| 10 #include "ash/launcher/launcher_model.h" | |
| 11 #include "ash/root_window_controller.h" | |
| 12 #include "ash/shelf/shelf_layout_manager.h" | |
| 13 #include "ash/shelf/shelf_widget.h" | |
| 14 #include "ash/shell.h" | |
| 15 #include "ash/wm/window_util.h" | |
| 16 #include "base/command_line.h" | |
| 17 #include "base/strings/string_number_conversions.h" | |
| 18 #include "base/values.h" | |
| 19 #include "chrome/browser/app_mode/app_mode_utils.h" | |
| 20 #include "chrome/browser/defaults.h" | |
| 21 #include "chrome/browser/chrome_notification_types.h" | |
| 22 #include "chrome/browser/extensions/app_icon_loader_impl.h" | |
| 23 #include "chrome/browser/extensions/extension_service.h" | |
| 24 #include "chrome/browser/extensions/extension_system.h" | |
| 25 #include "chrome/browser/prefs/incognito_mode_prefs.h" | |
| 26 #include "chrome/browser/prefs/pref_service_syncable.h" | |
| 27 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
| 28 #include "chrome/browser/profiles/profile.h" | |
| 29 #include "chrome/browser/profiles/profile_manager.h" | |
| 30 #include "chrome/browser/ui/ash/app_sync_ui_state.h" | |
| 31 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" | |
| 32 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h" | |
| 33 #include "chrome/browser/ui/ash/launcher/launcher_app_tab_helper.h" | |
| 34 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h" | |
| 35 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h" | |
| 36 #include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h" | |
| 37 #include "chrome/browser/ui/browser.h" | |
| 38 #include "chrome/browser/ui/browser_commands.h" | |
| 39 #include "chrome/browser/ui/browser_finder.h" | |
| 40 #include "chrome/browser/ui/browser_tabstrip.h" | |
| 41 #include "chrome/browser/ui/browser_window.h" | |
| 42 #include "chrome/browser/ui/extensions/application_launch.h" | |
| 43 #include "chrome/browser/ui/extensions/extension_enable_flow.h" | |
| 44 #include "chrome/browser/ui/host_desktop.h" | |
| 45 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 46 #include "chrome/browser/web_applications/web_app.h" | |
| 47 #include "chrome/common/chrome_switches.h" | |
| 48 #include "chrome/common/extensions/extension.h" | |
| 49 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | |
| 50 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" | |
| 51 #include "chrome/common/pref_names.h" | |
| 52 #include "chrome/common/url_constants.h" | |
| 53 #include "content/public/browser/navigation_entry.h" | |
| 54 #include "content/public/browser/notification_service.h" | |
| 55 #include "content/public/browser/web_contents.h" | |
| 56 #include "extensions/common/url_pattern.h" | |
| 57 #include "grit/chromium_strings.h" | |
| 58 #include "grit/theme_resources.h" | |
| 59 #include "ui/aura/root_window.h" | |
| 60 #include "ui/aura/window.h" | |
| 61 #include "ui/base/l10n/l10n_util.h" | |
| 62 | |
| 63 #if defined(OS_CHROMEOS) | |
| 64 #include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h" | |
| 65 #endif | |
| 66 | |
| 67 using content::WebContents; | |
| 68 using extensions::Extension; | |
| 69 | |
| 70 namespace { | |
| 71 | |
| 72 // Item controller for an app shortcut. Shortcuts track app and launcher ids, | |
| 73 // but do not have any associated windows (opening a shortcut will replace the | |
| 74 // item with the appropriate LauncherItemController type). | |
| 75 class AppShortcutLauncherItemController : public LauncherItemController { | |
| 76 public: | |
| 77 AppShortcutLauncherItemController( | |
| 78 const std::string& app_id, | |
| 79 ChromeLauncherControllerPerBrowser* controller) | |
| 80 : LauncherItemController(TYPE_SHORTCUT, app_id, controller) { | |
| 81 // Google Drive should just refocus to it's main app UI. | |
| 82 // TODO(davemoore): Generalize this for other applications. | |
| 83 if (app_id == "apdfllckaahabafndbhieahigkjlhalf") { | |
| 84 const Extension* extension = | |
| 85 launcher_controller()->GetExtensionForAppID(app_id); | |
| 86 refocus_url_ = GURL( | |
| 87 extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 virtual ~AppShortcutLauncherItemController() {} | |
| 92 | |
| 93 // LauncherItemController overrides: | |
| 94 virtual string16 GetTitle() OVERRIDE { | |
| 95 return GetAppTitle(); | |
| 96 } | |
| 97 | |
| 98 virtual bool IsCurrentlyShownInWindow(aura::Window* window) const OVERRIDE { | |
| 99 return false; | |
| 100 } | |
| 101 | |
| 102 virtual bool IsOpen() const OVERRIDE { | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 virtual bool IsVisible() const OVERRIDE { | |
| 107 return false; | |
| 108 } | |
| 109 | |
| 110 virtual void Launch(int event_flags) OVERRIDE { | |
| 111 launcher_controller()->LaunchApp(app_id(), event_flags); | |
| 112 } | |
| 113 | |
| 114 virtual void Activate() OVERRIDE { | |
| 115 launcher_controller()->ActivateApp(app_id(), ui::EF_NONE); | |
| 116 } | |
| 117 | |
| 118 virtual void Close() OVERRIDE { | |
| 119 // TODO: maybe should treat as unpin? | |
| 120 } | |
| 121 | |
| 122 virtual void Clicked(const ui::Event& event) OVERRIDE { | |
| 123 Activate(); | |
| 124 } | |
| 125 | |
| 126 virtual void OnRemoved() OVERRIDE { | |
| 127 // AppShortcutLauncherItemController is unowned; delete on removal. | |
| 128 delete this; | |
| 129 } | |
| 130 | |
| 131 virtual void LauncherItemChanged( | |
| 132 int model_index, | |
| 133 const ash::LauncherItem& old_item) OVERRIDE { | |
| 134 } | |
| 135 | |
| 136 virtual ChromeLauncherAppMenuItems GetApplicationList( | |
| 137 int event_flags) OVERRIDE { | |
| 138 ChromeLauncherAppMenuItems items; | |
| 139 return items.Pass(); | |
| 140 } | |
| 141 | |
| 142 // Stores the optional refocus url pattern for this item. | |
| 143 const GURL& refocus_url() const { return refocus_url_; } | |
| 144 void set_refocus_url(const GURL& refocus_url) { refocus_url_ = refocus_url; } | |
| 145 | |
| 146 private: | |
| 147 GURL refocus_url_; | |
| 148 DISALLOW_COPY_AND_ASSIGN(AppShortcutLauncherItemController); | |
| 149 }; | |
| 150 | |
| 151 std::string GetPrefKeyForRootWindow(aura::RootWindow* root_window) { | |
| 152 gfx::Display display = gfx::Screen::GetScreenFor( | |
| 153 root_window)->GetDisplayNearestWindow(root_window); | |
| 154 DCHECK(display.is_valid()); | |
| 155 | |
| 156 return base::Int64ToString(display.id()); | |
| 157 } | |
| 158 | |
| 159 void UpdatePerDisplayPref(PrefService* pref_service, | |
| 160 aura::RootWindow* root_window, | |
| 161 const char* pref_key, | |
| 162 const std::string& value) { | |
| 163 std::string key = GetPrefKeyForRootWindow(root_window); | |
| 164 if (key.empty()) | |
| 165 return; | |
| 166 | |
| 167 DictionaryPrefUpdate update(pref_service, prefs::kShelfPreferences); | |
| 168 base::DictionaryValue* shelf_prefs = update.Get(); | |
| 169 base::DictionaryValue* prefs = NULL; | |
| 170 if (!shelf_prefs->GetDictionary(key, &prefs)) { | |
| 171 prefs = new base::DictionaryValue(); | |
| 172 shelf_prefs->Set(key, prefs); | |
| 173 } | |
| 174 prefs->SetStringWithoutPathExpansion(pref_key, value); | |
| 175 } | |
| 176 | |
| 177 // Returns a pref value in |pref_service| for the display of |root_window|. The | |
| 178 // pref value is stored in |local_path| and |path|, but |pref_service| may have | |
| 179 // per-display preferences and the value can be specified by policy. Here is | |
| 180 // the priority: | |
| 181 // * A value managed by policy. This is a single value that applies to all | |
| 182 // displays. | |
| 183 // * A user-set value for the specified display. | |
| 184 // * A user-set value in |local_path| or |path|, if no per-display settings are | |
| 185 // ever specified (see http://crbug.com/173719 for why). |local_path| is | |
| 186 // preferred. See comment in |kShelfAlignment| as to why we consider two | |
| 187 // prefs and why |local_path| is preferred. | |
| 188 // * A value recommended by policy. This is a single value that applies to all | |
| 189 // root windows. | |
| 190 // * The default value for |local_path| if the value is not recommended by | |
| 191 // policy. | |
| 192 std::string GetPrefForRootWindow(PrefService* pref_service, | |
| 193 aura::RootWindow* root_window, | |
| 194 const char* local_path, | |
| 195 const char* path) { | |
| 196 const PrefService::Preference* local_pref = | |
| 197 pref_service->FindPreference(local_path); | |
| 198 const std::string value(pref_service->GetString(local_path)); | |
| 199 if (local_pref->IsManaged()) | |
| 200 return value; | |
| 201 | |
| 202 std::string pref_key = GetPrefKeyForRootWindow(root_window); | |
| 203 bool has_per_display_prefs = false; | |
| 204 if (!pref_key.empty()) { | |
| 205 const base::DictionaryValue* shelf_prefs = pref_service->GetDictionary( | |
| 206 prefs::kShelfPreferences); | |
| 207 const base::DictionaryValue* display_pref = NULL; | |
| 208 std::string per_display_value; | |
| 209 if (shelf_prefs->GetDictionary(pref_key, &display_pref) && | |
| 210 display_pref->GetString(path, &per_display_value)) | |
| 211 return per_display_value; | |
| 212 | |
| 213 // If the pref for the specified display is not found, scan the whole prefs | |
| 214 // and check if the prefs for other display is already specified. | |
| 215 std::string unused_value; | |
| 216 for (base::DictionaryValue::Iterator iter(*shelf_prefs); | |
| 217 !iter.IsAtEnd(); iter.Advance()) { | |
| 218 const base::DictionaryValue* display_pref = NULL; | |
| 219 if (iter.value().GetAsDictionary(&display_pref) && | |
| 220 display_pref->GetString(path, &unused_value)) { | |
| 221 has_per_display_prefs = true; | |
| 222 break; | |
| 223 } | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 if (local_pref->IsRecommended() || !has_per_display_prefs) | |
| 228 return value; | |
| 229 | |
| 230 const base::Value* default_value = | |
| 231 pref_service->GetDefaultPrefValue(local_path); | |
| 232 std::string default_string; | |
| 233 default_value->GetAsString(&default_string); | |
| 234 return default_string; | |
| 235 } | |
| 236 | |
| 237 // If prefs have synced and no user-set value exists at |local_path|, the value | |
| 238 // from |synced_path| is copied to |local_path|. | |
| 239 void MaybePropagatePrefToLocal(PrefServiceSyncable* pref_service, | |
| 240 const char* local_path, | |
| 241 const char* synced_path) { | |
| 242 if (!pref_service->FindPreference(local_path)->HasUserSetting() && | |
| 243 pref_service->IsSyncing()) { | |
| 244 // First time the user is using this machine, propagate from remote to | |
| 245 // local. | |
| 246 pref_service->SetString(local_path, pref_service->GetString(synced_path)); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 } // namespace | |
| 251 | |
| 252 // ChromeLauncherControllerPerBrowser ----------------------------------------- | |
| 253 | |
| 254 ChromeLauncherControllerPerBrowser::ChromeLauncherControllerPerBrowser( | |
| 255 Profile* profile, | |
| 256 ash::LauncherModel* model) | |
| 257 : model_(model), | |
| 258 profile_(profile), | |
| 259 app_sync_ui_state_(NULL), | |
| 260 ignore_persist_pinned_state_change_(false) { | |
| 261 if (!profile_) { | |
| 262 // Use the original profile as on chromeos we may get a temporary off the | |
| 263 // record profile. | |
| 264 profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile(); | |
| 265 | |
| 266 app_sync_ui_state_ = AppSyncUIState::Get(profile_); | |
| 267 if (app_sync_ui_state_) | |
| 268 app_sync_ui_state_->AddObserver(this); | |
| 269 } | |
| 270 | |
| 271 model_->AddObserver(this); | |
| 272 // Right now ash::Shell isn't created for tests. | |
| 273 // TODO(mukai): Allows it to observe display change and write tests. | |
| 274 if (ash::Shell::HasInstance()) | |
| 275 ash::Shell::GetInstance()->display_controller()->AddObserver(this); | |
| 276 // TODO(stevenjb): Find a better owner for shell_window_controller_? | |
| 277 shell_window_controller_.reset(new ShellWindowLauncherController(this)); | |
| 278 app_tab_helper_.reset(new LauncherAppTabHelper(profile_)); | |
| 279 app_icon_loader_.reset(new extensions::AppIconLoaderImpl( | |
| 280 profile_, extension_misc::EXTENSION_ICON_SMALL, this)); | |
| 281 | |
| 282 notification_registrar_.Add(this, | |
| 283 chrome::NOTIFICATION_EXTENSION_LOADED, | |
| 284 content::Source<Profile>(profile_)); | |
| 285 notification_registrar_.Add(this, | |
| 286 chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
| 287 content::Source<Profile>(profile_)); | |
| 288 pref_change_registrar_.Init(profile_->GetPrefs()); | |
| 289 pref_change_registrar_.Add( | |
| 290 prefs::kPinnedLauncherApps, | |
| 291 base::Bind(&ChromeLauncherControllerPerBrowser:: | |
| 292 UpdateAppLaunchersFromPref, | |
| 293 base::Unretained(this))); | |
| 294 pref_change_registrar_.Add( | |
| 295 prefs::kShelfAlignmentLocal, | |
| 296 base::Bind(&ChromeLauncherControllerPerBrowser:: | |
| 297 SetShelfAlignmentFromPrefs, | |
| 298 base::Unretained(this))); | |
| 299 pref_change_registrar_.Add( | |
| 300 prefs::kShelfAutoHideBehaviorLocal, | |
| 301 base::Bind(&ChromeLauncherControllerPerBrowser:: | |
| 302 SetShelfAutoHideBehaviorFromPrefs, | |
| 303 base::Unretained(this))); | |
| 304 pref_change_registrar_.Add( | |
| 305 prefs::kShelfPreferences, | |
| 306 base::Bind(&ChromeLauncherControllerPerBrowser:: | |
| 307 SetShelfBehaviorsFromPrefs, | |
| 308 base::Unretained(this))); | |
| 309 } | |
| 310 | |
| 311 ChromeLauncherControllerPerBrowser::~ChromeLauncherControllerPerBrowser() { | |
| 312 // Reset the shell window controller here since it has a weak pointer to | |
| 313 // this. | |
| 314 shell_window_controller_.reset(); | |
| 315 | |
| 316 for (std::set<ash::Launcher*>::iterator iter = launchers_.begin(); | |
| 317 iter != launchers_.end(); | |
| 318 ++iter) | |
| 319 (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this); | |
| 320 | |
| 321 model_->RemoveObserver(this); | |
| 322 if (ash::Shell::HasInstance()) | |
| 323 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this); | |
| 324 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin(); | |
| 325 i != id_to_item_controller_map_.end(); ++i) { | |
| 326 i->second->OnRemoved(); | |
| 327 model_->RemoveItemAt(model_->ItemIndexByID(i->first)); | |
| 328 } | |
| 329 | |
| 330 if (ash::Shell::HasInstance()) | |
| 331 ash::Shell::GetInstance()->RemoveShellObserver(this); | |
| 332 | |
| 333 if (app_sync_ui_state_) | |
| 334 app_sync_ui_state_->RemoveObserver(this); | |
| 335 | |
| 336 PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this); | |
| 337 } | |
| 338 | |
| 339 void ChromeLauncherControllerPerBrowser::Init() { | |
| 340 UpdateAppLaunchersFromPref(); | |
| 341 CreateBrowserShortcutLauncherItem(); | |
| 342 | |
| 343 // TODO(sky): update unit test so that this test isn't necessary. | |
| 344 if (ash::Shell::HasInstance()) { | |
| 345 SetShelfAutoHideBehaviorFromPrefs(); | |
| 346 SetShelfAlignmentFromPrefs(); | |
| 347 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); | |
| 348 if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() || | |
| 349 !prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)-> | |
| 350 HasUserSetting()) { | |
| 351 // This causes OnIsSyncingChanged to be called when the value of | |
| 352 // PrefService::IsSyncing() changes. | |
| 353 prefs->AddObserver(this); | |
| 354 } | |
| 355 ash::Shell::GetInstance()->AddShellObserver(this); | |
| 356 } | |
| 357 } | |
| 358 | |
| 359 ChromeLauncherControllerPerApp* | |
| 360 ChromeLauncherControllerPerBrowser::GetPerAppInterface() { | |
| 361 return NULL; | |
| 362 } | |
| 363 | |
| 364 ash::LauncherID ChromeLauncherControllerPerBrowser::CreateTabbedLauncherItem( | |
| 365 LauncherItemController* controller, | |
| 366 IncognitoState is_incognito, | |
| 367 ash::LauncherItemStatus status) { | |
| 368 ash::LauncherID id = model_->next_id(); | |
| 369 DCHECK(!HasItemController(id)); | |
| 370 DCHECK(controller); | |
| 371 id_to_item_controller_map_[id] = controller; | |
| 372 controller->set_launcher_id(id); | |
| 373 | |
| 374 ash::LauncherItem item; | |
| 375 item.type = ash::TYPE_TABBED; | |
| 376 item.is_incognito = (is_incognito == STATE_INCOGNITO); | |
| 377 item.status = status; | |
| 378 model_->Add(item); | |
| 379 return id; | |
| 380 } | |
| 381 | |
| 382 ash::LauncherID ChromeLauncherControllerPerBrowser::CreateAppLauncherItem( | |
| 383 LauncherItemController* controller, | |
| 384 const std::string& app_id, | |
| 385 ash::LauncherItemStatus status) { | |
| 386 DCHECK(controller); | |
| 387 int index = 0; | |
| 388 // Panels are inserted on the left so as not to push all existing panels over. | |
| 389 if (controller->GetLauncherItemType() != ash::TYPE_APP_PANEL) { | |
| 390 index = model_->item_count(); | |
| 391 // For the alternate shelf layout increment index (insert after app icon). | |
| 392 if (ash::switches::UseAlternateShelfLayout()) | |
| 393 ++index; | |
| 394 } | |
| 395 return InsertAppLauncherItem(controller, app_id, status, index); | |
| 396 } | |
| 397 | |
| 398 void ChromeLauncherControllerPerBrowser::SetItemStatus( | |
| 399 ash::LauncherID id, | |
| 400 ash::LauncherItemStatus status) { | |
| 401 int index = model_->ItemIndexByID(id); | |
| 402 DCHECK_GE(index, 0); | |
| 403 ash::LauncherItem item = model_->items()[index]; | |
| 404 item.status = status; | |
| 405 model_->Set(index, item); | |
| 406 } | |
| 407 | |
| 408 void ChromeLauncherControllerPerBrowser::SetItemController( | |
| 409 ash::LauncherID id, | |
| 410 LauncherItemController* controller) { | |
| 411 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 412 DCHECK(iter != id_to_item_controller_map_.end()); | |
| 413 iter->second->OnRemoved(); | |
| 414 iter->second = controller; | |
| 415 controller->set_launcher_id(id); | |
| 416 } | |
| 417 | |
| 418 void ChromeLauncherControllerPerBrowser::CloseLauncherItem( | |
| 419 ash::LauncherID id) { | |
| 420 if (IsPinned(id)) { | |
| 421 // Create a new shortcut controller. | |
| 422 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 423 DCHECK(iter != id_to_item_controller_map_.end()); | |
| 424 SetItemStatus(id, ash::STATUS_CLOSED); | |
| 425 std::string app_id = iter->second->app_id(); | |
| 426 iter->second->OnRemoved(); | |
| 427 iter->second = new AppShortcutLauncherItemController(app_id, this); | |
| 428 iter->second->set_launcher_id(id); | |
| 429 } else { | |
| 430 LauncherItemClosed(id); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 void ChromeLauncherControllerPerBrowser::Unpin(ash::LauncherID id) { | |
| 435 DCHECK(HasItemController(id)); | |
| 436 | |
| 437 LauncherItemController* controller = id_to_item_controller_map_[id]; | |
| 438 if (controller->type() == LauncherItemController::TYPE_APP) { | |
| 439 int index = model_->ItemIndexByID(id); | |
| 440 ash::LauncherItem item = model_->items()[index]; | |
| 441 item.type = ash::TYPE_PLATFORM_APP; | |
| 442 model_->Set(index, item); | |
| 443 } else { | |
| 444 LauncherItemClosed(id); | |
| 445 } | |
| 446 if (CanPin()) | |
| 447 PersistPinnedState(); | |
| 448 } | |
| 449 | |
| 450 void ChromeLauncherControllerPerBrowser::Pin(ash::LauncherID id) { | |
| 451 DCHECK(HasItemController(id)); | |
| 452 | |
| 453 int index = model_->ItemIndexByID(id); | |
| 454 ash::LauncherItem item = model_->items()[index]; | |
| 455 | |
| 456 if (item.type != ash::TYPE_PLATFORM_APP) | |
| 457 return; | |
| 458 | |
| 459 item.type = ash::TYPE_APP_SHORTCUT; | |
| 460 model_->Set(index, item); | |
| 461 | |
| 462 if (CanPin()) | |
| 463 PersistPinnedState(); | |
| 464 } | |
| 465 | |
| 466 bool ChromeLauncherControllerPerBrowser::IsPinned(ash::LauncherID id) { | |
| 467 int index = model_->ItemIndexByID(id); | |
| 468 ash::LauncherItemType type = model_->items()[index].type; | |
| 469 return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT); | |
| 470 } | |
| 471 | |
| 472 void ChromeLauncherControllerPerBrowser::TogglePinned(ash::LauncherID id) { | |
| 473 if (!HasItemController(id)) | |
| 474 return; // May happen if item closed with menu open. | |
| 475 | |
| 476 if (IsPinned(id)) | |
| 477 Unpin(id); | |
| 478 else | |
| 479 Pin(id); | |
| 480 } | |
| 481 | |
| 482 bool ChromeLauncherControllerPerBrowser::IsPinnable(ash::LauncherID id) const { | |
| 483 int index = model_->ItemIndexByID(id); | |
| 484 if (index == -1) | |
| 485 return false; | |
| 486 | |
| 487 ash::LauncherItemType type = model_->items()[index].type; | |
| 488 return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP) && | |
| 489 CanPin()); | |
| 490 } | |
| 491 | |
| 492 void ChromeLauncherControllerPerBrowser::LockV1AppWithID( | |
| 493 const std::string& app_id) { | |
| 494 } | |
| 495 | |
| 496 void ChromeLauncherControllerPerBrowser::UnlockV1AppWithID( | |
| 497 const std::string& app_id) { | |
| 498 } | |
| 499 | |
| 500 void ChromeLauncherControllerPerBrowser::Launch( | |
| 501 ash::LauncherID id, int event_flags) { | |
| 502 if (!HasItemController(id)) | |
| 503 return; // In case invoked from menu and item closed while menu up. | |
| 504 id_to_item_controller_map_[id]->Launch(event_flags); | |
| 505 } | |
| 506 | |
| 507 void ChromeLauncherControllerPerBrowser::Close(ash::LauncherID id) { | |
| 508 if (!HasItemController(id)) | |
| 509 return; // May happen if menu closed. | |
| 510 id_to_item_controller_map_[id]->Close(); | |
| 511 } | |
| 512 | |
| 513 bool ChromeLauncherControllerPerBrowser::IsOpen(ash::LauncherID id) { | |
| 514 if (!HasItemController(id)) | |
| 515 return false; | |
| 516 return id_to_item_controller_map_[id]->IsOpen(); | |
| 517 } | |
| 518 | |
| 519 bool ChromeLauncherControllerPerBrowser::IsPlatformApp(ash::LauncherID id) { | |
| 520 if (!HasItemController(id)) | |
| 521 return false; | |
| 522 | |
| 523 std::string app_id = GetAppIDForLauncherID(id); | |
| 524 const Extension* extension = GetExtensionForAppID(app_id); | |
| 525 DCHECK(extension); | |
| 526 return extension->is_platform_app(); | |
| 527 } | |
| 528 | |
| 529 void ChromeLauncherControllerPerBrowser::LaunchApp(const std::string& app_id, | |
| 530 int event_flags) { | |
| 531 // |extension| could be NULL when it is being unloaded for updating. | |
| 532 const Extension* extension = GetExtensionForAppID(app_id); | |
| 533 if (!extension) | |
| 534 return; | |
| 535 | |
| 536 const ExtensionService* service = | |
| 537 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 538 if (!service->IsExtensionEnabledForLauncher(app_id)) { | |
| 539 // Do nothing if there is already a running enable flow. | |
| 540 if (extension_enable_flow_) | |
| 541 return; | |
| 542 | |
| 543 extension_enable_flow_.reset( | |
| 544 new ExtensionEnableFlow(profile_, app_id, this)); | |
| 545 extension_enable_flow_->StartForNativeWindow(NULL); | |
| 546 return; | |
| 547 } | |
| 548 | |
| 549 chrome::OpenApplication(chrome::AppLaunchParams(GetProfileForNewWindows(), | |
| 550 extension, | |
| 551 event_flags)); | |
| 552 } | |
| 553 | |
| 554 void ChromeLauncherControllerPerBrowser::ActivateApp(const std::string& app_id, | |
| 555 int event_flags) { | |
| 556 if (app_id == extension_misc::kChromeAppId) { | |
| 557 BrowserShortcutClicked(event_flags); | |
| 558 return; | |
| 559 } | |
| 560 | |
| 561 // If there is an existing non-shortcut controller for this app, open it. | |
| 562 ash::LauncherID id = GetLauncherIDForAppID(app_id); | |
| 563 URLPattern refocus_pattern(URLPattern::SCHEME_ALL); | |
| 564 refocus_pattern.SetMatchAllURLs(true); | |
| 565 | |
| 566 if (id > 0) { | |
| 567 LauncherItemController* controller = id_to_item_controller_map_[id]; | |
| 568 if (controller->type() != LauncherItemController::TYPE_SHORTCUT) { | |
| 569 controller->Activate(); | |
| 570 return; | |
| 571 } | |
| 572 | |
| 573 AppShortcutLauncherItemController* app_controller = | |
| 574 static_cast<AppShortcutLauncherItemController*>(controller); | |
| 575 const GURL refocus_url = app_controller->refocus_url(); | |
| 576 | |
| 577 if (!refocus_url.is_empty()) | |
| 578 refocus_pattern.Parse(refocus_url.spec()); | |
| 579 } | |
| 580 | |
| 581 // Check if there are any open tabs for this app. | |
| 582 AppIDToWebContentsListMap::iterator app_i = | |
| 583 app_id_to_web_contents_list_.find(app_id); | |
| 584 if (app_i != app_id_to_web_contents_list_.end()) { | |
| 585 for (WebContentsList::iterator tab_i = app_i->second.begin(); | |
| 586 tab_i != app_i->second.end(); | |
| 587 ++tab_i) { | |
| 588 WebContents* tab = *tab_i; | |
| 589 const GURL tab_url = tab->GetURL(); | |
| 590 if (refocus_pattern.MatchesURL(tab_url)) { | |
| 591 Browser* browser = chrome::FindBrowserWithWebContents(tab); | |
| 592 TabStripModel* tab_strip = browser->tab_strip_model(); | |
| 593 int index = tab_strip->GetIndexOfWebContents(tab); | |
| 594 DCHECK_NE(TabStripModel::kNoTab, index); | |
| 595 tab_strip->ActivateTabAt(index, false); | |
| 596 browser->window()->Show(); | |
| 597 ash::wm::ActivateWindow(browser->window()->GetNativeWindow()); | |
| 598 return; | |
| 599 } | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 LaunchApp(app_id, event_flags); | |
| 604 } | |
| 605 | |
| 606 extensions::ExtensionPrefs::LaunchType | |
| 607 ChromeLauncherControllerPerBrowser::GetLaunchType(ash::LauncherID id) { | |
| 608 DCHECK(HasItemController(id)); | |
| 609 | |
| 610 const Extension* extension = GetExtensionForAppID( | |
| 611 id_to_item_controller_map_[id]->app_id()); | |
| 612 return profile_->GetExtensionService()->extension_prefs()->GetLaunchType( | |
| 613 extension, | |
| 614 extensions::ExtensionPrefs::LAUNCH_DEFAULT); | |
| 615 } | |
| 616 | |
| 617 std::string ChromeLauncherControllerPerBrowser::GetAppID( | |
| 618 content::WebContents* tab) { | |
| 619 return app_tab_helper_->GetAppID(tab); | |
| 620 } | |
| 621 | |
| 622 ash::LauncherID ChromeLauncherControllerPerBrowser::GetLauncherIDForAppID( | |
| 623 const std::string& app_id) { | |
| 624 for (IDToItemControllerMap::const_iterator i = | |
| 625 id_to_item_controller_map_.begin(); | |
| 626 i != id_to_item_controller_map_.end(); ++i) { | |
| 627 if (i->second->type() == LauncherItemController::TYPE_APP_PANEL) | |
| 628 continue; // Don't include panels | |
| 629 if (i->second->app_id() == app_id) | |
| 630 return i->first; | |
| 631 } | |
| 632 return 0; | |
| 633 } | |
| 634 | |
| 635 std::string ChromeLauncherControllerPerBrowser::GetAppIDForLauncherID( | |
| 636 ash::LauncherID id) { | |
| 637 DCHECK(HasItemController(id)); | |
| 638 return id_to_item_controller_map_[id]->app_id(); | |
| 639 } | |
| 640 | |
| 641 void ChromeLauncherControllerPerBrowser::SetAppImage( | |
| 642 const std::string& id, | |
| 643 const gfx::ImageSkia& image) { | |
| 644 // TODO: need to get this working for shortcuts. | |
| 645 | |
| 646 for (IDToItemControllerMap::const_iterator i = | |
| 647 id_to_item_controller_map_.begin(); | |
| 648 i != id_to_item_controller_map_.end(); ++i) { | |
| 649 if (i->second->app_id() != id) | |
| 650 continue; | |
| 651 | |
| 652 int index = model_->ItemIndexByID(i->first); | |
| 653 ash::LauncherItem item = model_->items()[index]; | |
| 654 item.image = image; | |
| 655 model_->Set(index, item); | |
| 656 // It's possible we're waiting on more than one item, so don't break. | |
| 657 } | |
| 658 } | |
| 659 | |
| 660 void ChromeLauncherControllerPerBrowser::OnAutoHideBehaviorChanged( | |
| 661 aura::RootWindow* root_window, | |
| 662 ash::ShelfAutoHideBehavior new_behavior) { | |
| 663 SetShelfAutoHideBehaviorPrefs(new_behavior, root_window); | |
| 664 } | |
| 665 | |
| 666 void ChromeLauncherControllerPerBrowser::SetLauncherItemImage( | |
| 667 ash::LauncherID launcher_id, | |
| 668 const gfx::ImageSkia& image) { | |
| 669 int index = model_->ItemIndexByID(launcher_id); | |
| 670 if (index == -1) | |
| 671 return; | |
| 672 ash::LauncherItem item = model_->items()[index]; | |
| 673 item.image = image; | |
| 674 model_->Set(index, item); | |
| 675 } | |
| 676 | |
| 677 bool ChromeLauncherControllerPerBrowser::IsAppPinned( | |
| 678 const std::string& app_id) { | |
| 679 // Check the LauncherModel since there is no controller for the browser item. | |
| 680 if (app_id == extension_misc::kChromeAppId) { | |
| 681 for (size_t index = 0; index < model_->items().size(); index++) { | |
| 682 if (model_->items()[index].type == ash::TYPE_BROWSER_SHORTCUT) | |
| 683 return true; | |
| 684 } | |
| 685 return false; | |
| 686 } | |
| 687 for (IDToItemControllerMap::const_iterator i = | |
| 688 id_to_item_controller_map_.begin(); | |
| 689 i != id_to_item_controller_map_.end(); ++i) { | |
| 690 if (IsPinned(i->first) && i->second->app_id() == app_id) | |
| 691 return true; | |
| 692 } | |
| 693 return false; | |
| 694 } | |
| 695 | |
| 696 void ChromeLauncherControllerPerBrowser::PinAppWithID( | |
| 697 const std::string& app_id) { | |
| 698 if (CanPin()) | |
| 699 DoPinAppWithID(app_id); | |
| 700 else | |
| 701 NOTREACHED(); | |
| 702 } | |
| 703 | |
| 704 void ChromeLauncherControllerPerBrowser::SetLaunchType( | |
| 705 ash::LauncherID id, | |
| 706 extensions::ExtensionPrefs::LaunchType launch_type) { | |
| 707 if (!HasItemController(id)) | |
| 708 return; | |
| 709 | |
| 710 return profile_->GetExtensionService()->extension_prefs()->SetLaunchType( | |
| 711 id_to_item_controller_map_[id]->app_id(), launch_type); | |
| 712 } | |
| 713 | |
| 714 void ChromeLauncherControllerPerBrowser::UnpinAppsWithID( | |
| 715 const std::string& app_id) { | |
| 716 if (CanPin()) | |
| 717 DoUnpinAppsWithID(app_id); | |
| 718 else | |
| 719 NOTREACHED(); | |
| 720 } | |
| 721 | |
| 722 bool ChromeLauncherControllerPerBrowser::IsLoggedInAsGuest() { | |
| 723 return ProfileManager::GetDefaultProfileOrOffTheRecord()->IsOffTheRecord(); | |
| 724 } | |
| 725 | |
| 726 void ChromeLauncherControllerPerBrowser::CreateNewWindow() { | |
| 727 chrome::NewEmptyWindow( | |
| 728 GetProfileForNewWindows(), chrome::HOST_DESKTOP_TYPE_ASH); | |
| 729 } | |
| 730 | |
| 731 void ChromeLauncherControllerPerBrowser::CreateNewIncognitoWindow() { | |
| 732 chrome::NewEmptyWindow(GetProfileForNewWindows()->GetOffTheRecordProfile(), | |
| 733 chrome::HOST_DESKTOP_TYPE_ASH); | |
| 734 } | |
| 735 | |
| 736 bool ChromeLauncherControllerPerBrowser::CanPin() const { | |
| 737 const PrefService::Preference* pref = | |
| 738 profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps); | |
| 739 return pref && pref->IsUserModifiable(); | |
| 740 } | |
| 741 | |
| 742 ash::ShelfAutoHideBehavior | |
| 743 ChromeLauncherControllerPerBrowser::GetShelfAutoHideBehavior( | |
| 744 aura::RootWindow* root_window) const { | |
| 745 // Don't show the shelf in the app mode. | |
| 746 if (chrome::IsRunningInAppMode()) | |
| 747 return ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN; | |
| 748 | |
| 749 // See comment in |kShelfAlignment| as to why we consider two prefs. | |
| 750 const std::string behavior_value( | |
| 751 GetPrefForRootWindow(profile_->GetPrefs(), | |
| 752 root_window, | |
| 753 prefs::kShelfAutoHideBehaviorLocal, | |
| 754 prefs::kShelfAutoHideBehavior)); | |
| 755 | |
| 756 // Note: To maintain sync compatibility with old images of chrome/chromeos | |
| 757 // the set of values that may be encountered includes the now-extinct | |
| 758 // "Default" as well as "Never" and "Always", "Default" should now | |
| 759 // be treated as "Never" (http://crbug.com/146773). | |
| 760 if (behavior_value == ash::kShelfAutoHideBehaviorAlways) | |
| 761 return ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; | |
| 762 return ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER; | |
| 763 } | |
| 764 | |
| 765 bool ChromeLauncherControllerPerBrowser::CanUserModifyShelfAutoHideBehavior( | |
| 766 aura::RootWindow* root_window) const { | |
| 767 return profile_->GetPrefs()-> | |
| 768 FindPreference(prefs::kShelfAutoHideBehaviorLocal)->IsUserModifiable(); | |
| 769 } | |
| 770 | |
| 771 void ChromeLauncherControllerPerBrowser::ToggleShelfAutoHideBehavior( | |
| 772 aura::RootWindow* root_window) { | |
| 773 ash::ShelfAutoHideBehavior behavior = GetShelfAutoHideBehavior(root_window) == | |
| 774 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS ? | |
| 775 ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER : | |
| 776 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; | |
| 777 SetShelfAutoHideBehaviorPrefs(behavior, root_window); | |
| 778 return; | |
| 779 } | |
| 780 | |
| 781 void ChromeLauncherControllerPerBrowser::RemoveTabFromRunningApp( | |
| 782 WebContents* tab, | |
| 783 const std::string& app_id) { | |
| 784 web_contents_to_app_id_.erase(tab); | |
| 785 AppIDToWebContentsListMap::iterator i_app_id = | |
| 786 app_id_to_web_contents_list_.find(app_id); | |
| 787 if (i_app_id != app_id_to_web_contents_list_.end()) { | |
| 788 WebContentsList* tab_list = &i_app_id->second; | |
| 789 tab_list->remove(tab); | |
| 790 if (tab_list->empty()) { | |
| 791 app_id_to_web_contents_list_.erase(i_app_id); | |
| 792 i_app_id = app_id_to_web_contents_list_.end(); | |
| 793 ash::LauncherID id = GetLauncherIDForAppID(app_id); | |
| 794 if (id > 0) | |
| 795 SetItemStatus(id, ash::STATUS_CLOSED); | |
| 796 } | |
| 797 } | |
| 798 } | |
| 799 | |
| 800 void ChromeLauncherControllerPerBrowser::UpdateAppState( | |
| 801 content::WebContents* contents, | |
| 802 AppState app_state) { | |
| 803 std::string app_id = GetAppID(contents); | |
| 804 | |
| 805 // Check the old |app_id| for a tab. If the contents has changed we need to | |
| 806 // remove it from the previous app. | |
| 807 if (web_contents_to_app_id_.find(contents) != web_contents_to_app_id_.end()) { | |
| 808 std::string last_app_id = web_contents_to_app_id_[contents]; | |
| 809 if (last_app_id != app_id) | |
| 810 RemoveTabFromRunningApp(contents, last_app_id); | |
| 811 } | |
| 812 | |
| 813 if (app_id.empty()) | |
| 814 return; | |
| 815 | |
| 816 web_contents_to_app_id_[contents] = app_id; | |
| 817 | |
| 818 if (app_state == APP_STATE_REMOVED) { | |
| 819 // The tab has gone away. | |
| 820 RemoveTabFromRunningApp(contents, app_id); | |
| 821 } else { | |
| 822 WebContentsList& tab_list(app_id_to_web_contents_list_[app_id]); | |
| 823 | |
| 824 if (app_state == APP_STATE_INACTIVE) { | |
| 825 WebContentsList::const_iterator i_tab = | |
| 826 std::find(tab_list.begin(), tab_list.end(), contents); | |
| 827 if (i_tab == tab_list.end()) | |
| 828 tab_list.push_back(contents); | |
| 829 if (i_tab != tab_list.begin()) { | |
| 830 // Going inactive, but wasn't the front tab, indicating that a new | |
| 831 // tab has already become active. | |
| 832 return; | |
| 833 } | |
| 834 } else { | |
| 835 tab_list.remove(contents); | |
| 836 tab_list.push_front(contents); | |
| 837 } | |
| 838 ash::LauncherID id = GetLauncherIDForAppID(app_id); | |
| 839 if (id > 0) { | |
| 840 // If the window is active, mark the app as active. | |
| 841 SetItemStatus(id, app_state == APP_STATE_WINDOW_ACTIVE ? | |
| 842 ash::STATUS_ACTIVE : ash::STATUS_RUNNING); | |
| 843 } | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 void ChromeLauncherControllerPerBrowser::SetRefocusURLPatternForTest( | |
| 848 ash::LauncherID id, | |
| 849 const GURL& url) { | |
| 850 DCHECK(HasItemController(id)); | |
| 851 LauncherItemController* controller = id_to_item_controller_map_[id]; | |
| 852 | |
| 853 int index = model_->ItemIndexByID(id); | |
| 854 if (index == -1) { | |
| 855 NOTREACHED() << "Invalid launcher id"; | |
| 856 return; | |
| 857 } | |
| 858 | |
| 859 ash::LauncherItemType type = model_->items()[index].type; | |
| 860 if (type == ash::TYPE_APP_SHORTCUT) { | |
| 861 AppShortcutLauncherItemController* app_controller = | |
| 862 static_cast<AppShortcutLauncherItemController*>(controller); | |
| 863 app_controller->set_refocus_url(url); | |
| 864 } else { | |
| 865 NOTREACHED() << "Invalid launcher type"; | |
| 866 } | |
| 867 } | |
| 868 | |
| 869 const Extension* ChromeLauncherControllerPerBrowser::GetExtensionForAppID( | |
| 870 const std::string& app_id) const { | |
| 871 return profile_->GetExtensionService()->GetInstalledExtension(app_id); | |
| 872 } | |
| 873 | |
| 874 void ChromeLauncherControllerPerBrowser::ActivateWindowOrMinimizeIfActive( | |
| 875 ui::BaseWindow* window, | |
| 876 bool allow_minimize) { | |
| 877 window->Show(); | |
| 878 window->Activate(); | |
| 879 } | |
| 880 | |
| 881 void ChromeLauncherControllerPerBrowser::BrowserShortcutClicked( | |
| 882 int event_flags) { | |
| 883 #if defined(OS_CHROMEOS) | |
| 884 chromeos::default_pinned_apps_field_trial::RecordShelfClick( | |
| 885 chromeos::default_pinned_apps_field_trial::CHROME); | |
| 886 #endif | |
| 887 if (event_flags & ui::EF_CONTROL_DOWN) { | |
| 888 CreateNewWindow(); | |
| 889 return; | |
| 890 } | |
| 891 | |
| 892 Browser* last_browser = chrome::FindTabbedBrowser( | |
| 893 GetProfileForNewWindows(), true, chrome::HOST_DESKTOP_TYPE_ASH); | |
| 894 | |
| 895 if (!last_browser) { | |
| 896 CreateNewWindow(); | |
| 897 return; | |
| 898 } | |
| 899 | |
| 900 aura::Window* window = last_browser->window()->GetNativeWindow(); | |
| 901 window->Show(); | |
| 902 ash::wm::ActivateWindow(window); | |
| 903 } | |
| 904 | |
| 905 void ChromeLauncherControllerPerBrowser::ItemSelected( | |
| 906 const ash::LauncherItem& item, | |
| 907 const ui::Event& event) { | |
| 908 if (item.type == ash::TYPE_BROWSER_SHORTCUT) { | |
| 909 BrowserShortcutClicked(event.flags()); | |
| 910 return; | |
| 911 } | |
| 912 | |
| 913 DCHECK(HasItemController(item.id)); | |
| 914 LauncherItemController* item_controller = id_to_item_controller_map_[item.id]; | |
| 915 #if defined(OS_CHROMEOS) | |
| 916 if (!item_controller->app_id().empty()) { | |
| 917 chromeos::default_pinned_apps_field_trial::RecordShelfAppClick( | |
| 918 item_controller->app_id()); | |
| 919 } | |
| 920 #endif | |
| 921 item_controller->Clicked(event); | |
| 922 } | |
| 923 | |
| 924 string16 ChromeLauncherControllerPerBrowser::GetTitle( | |
| 925 const ash::LauncherItem& item) { | |
| 926 if (item.type == ash::TYPE_BROWSER_SHORTCUT) | |
| 927 return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); | |
| 928 | |
| 929 DCHECK(HasItemController(item.id)); | |
| 930 return id_to_item_controller_map_[item.id]->GetTitle(); | |
| 931 } | |
| 932 | |
| 933 ui::MenuModel* ChromeLauncherControllerPerBrowser::CreateContextMenu( | |
| 934 const ash::LauncherItem& item, | |
| 935 aura::RootWindow* root_window) { | |
| 936 return new LauncherContextMenu(this, &item, root_window); | |
| 937 } | |
| 938 | |
| 939 ash::LauncherMenuModel* | |
| 940 ChromeLauncherControllerPerBrowser::CreateApplicationMenu( | |
| 941 const ash::LauncherItem& item, | |
| 942 int event_flags) { | |
| 943 // Not used by this launcher type. | |
| 944 return NULL; | |
| 945 } | |
| 946 | |
| 947 ash::LauncherID ChromeLauncherControllerPerBrowser::GetIDByWindow( | |
| 948 aura::Window* window) { | |
| 949 for (IDToItemControllerMap::const_iterator i = | |
| 950 id_to_item_controller_map_.begin(); | |
| 951 i != id_to_item_controller_map_.end(); ++i) { | |
| 952 if (i->second->IsCurrentlyShownInWindow(window)) | |
| 953 return i->first; | |
| 954 } | |
| 955 return 0; | |
| 956 } | |
| 957 | |
| 958 bool ChromeLauncherControllerPerBrowser::IsDraggable( | |
| 959 const ash::LauncherItem& item) { | |
| 960 return item.type == ash::TYPE_APP_SHORTCUT ? CanPin() : true; | |
| 961 } | |
| 962 | |
| 963 bool ChromeLauncherControllerPerBrowser::ShouldShowTooltip( | |
| 964 const ash::LauncherItem& item) { | |
| 965 if (item.type == ash::TYPE_APP_PANEL && | |
| 966 id_to_item_controller_map_[item.id]->IsVisible()) | |
| 967 return false; | |
| 968 return true; | |
| 969 } | |
| 970 | |
| 971 void ChromeLauncherControllerPerBrowser::OnLauncherCreated( | |
| 972 ash::Launcher* launcher) { | |
| 973 launchers_.insert(launcher); | |
| 974 launcher->shelf_widget()->shelf_layout_manager()->AddObserver(this); | |
| 975 } | |
| 976 | |
| 977 void ChromeLauncherControllerPerBrowser::OnLauncherDestroyed( | |
| 978 ash::Launcher* launcher) { | |
| 979 launchers_.erase(launcher); | |
| 980 // RemoveObserver is not called here, since by the time this method is called | |
| 981 // Launcher is already in its destructor. | |
| 982 } | |
| 983 | |
| 984 void ChromeLauncherControllerPerBrowser::LauncherItemAdded(int index) { | |
| 985 } | |
| 986 | |
| 987 void ChromeLauncherControllerPerBrowser::LauncherItemRemoved( | |
| 988 int index, | |
| 989 ash::LauncherID id) { | |
| 990 } | |
| 991 | |
| 992 void ChromeLauncherControllerPerBrowser::LauncherItemMoved( | |
| 993 int start_index, | |
| 994 int target_index) { | |
| 995 ash::LauncherID id = model_->items()[target_index].id; | |
| 996 if (HasItemController(id) && IsPinned(id)) | |
| 997 PersistPinnedState(); | |
| 998 else if (!HasItemController(id) && | |
| 999 model_->items()[target_index].type == ash::TYPE_BROWSER_SHORTCUT) | |
| 1000 PersistPinnedState(); | |
| 1001 } | |
| 1002 | |
| 1003 void ChromeLauncherControllerPerBrowser::LauncherItemChanged( | |
| 1004 int index, | |
| 1005 const ash::LauncherItem& old_item) { | |
| 1006 ash::LauncherID id = model_->items()[index].id; | |
| 1007 id_to_item_controller_map_[id]->LauncherItemChanged(index, old_item); | |
| 1008 } | |
| 1009 | |
| 1010 void ChromeLauncherControllerPerBrowser::LauncherStatusChanged() { | |
| 1011 } | |
| 1012 | |
| 1013 void ChromeLauncherControllerPerBrowser::Observe( | |
| 1014 int type, | |
| 1015 const content::NotificationSource& source, | |
| 1016 const content::NotificationDetails& details) { | |
| 1017 switch (type) { | |
| 1018 case chrome::NOTIFICATION_EXTENSION_LOADED: { | |
| 1019 const Extension* extension = | |
| 1020 content::Details<const Extension>(details).ptr(); | |
| 1021 if (IsAppPinned(extension->id())) { | |
| 1022 // Clear and re-fetch to ensure icon is up-to-date. | |
| 1023 app_icon_loader_->ClearImage(extension->id()); | |
| 1024 app_icon_loader_->FetchImage(extension->id()); | |
| 1025 } | |
| 1026 | |
| 1027 UpdateAppLaunchersFromPref(); | |
| 1028 break; | |
| 1029 } | |
| 1030 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
| 1031 const content::Details<extensions::UnloadedExtensionInfo>& unload_info( | |
| 1032 details); | |
| 1033 const Extension* extension = unload_info->extension; | |
| 1034 if (IsAppPinned(extension->id())) { | |
| 1035 if (unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) { | |
| 1036 DoUnpinAppsWithID(extension->id()); | |
| 1037 app_icon_loader_->ClearImage(extension->id()); | |
| 1038 } else { | |
| 1039 app_icon_loader_->UpdateImage(extension->id()); | |
| 1040 } | |
| 1041 } | |
| 1042 break; | |
| 1043 } | |
| 1044 default: | |
| 1045 NOTREACHED() << "Unexpected notification type=" << type; | |
| 1046 } | |
| 1047 } | |
| 1048 | |
| 1049 void ChromeLauncherControllerPerBrowser::OnShelfAlignmentChanged( | |
| 1050 aura::RootWindow* root_window) { | |
| 1051 const char* pref_value = NULL; | |
| 1052 switch (ash::Shell::GetInstance()->GetShelfAlignment(root_window)) { | |
| 1053 case ash::SHELF_ALIGNMENT_BOTTOM: | |
| 1054 pref_value = ash::kShelfAlignmentBottom; | |
| 1055 break; | |
| 1056 case ash::SHELF_ALIGNMENT_LEFT: | |
| 1057 pref_value = ash::kShelfAlignmentLeft; | |
| 1058 break; | |
| 1059 case ash::SHELF_ALIGNMENT_RIGHT: | |
| 1060 pref_value = ash::kShelfAlignmentRight; | |
| 1061 break; | |
| 1062 case ash::SHELF_ALIGNMENT_TOP: | |
| 1063 pref_value = ash::kShelfAlignmentTop; | |
| 1064 break; | |
| 1065 } | |
| 1066 | |
| 1067 UpdatePerDisplayPref( | |
| 1068 profile_->GetPrefs(), root_window, prefs::kShelfAlignment, pref_value); | |
| 1069 | |
| 1070 if (root_window == ash::Shell::GetPrimaryRootWindow()) { | |
| 1071 // See comment in |kShelfAlignment| about why we have two prefs here. | |
| 1072 profile_->GetPrefs()->SetString(prefs::kShelfAlignmentLocal, pref_value); | |
| 1073 profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value); | |
| 1074 } | |
| 1075 } | |
| 1076 | |
| 1077 void ChromeLauncherControllerPerBrowser::OnDisplayConfigurationChanging() { | |
| 1078 } | |
| 1079 | |
| 1080 void ChromeLauncherControllerPerBrowser::OnDisplayConfigurationChanged() { | |
| 1081 SetShelfBehaviorsFromPrefs(); | |
| 1082 } | |
| 1083 | |
| 1084 void ChromeLauncherControllerPerBrowser::OnIsSyncingChanged() { | |
| 1085 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); | |
| 1086 MaybePropagatePrefToLocal(prefs, | |
| 1087 prefs::kShelfAlignmentLocal, | |
| 1088 prefs::kShelfAlignment); | |
| 1089 MaybePropagatePrefToLocal(prefs, | |
| 1090 prefs::kShelfAutoHideBehaviorLocal, | |
| 1091 prefs::kShelfAutoHideBehavior); | |
| 1092 } | |
| 1093 | |
| 1094 void ChromeLauncherControllerPerBrowser::OnAppSyncUIStatusChanged() { | |
| 1095 if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING) | |
| 1096 model_->SetStatus(ash::LauncherModel::STATUS_LOADING); | |
| 1097 else | |
| 1098 model_->SetStatus(ash::LauncherModel::STATUS_NORMAL); | |
| 1099 } | |
| 1100 | |
| 1101 void ChromeLauncherControllerPerBrowser::ExtensionEnableFlowFinished() { | |
| 1102 LaunchApp(extension_enable_flow_->extension_id(), ui::EF_NONE); | |
| 1103 extension_enable_flow_.reset(); | |
| 1104 } | |
| 1105 | |
| 1106 void ChromeLauncherControllerPerBrowser::ExtensionEnableFlowAborted( | |
| 1107 bool user_initiated) { | |
| 1108 extension_enable_flow_.reset(); | |
| 1109 } | |
| 1110 | |
| 1111 void ChromeLauncherControllerPerBrowser::PersistPinnedState() { | |
| 1112 if (ignore_persist_pinned_state_change_) | |
| 1113 return; | |
| 1114 // It is a coding error to call PersistPinnedState() if the pinned apps are | |
| 1115 // not user-editable. The code should check earlier and not perform any | |
| 1116 // modification actions that trigger persisting the state. | |
| 1117 if (!CanPin()) { | |
| 1118 NOTREACHED() << "Can't pin but pinned state being updated"; | |
| 1119 return; | |
| 1120 } | |
| 1121 | |
| 1122 // Mutating kPinnedLauncherApps is going to notify us and trigger us to | |
| 1123 // process the change. We don't want that to happen so remove ourselves as a | |
| 1124 // listener. | |
| 1125 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps); | |
| 1126 { | |
| 1127 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps); | |
| 1128 updater->Clear(); | |
| 1129 for (size_t i = 0; i < model_->items().size(); ++i) { | |
| 1130 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) { | |
| 1131 ash::LauncherID id = model_->items()[i].id; | |
| 1132 if (HasItemController(id) && IsPinned(id)) { | |
| 1133 base::DictionaryValue* app_value = ash::CreateAppDict( | |
| 1134 id_to_item_controller_map_[id]->app_id()); | |
| 1135 if (app_value) | |
| 1136 updater->Append(app_value); | |
| 1137 } | |
| 1138 } else if (model_->items()[i].type == ash::TYPE_BROWSER_SHORTCUT) { | |
| 1139 SetChromeIconIndexToPref(i); | |
| 1140 } | |
| 1141 } | |
| 1142 } | |
| 1143 pref_change_registrar_.Add( | |
| 1144 prefs::kPinnedLauncherApps, | |
| 1145 base::Bind(&ChromeLauncherControllerPerBrowser:: | |
| 1146 UpdateAppLaunchersFromPref, | |
| 1147 base::Unretained(this))); | |
| 1148 } | |
| 1149 | |
| 1150 ash::LauncherModel* ChromeLauncherControllerPerBrowser::model() { | |
| 1151 return model_; | |
| 1152 } | |
| 1153 | |
| 1154 Profile* ChromeLauncherControllerPerBrowser::profile() { | |
| 1155 return profile_; | |
| 1156 } | |
| 1157 | |
| 1158 Profile* ChromeLauncherControllerPerBrowser::GetProfileForNewWindows() { | |
| 1159 return ProfileManager::GetDefaultProfileOrOffTheRecord(); | |
| 1160 } | |
| 1161 | |
| 1162 void ChromeLauncherControllerPerBrowser::LauncherItemClosed( | |
| 1163 ash::LauncherID id) { | |
| 1164 IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); | |
| 1165 DCHECK(iter != id_to_item_controller_map_.end()); | |
| 1166 app_icon_loader_->ClearImage(iter->second->app_id()); | |
| 1167 iter->second->OnRemoved(); | |
| 1168 id_to_item_controller_map_.erase(iter); | |
| 1169 model_->RemoveItemAt(model_->ItemIndexByID(id)); | |
| 1170 } | |
| 1171 | |
| 1172 void ChromeLauncherControllerPerBrowser::DoPinAppWithID( | |
| 1173 const std::string& app_id) { | |
| 1174 // If there is an item, do nothing and return. | |
| 1175 if (IsAppPinned(app_id)) | |
| 1176 return; | |
| 1177 | |
| 1178 ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id); | |
| 1179 if (launcher_id) { | |
| 1180 // App item exists, pin it | |
| 1181 Pin(launcher_id); | |
| 1182 } else { | |
| 1183 // Otherwise, create a shortcut item for it. | |
| 1184 CreateAppShortcutLauncherItem(app_id, model_->item_count()); | |
| 1185 if (CanPin()) | |
| 1186 PersistPinnedState(); | |
| 1187 } | |
| 1188 } | |
| 1189 | |
| 1190 void ChromeLauncherControllerPerBrowser::DoUnpinAppsWithID( | |
| 1191 const std::string& app_id) { | |
| 1192 for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin(); | |
| 1193 i != id_to_item_controller_map_.end(); ) { | |
| 1194 IDToItemControllerMap::iterator current(i); | |
| 1195 ++i; | |
| 1196 if (current->second->app_id() == app_id && IsPinned(current->first)) | |
| 1197 Unpin(current->first); | |
| 1198 } | |
| 1199 } | |
| 1200 | |
| 1201 void ChromeLauncherControllerPerBrowser::UpdateAppLaunchersFromPref() { | |
| 1202 // Construct a vector representation of to-be-pinned apps from the pref. | |
| 1203 std::vector<std::string> pinned_apps; | |
| 1204 int chrome_icon_index = GetChromeIconIndexFromPref(); | |
| 1205 const base::ListValue* pinned_apps_pref = | |
| 1206 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps); | |
| 1207 for (base::ListValue::const_iterator it(pinned_apps_pref->begin()); | |
| 1208 it != pinned_apps_pref->end(); ++it) { | |
| 1209 // To preserve the Chrome icon position, we insert a dummy slot for it - if | |
| 1210 // the model has a Chrome item. While initializing we can come here with no | |
| 1211 // item in which case the count would be 1 or below. | |
| 1212 if (it - pinned_apps_pref->begin() == chrome_icon_index && | |
| 1213 model_->item_count() > 1) { | |
| 1214 pinned_apps.push_back(extension_misc::kChromeAppId); | |
| 1215 } | |
| 1216 DictionaryValue* app = NULL; | |
| 1217 std::string app_id; | |
| 1218 if ((*it)->GetAsDictionary(&app) && | |
| 1219 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) && | |
| 1220 std::find(pinned_apps.begin(), pinned_apps.end(), app_id) == | |
| 1221 pinned_apps.end() && | |
| 1222 app_tab_helper_->IsValidID(app_id)) { | |
| 1223 pinned_apps.push_back(app_id); | |
| 1224 } | |
| 1225 } | |
| 1226 | |
| 1227 // Walk the model and |pinned_apps| from the pref lockstep, adding and | |
| 1228 // removing items as necessary. NB: This code uses plain old indexing instead | |
| 1229 // of iterators because of model mutations as part of the loop. | |
| 1230 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin()); | |
| 1231 int index = 0; | |
| 1232 int max_index = model_->item_count(); | |
| 1233 if (ash::switches::UseAlternateShelfLayout()) { | |
| 1234 ++index; | |
| 1235 ++max_index; | |
| 1236 } | |
| 1237 for (; index < model_->item_count() && pref_app_id != pinned_apps.end(); | |
| 1238 ++index) { | |
| 1239 // If the next app launcher according to the pref is present in the model, | |
| 1240 // delete all app launcher entries in between. | |
| 1241 if (*pref_app_id == extension_misc::kChromeAppId || | |
| 1242 IsAppPinned(*pref_app_id)) { | |
| 1243 for (; index < model_->item_count(); ++index) { | |
| 1244 const ash::LauncherItem& item(model_->items()[index]); | |
| 1245 if (item.type != ash::TYPE_APP_SHORTCUT && | |
| 1246 item.type != ash::TYPE_BROWSER_SHORTCUT) | |
| 1247 continue; | |
| 1248 | |
| 1249 IDToItemControllerMap::const_iterator entry = | |
| 1250 id_to_item_controller_map_.find(item.id); | |
| 1251 if ((extension_misc::kChromeAppId == *pref_app_id && | |
| 1252 item.type == ash::TYPE_BROWSER_SHORTCUT) || | |
| 1253 (entry != id_to_item_controller_map_.end() && | |
| 1254 entry->second->app_id() == *pref_app_id)) { | |
| 1255 ++pref_app_id; | |
| 1256 break; | |
| 1257 } else { | |
| 1258 if (item.type == ash::TYPE_BROWSER_SHORTCUT) { | |
| 1259 // We cannot delete the browser shortcut. As such we move it up by | |
| 1260 // one. To avoid any side effects from our pinned state observer, we | |
| 1261 // do not call the model directly. | |
| 1262 MoveItemWithoutPinnedStateChangeNotification(index, index + 1); | |
| 1263 } else { | |
| 1264 LauncherItemClosed(item.id); | |
| 1265 --max_index; | |
| 1266 } | |
| 1267 --index; | |
| 1268 } | |
| 1269 } | |
| 1270 // If the item wasn't found, that means id_to_item_controller_map_ | |
| 1271 // is out of sync. | |
| 1272 DCHECK(index < model_->item_count()); | |
| 1273 } else { | |
| 1274 // This app wasn't pinned before, insert a new entry. | |
| 1275 ash::LauncherID id = CreateAppShortcutLauncherItem(*pref_app_id, index); | |
| 1276 index = model_->ItemIndexByID(id); | |
| 1277 ++pref_app_id; | |
| 1278 } | |
| 1279 } | |
| 1280 | |
| 1281 // Remove any trailing existing items. | |
| 1282 while (index < model_->item_count()) { | |
| 1283 const ash::LauncherItem& item(model_->items()[index]); | |
| 1284 if (item.type == ash::TYPE_APP_SHORTCUT) | |
| 1285 LauncherItemClosed(item.id); | |
| 1286 else | |
| 1287 ++index; | |
| 1288 } | |
| 1289 | |
| 1290 // Append unprocessed items from the pref to the end of the model. | |
| 1291 for (; pref_app_id != pinned_apps.end(); ++pref_app_id) { | |
| 1292 // Ignore the chrome icon. | |
| 1293 if (*pref_app_id != extension_misc::kChromeAppId) | |
| 1294 DoPinAppWithID(*pref_app_id); | |
| 1295 } | |
| 1296 } | |
| 1297 | |
| 1298 void ChromeLauncherControllerPerBrowser::SetShelfAutoHideBehaviorPrefs( | |
| 1299 ash::ShelfAutoHideBehavior behavior, | |
| 1300 aura::RootWindow* root_window) { | |
| 1301 const char* value = NULL; | |
| 1302 switch (behavior) { | |
| 1303 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS: | |
| 1304 value = ash::kShelfAutoHideBehaviorAlways; | |
| 1305 break; | |
| 1306 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER: | |
| 1307 value = ash::kShelfAutoHideBehaviorNever; | |
| 1308 break; | |
| 1309 case ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN: | |
| 1310 // This one should not be a valid preference option for now. We only want | |
| 1311 // to completely hide it when we run app mode. | |
| 1312 NOTREACHED(); | |
| 1313 return; | |
| 1314 } | |
| 1315 | |
| 1316 UpdatePerDisplayPref( | |
| 1317 profile_->GetPrefs(), root_window, prefs::kShelfAutoHideBehavior, value); | |
| 1318 | |
| 1319 if (root_window == ash::Shell::GetPrimaryRootWindow()) { | |
| 1320 // See comment in |kShelfAlignment| about why we have two prefs here. | |
| 1321 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehaviorLocal, value); | |
| 1322 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value); | |
| 1323 } | |
| 1324 } | |
| 1325 | |
| 1326 void ChromeLauncherControllerPerBrowser::SetShelfAutoHideBehaviorFromPrefs() { | |
| 1327 ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows(); | |
| 1328 | |
| 1329 for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin(); | |
| 1330 iter != root_windows.end(); ++iter) { | |
| 1331 ash::Shell::GetInstance()->SetShelfAutoHideBehavior( | |
| 1332 GetShelfAutoHideBehavior(*iter), *iter); | |
| 1333 } | |
| 1334 } | |
| 1335 | |
| 1336 void ChromeLauncherControllerPerBrowser::SetShelfAlignmentFromPrefs() { | |
| 1337 if (!ash::ShelfWidget::ShelfAlignmentAllowed()) | |
| 1338 return; | |
| 1339 | |
| 1340 ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows(); | |
| 1341 | |
| 1342 for (ash::Shell::RootWindowList::const_iterator iter = root_windows.begin(); | |
| 1343 iter != root_windows.end(); ++iter) { | |
| 1344 // See comment in |kShelfAlignment| as to why we consider two prefs. | |
| 1345 const std::string alignment_value( | |
| 1346 GetPrefForRootWindow(profile_->GetPrefs(), | |
| 1347 *iter, | |
| 1348 prefs::kShelfAlignmentLocal, | |
| 1349 prefs::kShelfAlignment)); | |
| 1350 ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM; | |
| 1351 if (alignment_value == ash::kShelfAlignmentLeft) | |
| 1352 alignment = ash::SHELF_ALIGNMENT_LEFT; | |
| 1353 else if (alignment_value == ash::kShelfAlignmentRight) | |
| 1354 alignment = ash::SHELF_ALIGNMENT_RIGHT; | |
| 1355 else if (alignment_value == ash::kShelfAlignmentTop) | |
| 1356 alignment = ash::SHELF_ALIGNMENT_TOP; | |
| 1357 ash::Shell::GetInstance()->SetShelfAlignment(alignment, *iter); | |
| 1358 } | |
| 1359 } | |
| 1360 | |
| 1361 void ChromeLauncherControllerPerBrowser::SetShelfBehaviorsFromPrefs() { | |
| 1362 SetShelfAutoHideBehaviorFromPrefs(); | |
| 1363 SetShelfAlignmentFromPrefs(); | |
| 1364 } | |
| 1365 | |
| 1366 WebContents* ChromeLauncherControllerPerBrowser::GetLastActiveWebContents( | |
| 1367 const std::string& app_id) { | |
| 1368 AppIDToWebContentsListMap::const_iterator i = | |
| 1369 app_id_to_web_contents_list_.find(app_id); | |
| 1370 if (i == app_id_to_web_contents_list_.end()) | |
| 1371 return NULL; | |
| 1372 DCHECK_GT(i->second.size(), 0u); | |
| 1373 return *i->second.begin(); | |
| 1374 } | |
| 1375 | |
| 1376 ash::LauncherID ChromeLauncherControllerPerBrowser::InsertAppLauncherItem( | |
| 1377 LauncherItemController* controller, | |
| 1378 const std::string& app_id, | |
| 1379 ash::LauncherItemStatus status, | |
| 1380 int index) { | |
| 1381 ash::LauncherID id = model_->next_id(); | |
| 1382 DCHECK(!HasItemController(id)); | |
| 1383 DCHECK(controller); | |
| 1384 id_to_item_controller_map_[id] = controller; | |
| 1385 controller->set_launcher_id(id); | |
| 1386 | |
| 1387 ash::LauncherItem item; | |
| 1388 item.type = controller->GetLauncherItemType(); | |
| 1389 item.is_incognito = false; | |
| 1390 item.image = extensions::IconsInfo::GetDefaultAppIcon(); | |
| 1391 | |
| 1392 WebContents* active_tab = GetLastActiveWebContents(app_id); | |
| 1393 if (active_tab) { | |
| 1394 Browser* browser = chrome::FindBrowserWithWebContents(active_tab); | |
| 1395 DCHECK(browser); | |
| 1396 if (browser->window()->IsActive()) | |
| 1397 status = ash::STATUS_ACTIVE; | |
| 1398 else | |
| 1399 status = ash::STATUS_RUNNING; | |
| 1400 } | |
| 1401 item.status = status; | |
| 1402 | |
| 1403 model_->AddAt(index, item); | |
| 1404 | |
| 1405 app_icon_loader_->FetchImage(app_id); | |
| 1406 | |
| 1407 return id; | |
| 1408 } | |
| 1409 | |
| 1410 bool ChromeLauncherControllerPerBrowser::HasItemController( | |
| 1411 ash::LauncherID id) const { | |
| 1412 return id_to_item_controller_map_.find(id) != | |
| 1413 id_to_item_controller_map_.end(); | |
| 1414 } | |
| 1415 | |
| 1416 ash::LauncherID | |
| 1417 ChromeLauncherControllerPerBrowser::CreateBrowserShortcutLauncherItem() { | |
| 1418 ash::LauncherItem browser_shortcut; | |
| 1419 browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT; | |
| 1420 browser_shortcut.is_incognito = false; | |
| 1421 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 1422 browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32); | |
| 1423 ash::LauncherID id = model_->next_id(); | |
| 1424 size_t index = GetChromeIconIndexFromPref(); | |
| 1425 model_->AddAt(index, browser_shortcut); | |
| 1426 return id; | |
| 1427 } | |
| 1428 | |
| 1429 void ChromeLauncherControllerPerBrowser::SetChromeIconIndexToPref(int index) { | |
| 1430 profile_->GetPrefs()->SetInteger(prefs::kShelfChromeIconIndex, index); | |
| 1431 } | |
| 1432 | |
| 1433 int ChromeLauncherControllerPerBrowser::GetChromeIconIndexFromPref() const { | |
| 1434 size_t index = profile_->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex); | |
| 1435 const base::ListValue* pinned_apps_pref = | |
| 1436 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps); | |
| 1437 if (ash::switches::UseAlternateShelfLayout()) | |
| 1438 return std::max(static_cast<size_t>(1), | |
| 1439 std::min(pinned_apps_pref->GetSize(), index)); | |
| 1440 return std::max(static_cast<size_t>(0), | |
| 1441 std::min(pinned_apps_pref->GetSize(), index)); | |
| 1442 } | |
| 1443 | |
| 1444 ash::LauncherID | |
| 1445 ChromeLauncherControllerPerBrowser::CreateAppShortcutLauncherItem( | |
| 1446 const std::string& app_id, | |
| 1447 int index) { | |
| 1448 AppShortcutLauncherItemController* controller = | |
| 1449 new AppShortcutLauncherItemController(app_id, this); | |
| 1450 ash::LauncherID launcher_id = InsertAppLauncherItem( | |
| 1451 controller, app_id, ash::STATUS_CLOSED, index); | |
| 1452 return launcher_id; | |
| 1453 } | |
| 1454 | |
| 1455 void ChromeLauncherControllerPerBrowser::SetAppTabHelperForTest( | |
| 1456 AppTabHelper* helper) { | |
| 1457 app_tab_helper_.reset(helper); | |
| 1458 } | |
| 1459 | |
| 1460 void ChromeLauncherControllerPerBrowser::SetAppIconLoaderForTest( | |
| 1461 extensions::AppIconLoader* loader) { | |
| 1462 app_icon_loader_.reset(loader); | |
| 1463 } | |
| 1464 | |
| 1465 const std::string& | |
| 1466 ChromeLauncherControllerPerBrowser::GetAppIdFromLauncherIdForTest( | |
| 1467 ash::LauncherID id) { | |
| 1468 return id_to_item_controller_map_[id]->app_id(); | |
| 1469 } | |
| 1470 | |
| 1471 void ChromeLauncherControllerPerBrowser:: | |
| 1472 MoveItemWithoutPinnedStateChangeNotification(int source_index, | |
| 1473 int target_index) { | |
| 1474 base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true); | |
| 1475 model_->Move(source_index, target_index); | |
| 1476 } | |
| OLD | NEW |