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