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