Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc

Issue 22887015: Remove PerBrowser launcher (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix indentation Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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"
Mr4D (OOO till 08-26) 2013/08/19 16:13:56 Not quite sure what I am looking at here therefore
simonhong_ 2013/08/19 21:00:57 Yes, I did below steps. 1) git rm chrome_launcher_
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698