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

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

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

Powered by Google App Engine
This is Rietveld 408576698