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

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

Issue 10855094: launcher: Remove old files and update DEPS whitelist. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 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 | Annotate | Revision Log
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/views/ash/launcher/chrome_launcher_controller.h"
6
7 #include <set>
8 #include <vector>
9
10 #include "ash/launcher/launcher_model.h"
11 #include "ash/launcher/launcher_types.h"
12 #include "ash/shell.h"
13 #include "ash/wm/window_util.h"
14 #include "base/command_line.h"
15 #include "base/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/defaults.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/prefs/incognito_mode_prefs.h"
20 #include "chrome/browser/prefs/pref_service.h"
21 #include "chrome/browser/prefs/scoped_user_pref_update.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/profiles/profile_manager.h"
24 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
25 #include "chrome/browser/ui/ash/extension_utils.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/browser/ui/browser_commands.h"
28 #include "chrome/browser/ui/browser_finder.h"
29 #include "chrome/browser/ui/browser_window.h"
30 #include "chrome/browser/ui/extensions/shell_window.h"
31 #include "chrome/browser/ui/tab_contents/tab_contents.h"
32 #include "chrome/browser/ui/tabs/tab_strip_model.h"
33 #include "chrome/browser/ui/views/ash/launcher/browser_launcher_item_controller. h"
34 #include "chrome/browser/ui/views/ash/launcher/launcher_app_icon_loader.h"
35 #include "chrome/browser/ui/views/ash/launcher/launcher_app_tab_helper.h"
36 #include "chrome/browser/ui/views/ash/launcher/launcher_context_menu.h"
37 #include "chrome/browser/web_applications/web_app.h"
38 #include "chrome/common/chrome_notification_types.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/extensions/extension.h"
41 #include "chrome/common/extensions/extension_resource.h"
42 #include "chrome/common/pref_names.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/web_contents.h"
45 #include "grit/theme_resources.h"
46 #include "ui/aura/client/activation_client.h"
47 #include "ui/aura/window.h"
48 #include "ui/views/widget/widget.h"
49
50 using extensions::Extension;
51
52 // ChromeLauncherController::Item ----------------------------------------------
53
54 ChromeLauncherController::Item::Item()
55 : item_type(TYPE_TABBED_BROWSER),
56 controller(NULL) {
57 }
58
59 ChromeLauncherController::Item::~Item() {
60 }
61
62 // ChromeLauncherController ----------------------------------------------------
63
64 // static
65 ChromeLauncherController* ChromeLauncherController::instance_ = NULL;
66
67 ChromeLauncherController::ChromeLauncherController(Profile* profile,
68 ash::LauncherModel* model)
69 : model_(model),
70 profile_(profile),
71 activation_client_(NULL) {
72 if (!profile_) {
73 // Use the original profile as on chromeos we may get a temporary off the
74 // record profile.
75 profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile();
76 }
77 instance_ = this;
78 model_->AddObserver(this);
79 ShellWindowRegistry::Get(profile_)->AddObserver(this);
80 app_tab_helper_.reset(new LauncherAppTabHelper(profile_));
81 app_icon_loader_.reset(new LauncherAppIconLoader(profile_, this));
82
83 notification_registrar_.Add(this,
84 chrome::NOTIFICATION_EXTENSION_LOADED,
85 content::Source<Profile>(profile_));
86 notification_registrar_.Add(this,
87 chrome::NOTIFICATION_EXTENSION_UNLOADED,
88 content::Source<Profile>(profile_));
89 pref_change_registrar_.Init(profile_->GetPrefs());
90 pref_change_registrar_.Add(prefs::kPinnedLauncherApps, this);
91 pref_change_registrar_.Add(prefs::kShelfAlignment, this);
92 pref_change_registrar_.Add(prefs::kShelfAutoHideBehavior, this);
93 }
94
95 ChromeLauncherController::~ChromeLauncherController() {
96 ShellWindowRegistry::Get(profile_)->RemoveObserver(this);
97 model_->RemoveObserver(this);
98 for (IDToItemMap::iterator i = id_to_item_map_.begin();
99 i != id_to_item_map_.end(); ++i) {
100 model_->RemoveItemAt(model_->ItemIndexByID(i->first));
101 }
102 if (instance_ == this)
103 instance_ = NULL;
104 if (activation_client_)
105 activation_client_->RemoveObserver(this);
106
107 for (WindowToIDMap::iterator i = window_to_id_map_.begin();
108 i != window_to_id_map_.end(); ++i) {
109 i->first->RemoveObserver(this);
110 }
111
112 if (ash::Shell::HasInstance())
113 ash::Shell::GetInstance()->RemoveShellObserver(this);
114 }
115
116 void ChromeLauncherController::Init() {
117 // TODO(xiyuan): Remove migration code and kUseDefaultPinnedApp after M20.
118 // Migration cases:
119 // - Users that unpin all apps:
120 // - have default pinned apps
121 // - kUseDefaultPinnedApps set to false
122 // Migrate them by setting an empty list for kPinnedLauncherApps.
123 //
124 // - Users that have customized pinned apps:
125 // - have non-default non-empty pinned apps list
126 // - kUseDefaultPinnedApps set to false
127 // Nothing needs to be done because customized pref overrides default.
128 //
129 // - Users that have default apps (i.e. new user or never pin/unpin):
130 // - have default pinned apps
131 // - kUseDefaultPinnedApps is still true
132 // Nothing needs to be done because they should get the default.
133 if (profile_->GetPrefs()->FindPreference(
134 prefs::kPinnedLauncherApps)->IsDefaultValue() &&
135 !profile_->GetPrefs()->GetBoolean(prefs::kUseDefaultPinnedApps)) {
136 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
137 updater.Get()->Clear();
138 }
139
140 UpdateAppLaunchersFromPref();
141
142 // TODO(sky): update unit test so that this test isn't necessary.
143 if (ash::Shell::HasInstance()) {
144 SetShelfAutoHideBehaviorFromPrefs();
145 SetShelfAlignmentFromPrefs();
146
147 activation_client_ =
148 aura::client::GetActivationClient(
149 ash::Shell::GetInstance()->GetPrimaryRootWindow());
150 activation_client_->AddObserver(this);
151
152 ash::Shell::GetInstance()->AddShellObserver(this);
153 }
154 }
155
156 ash::LauncherID ChromeLauncherController::CreateTabbedLauncherItem(
157 BrowserLauncherItemController* controller,
158 IncognitoState is_incognito,
159 ash::LauncherItemStatus status) {
160 ash::LauncherID id = model_->next_id();
161 DCHECK(id_to_item_map_.find(id) == id_to_item_map_.end());
162 id_to_item_map_[id].item_type = TYPE_TABBED_BROWSER;
163 id_to_item_map_[id].controller = controller;
164
165 ash::LauncherItem item;
166 item.type = ash::TYPE_TABBED;
167 item.is_incognito = (is_incognito == STATE_INCOGNITO);
168 item.status = status;
169 model_->Add(item);
170 return id;
171 }
172
173 ash::LauncherID ChromeLauncherController::CreateAppLauncherItem(
174 BrowserLauncherItemController* controller,
175 const std::string& app_id,
176 ash::LauncherItemStatus status) {
177 return InsertAppLauncherItem(controller, app_id, status,
178 model_->item_count());
179 }
180
181 void ChromeLauncherController::SetItemStatus(ash::LauncherID id,
182 ash::LauncherItemStatus status) {
183 int index = model_->ItemIndexByID(id);
184 DCHECK_GE(index, 0);
185 ash::LauncherItem item = model_->items()[index];
186 item.status = status;
187 model_->Set(index, item);
188 }
189
190 void ChromeLauncherController::LauncherItemClosed(ash::LauncherID id) {
191 DCHECK(id_to_item_map_.find(id) != id_to_item_map_.end());
192 id_to_item_map_.erase(id);
193 model_->RemoveItemAt(model_->ItemIndexByID(id));
194 }
195
196 void ChromeLauncherController::Unpin(ash::LauncherID id) {
197 DCHECK(id_to_item_map_.find(id) != id_to_item_map_.end());
198 DCHECK(!id_to_item_map_[id].controller);
199
200 if (ShellWindowRegistry::Get(profile_)->GetShellWindowsForApp(
201 id_to_item_map_[id].app_id).size() > 0) {
202 int index = model_->ItemIndexByID(id);
203 ash::LauncherItem item = model_->items()[index];
204 item.type = ash::TYPE_PLATFORM_APP;
205 model_->Set(index, item);
206 } else {
207 LauncherItemClosed(id);
208 }
209 if (CanPin())
210 PersistPinnedState();
211 }
212
213 void ChromeLauncherController::Pin(ash::LauncherID id) {
214 DCHECK(id_to_item_map_.find(id) != id_to_item_map_.end());
215 DCHECK(!id_to_item_map_[id].controller);
216
217 int index = model_->ItemIndexByID(id);
218 ash::LauncherItem item = model_->items()[index];
219
220 if (item.type != ash::TYPE_PLATFORM_APP)
221 return;
222
223 item.type = ash::TYPE_APP_SHORTCUT;
224 model_->Set(index, item);
225
226 if (CanPin())
227 PersistPinnedState();
228 }
229
230 bool ChromeLauncherController::IsPinned(ash::LauncherID id) {
231 int index = model_->ItemIndexByID(id);
232 ash::LauncherItemType type = model_->items()[index].type;
233 return type == ash::TYPE_APP_SHORTCUT;
234 }
235
236 void ChromeLauncherController::TogglePinned(ash::LauncherID id) {
237 if (id_to_item_map_.find(id) == id_to_item_map_.end())
238 return; // May happen if item closed with menu open.
239
240 if (IsPinned(id))
241 Unpin(id);
242 else
243 Pin(id);
244 }
245
246 bool ChromeLauncherController::IsPinnable(ash::LauncherID id) const {
247 int index = model_->ItemIndexByID(id);
248 if (index == -1)
249 return false;
250
251 ash::LauncherItemType type = model_->items()[index].type;
252 return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_PLATFORM_APP) &&
253 CanPin());
254 }
255
256 void ChromeLauncherController::Open(ash::LauncherID id, int event_flags) {
257 if (id_to_item_map_.find(id) == id_to_item_map_.end())
258 return; // In case invoked from menu and item closed while menu up.
259
260 BrowserLauncherItemController* controller = id_to_item_map_[id].controller;
261 if (controller) {
262 controller->window()->Show();
263 ash::wm::ActivateWindow(controller->window());
264 } else {
265 DCHECK_EQ(TYPE_APP, id_to_item_map_[id].item_type);
266
267 // Do nothing for pending app shortcut.
268 if (GetItemStatus(id) == ash::STATUS_IS_PENDING)
269 return;
270
271 OpenAppID(id_to_item_map_[id].app_id, event_flags);
272 }
273 }
274
275 void ChromeLauncherController::OpenAppID(
276 const std::string& app_id,
277 int event_flags) {
278 ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
279 // Check if this item has any windows in the activation list.
280 for (WindowList::const_iterator i = platform_app_windows_.begin();
281 i != platform_app_windows_.end(); ++i) {
282 if (window_to_id_map_[*i] == launcher_id) {
283 (*i)->Show();
284 ash::wm::ActivateWindow(*i);
285 return;
286 }
287 }
288
289 // Check if there are any open tabs for this app.
290 AppIDToTabContentsListMap::iterator i =
291 app_id_to_tab_contents_list_.find(app_id);
292
293 if (i != app_id_to_tab_contents_list_.end()) {
294 DCHECK(!i->second.empty());
295 TabContents* tab = i->second.front();
296 Browser* browser = browser::FindBrowserWithWebContents(
297 tab->web_contents());
298 TabStripModel* tab_strip = browser->tab_strip_model();
299 int index = tab_strip->GetIndexOfTabContents(tab);
300 DCHECK_NE(TabStripModel::kNoTab, index);
301 tab_strip->ActivateTabAt(index, false);
302 browser->window()->Show();
303 ash::wm::ActivateWindow(browser->window()->GetNativeWindow());
304 } else {
305 const Extension* extension =
306 profile_->GetExtensionService()->GetInstalledExtension(app_id);
307 extension_utils::OpenExtension(GetProfileForNewWindows(),
308 extension,
309 event_flags);
310 }
311 }
312
313 void ChromeLauncherController::Close(ash::LauncherID id) {
314 if (id_to_item_map_.find(id) == id_to_item_map_.end())
315 return; // May happen if menu closed.
316
317 if (!id_to_item_map_[id].controller)
318 return; // TODO: maybe should treat as unpin?
319
320 views::Widget* widget = views::Widget::GetWidgetForNativeView(
321 id_to_item_map_[id].controller->window());
322 if (widget)
323 widget->Close();
324 }
325
326 bool ChromeLauncherController::IsOpen(ash::LauncherID id) {
327 return id_to_item_map_.find(id) != id_to_item_map_.end() &&
328 id_to_item_map_[id].controller != NULL;
329 }
330
331 extensions::ExtensionPrefs::LaunchType ChromeLauncherController::GetLaunchType(
332 ash::LauncherID id) {
333 DCHECK(id_to_item_map_.find(id) != id_to_item_map_.end());
334
335 return profile_->GetExtensionService()->extension_prefs()->GetLaunchType(
336 id_to_item_map_[id].app_id, extensions::ExtensionPrefs::LAUNCH_DEFAULT);
337 }
338
339 std::string ChromeLauncherController::GetAppID(TabContents* tab) {
340 return app_tab_helper_->GetAppID(tab);
341 }
342
343 ash::LauncherID ChromeLauncherController::GetLauncherIDForAppID(
344 const std::string& app_id) {
345 for (IDToItemMap::const_iterator i = id_to_item_map_.begin();
346 i != id_to_item_map_.end(); ++i) {
347 if (i->second.app_id == app_id)
348 return i->first;
349 }
350 return 0;
351 }
352
353 void ChromeLauncherController::SetAppImage(const std::string& id,
354 const gfx::ImageSkia& image) {
355 // TODO: need to get this working for shortcuts.
356
357 for (IDToItemMap::const_iterator i = id_to_item_map_.begin();
358 i != id_to_item_map_.end(); ++i) {
359 if (i->second.app_id != id)
360 continue;
361
362 // Panel items may share the same app_id as the app that created them,
363 // but they set their icon image in
364 // BrowserLauncherItemController::UpdateLauncher(), so do not set panel
365 // images here.
366 if (i->second.controller &&
367 i->second.controller->type() ==
368 BrowserLauncherItemController::TYPE_EXTENSION_PANEL) {
369 continue;
370 }
371
372 int index = model_->ItemIndexByID(i->first);
373 ash::LauncherItem item = model_->items()[index];
374 item.image = image;
375 model_->Set(index, item);
376 // It's possible we're waiting on more than one item, so don't break.
377 }
378 }
379
380 bool ChromeLauncherController::IsAppPinned(const std::string& app_id) {
381 for (IDToItemMap::const_iterator i = id_to_item_map_.begin();
382 i != id_to_item_map_.end(); ++i) {
383 if (IsPinned(i->first) && i->second.app_id == app_id)
384 return true;
385 }
386 return false;
387 }
388
389 void ChromeLauncherController::PinAppWithID(const std::string& app_id) {
390 if (CanPin())
391 DoPinAppWithID(app_id);
392 else
393 NOTREACHED();
394 }
395
396 void ChromeLauncherController::SetLaunchType(
397 ash::LauncherID id,
398 extensions::ExtensionPrefs::LaunchType launch_type) {
399 if (id_to_item_map_.find(id) == id_to_item_map_.end())
400 return;
401
402 return profile_->GetExtensionService()->extension_prefs()->SetLaunchType(
403 id_to_item_map_[id].app_id, launch_type);
404 }
405
406 void ChromeLauncherController::UnpinAppsWithID(const std::string& app_id) {
407 if (CanPin())
408 DoUnpinAppsWithID(app_id);
409 else
410 NOTREACHED();
411 }
412
413 bool ChromeLauncherController::IsLoggedInAsGuest() {
414 return ProfileManager::GetDefaultProfileOrOffTheRecord()->IsOffTheRecord();
415 }
416
417 void ChromeLauncherController::CreateNewIncognitoWindow() {
418 chrome::NewEmptyWindow(GetProfileForNewWindows()->GetOffTheRecordProfile());
419 }
420
421 bool ChromeLauncherController::CanPin() const {
422 const PrefService::Preference* pref =
423 profile_->GetPrefs()->FindPreference(prefs::kPinnedLauncherApps);
424 return pref && pref->IsUserModifiable();
425 }
426
427 void ChromeLauncherController::SetAutoHideBehavior(
428 ash::ShelfAutoHideBehavior behavior) {
429 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(behavior);
430 const char* value = NULL;
431 switch (behavior) {
432 case ash::SHELF_AUTO_HIDE_BEHAVIOR_DEFAULT:
433 value = ash::kShelfAutoHideBehaviorDefault;
434 break;
435 case ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
436 value = ash::kShelfAutoHideBehaviorAlways;
437 break;
438 case ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
439 value = ash::kShelfAutoHideBehaviorNever;
440 break;
441 }
442 profile_->GetPrefs()->SetString(prefs::kShelfAutoHideBehavior, value);
443 }
444
445 void ChromeLauncherController::RemoveTabFromRunningApp(
446 TabContents* tab,
447 const std::string& app_id) {
448 tab_contents_to_app_id_.erase(tab);
449 AppIDToTabContentsListMap::iterator i_app_id =
450 app_id_to_tab_contents_list_.find(app_id);
451 if (i_app_id != app_id_to_tab_contents_list_.end()) {
452 TabContentsList* tab_list = &i_app_id->second;
453 tab_list->remove(tab);
454 if (tab_list->empty()) {
455 app_id_to_tab_contents_list_.erase(i_app_id);
456 i_app_id = app_id_to_tab_contents_list_.end();
457 ash::LauncherID id = GetLauncherIDForAppID(app_id);
458 if (id > 0)
459 SetItemStatus(id, ash::STATUS_CLOSED);
460 }
461 }
462 }
463
464 void ChromeLauncherController::UpdateAppState(TabContents* tab,
465 AppState app_state) {
466 std::string app_id = GetAppID(tab);
467
468 // Check the old |app_id| for a tab. If the contents has changed we need to
469 // remove it from the previous app.
470 if (tab_contents_to_app_id_.find(tab) != tab_contents_to_app_id_.end()) {
471 std::string last_app_id = tab_contents_to_app_id_[tab];
472 if (last_app_id != app_id)
473 RemoveTabFromRunningApp(tab, last_app_id);
474 }
475
476 if (app_id.empty())
477 return;
478
479 tab_contents_to_app_id_[tab] = app_id;
480
481 if (app_state == APP_STATE_REMOVED) {
482 // The tab has gone away.
483 RemoveTabFromRunningApp(tab, app_id);
484 } else {
485 TabContentsList& tab_list(app_id_to_tab_contents_list_[app_id]);
486 if (app_state == APP_STATE_INACTIVE) {
487 TabContentsList::const_iterator i_tab =
488 std::find(tab_list.begin(), tab_list.end(), tab);
489 if (i_tab == tab_list.end())
490 tab_list.push_back(tab);
491 } else {
492 tab_list.remove(tab);
493 tab_list.push_front(tab);
494 }
495 ash::LauncherID id = GetLauncherIDForAppID(app_id);
496 if (id > 0) {
497 // If the window is active, mark the app as active.
498 SetItemStatus(id, app_state == APP_STATE_WINDOW_ACTIVE ?
499 ash::STATUS_ACTIVE : ash::STATUS_RUNNING);
500 }
501 }
502 }
503
504 void ChromeLauncherController::CreateNewTab() {
505 Browser* last_browser = browser::FindTabbedBrowser(
506 GetProfileForNewWindows(), true);
507
508 if (!last_browser) {
509 CreateNewWindow();
510 return;
511 }
512
513 chrome::NewTab(last_browser);
514 aura::Window* window = last_browser->window()->GetNativeWindow();
515 window->Show();
516 ash::wm::ActivateWindow(window);
517 }
518
519 void ChromeLauncherController::CreateNewWindow() {
520 chrome::NewEmptyWindow(GetProfileForNewWindows());
521 }
522
523 void ChromeLauncherController::ItemClicked(const ash::LauncherItem& item,
524 int event_flags) {
525 DCHECK(id_to_item_map_.find(item.id) != id_to_item_map_.end());
526 BrowserLauncherItemController* controller =
527 id_to_item_map_[item.id].controller;
528 if (controller) {
529 views::Widget* widget =
530 views::Widget::GetWidgetForNativeView(controller->window());
531 if (widget && widget->IsActive()) {
532 widget->Minimize();
533 return;
534 }
535 // else case, fall through to show window.
536 }
537 Open(item.id, event_flags);
538 }
539
540 int ChromeLauncherController::GetBrowserShortcutResourceId() {
541 return IDR_PRODUCT_LOGO_32;
542 }
543
544 string16 ChromeLauncherController::GetTitle(const ash::LauncherItem& item) {
545 DCHECK(id_to_item_map_.find(item.id) != id_to_item_map_.end());
546 BrowserLauncherItemController* controller =
547 id_to_item_map_[item.id].controller;
548 if (controller) {
549 if (id_to_item_map_[item.id].item_type == TYPE_TABBED_BROWSER) {
550 return controller->tab_model()->GetActiveTabContents() ?
551 controller->tab_model()->GetActiveTabContents()->web_contents()->
552 GetTitle() : string16();
553 }
554 // Fall through to get title from extension.
555 }
556 const Extension* extension = profile_->GetExtensionService()->
557 GetInstalledExtension(id_to_item_map_[item.id].app_id);
558 return extension ? UTF8ToUTF16(extension->name()) : string16();
559 }
560
561 ui::MenuModel* ChromeLauncherController::CreateContextMenu(
562 const ash::LauncherItem& item) {
563 return new LauncherContextMenu(this, &item);
564 }
565
566 ui::MenuModel* ChromeLauncherController::CreateContextMenuForLauncher() {
567 return new LauncherContextMenu(this, NULL);
568 }
569
570 ash::LauncherID ChromeLauncherController::GetIDByWindow(
571 aura::Window* window) {
572 for (IDToItemMap::const_iterator i = id_to_item_map_.begin();
573 i != id_to_item_map_.end(); ++i) {
574 if (i->second.controller && i->second.controller->window() == window)
575 return i->first;
576 }
577 return 0;
578 }
579
580 bool ChromeLauncherController::IsDraggable(const ash::LauncherItem& item) {
581 return item.type == ash::TYPE_APP_SHORTCUT ? CanPin() : true;
582 }
583
584 void ChromeLauncherController::LauncherItemAdded(int index) {
585 }
586
587 void ChromeLauncherController::LauncherItemRemoved(int index,
588 ash::LauncherID id) {
589 }
590
591 void ChromeLauncherController::LauncherItemMoved(
592 int start_index,
593 int target_index) {
594 ash::LauncherID id = model_->items()[target_index].id;
595 if (id_to_item_map_.find(id) != id_to_item_map_.end() && IsPinned(id))
596 PersistPinnedState();
597 }
598
599 void ChromeLauncherController::LauncherItemChanged(
600 int index,
601 const ash::LauncherItem& old_item) {
602 if (model_->items()[index].status == ash::STATUS_ACTIVE &&
603 old_item.status == ash::STATUS_RUNNING) {
604 ash::LauncherID id = model_->items()[index].id;
605 if (id_to_item_map_[id].controller) {
606 aura::Window* window_to_activate =
607 id_to_item_map_[id].controller->window();
608 if (window_to_activate && ash::wm::IsActiveWindow(window_to_activate))
609 return;
610 window_to_activate->Show();
611 ash::wm::ActivateWindow(window_to_activate);
612 }
613 }
614 }
615
616 void ChromeLauncherController::Observe(
617 int type,
618 const content::NotificationSource& source,
619 const content::NotificationDetails& details) {
620 switch (type) {
621 case chrome::NOTIFICATION_EXTENSION_LOADED: {
622 UpdateAppLaunchersFromPref();
623 break;
624 }
625 case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
626 const content::Details<extensions::UnloadedExtensionInfo> unload_info(
627 details);
628 const Extension* extension = unload_info->extension;
629 if (IsAppPinned(extension->id())) {
630 if (unload_info->reason == extension_misc::UNLOAD_REASON_UPDATE)
631 MarkAppPending(extension->id());
632 else
633 DoUnpinAppsWithID(extension->id());
634 }
635 break;
636 }
637 case chrome::NOTIFICATION_PREF_CHANGED: {
638 const std::string& pref_name(
639 *content::Details<std::string>(details).ptr());
640 if (pref_name == prefs::kPinnedLauncherApps)
641 UpdateAppLaunchersFromPref();
642 else if (pref_name == prefs::kShelfAlignment)
643 SetShelfAlignmentFromPrefs();
644 else if (pref_name == prefs::kShelfAutoHideBehavior)
645 SetShelfAutoHideBehaviorFromPrefs();
646 else
647 NOTREACHED() << "Unexpected pref change for " << pref_name;
648 break;
649 }
650 default:
651 NOTREACHED() << "Unexpected notification type=" << type;
652 }
653 }
654
655 void ChromeLauncherController::OnShellWindowAdded(ShellWindow* shell_window) {
656 aura::Window* window = shell_window->GetNativeWindow();
657 ash::LauncherItemStatus status = ash::wm::IsActiveWindow(window) ?
658 ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
659 window->AddObserver(this);
660 const std::string app_id = shell_window->extension()->id();
661 ash::LauncherID id = 0;
662 for (IDToItemMap::const_iterator i = id_to_item_map_.begin();
663 i != id_to_item_map_.end(); ++i) {
664 if (i->second.app_id == app_id) {
665 id = i->first;
666 SetItemStatus(id, status);
667 break;
668 }
669 }
670 if (id == 0)
671 id = CreateAppLauncherItem(NULL, app_id, status);
672 window_to_id_map_[window] = id;
673 if (status == ash::STATUS_ACTIVE)
674 platform_app_windows_.push_front(window);
675 else
676 platform_app_windows_.push_back(window);
677 }
678
679 void ChromeLauncherController::OnShellWindowRemoved(ShellWindow* shell_window) {
680 // Window removal is handled in OnWindowRemovingFromRootWindow() below.
681 }
682
683 void ChromeLauncherController::OnWindowActivated(aura::Window* active,
684 aura::Window* old_active) {
685 if (window_to_id_map_.find(active) != window_to_id_map_.end()) {
686 ash::LauncherID active_id = window_to_id_map_[active];
687 platform_app_windows_.remove(active);
688 platform_app_windows_.push_front(active);
689 if (window_to_id_map_.find(old_active) != window_to_id_map_.end() &&
690 window_to_id_map_[old_active] == active_id) {
691 // Old and new windows are for the same item. Don't change the status.
692 return;
693 }
694 SetItemStatus(active_id, ash::STATUS_ACTIVE);
695 }
696 if (window_to_id_map_.find(old_active) != window_to_id_map_.end())
697 SetItemStatus(window_to_id_map_[old_active], ash::STATUS_RUNNING);
698 }
699
700 void ChromeLauncherController::OnWindowRemovingFromRootWindow(
701 aura::Window* window) {
702 window->RemoveObserver(this);
703 DCHECK(window_to_id_map_.find(window) != window_to_id_map_.end());
704 ash::LauncherID id = window_to_id_map_[window];
705 window_to_id_map_.erase(window);
706
707 DCHECK(id_to_item_map_.find(id) != id_to_item_map_.end());
708 platform_app_windows_.remove(window);
709 ShellWindowRegistry::ShellWindowSet remaining_windows =
710 ShellWindowRegistry::Get(profile_)->GetShellWindowsForApp(
711 id_to_item_map_[id].app_id);
712
713 // We can't count on getting called before or after the ShellWindowRegistry.
714 if (remaining_windows.size() > 1 ||
715 (remaining_windows.size() == 1 &&
716 (*remaining_windows.begin())->GetNativeWindow() != window)) {
717 return;
718 }
719
720 // Close or remove item.
721 int index = model_->ItemIndexByID(id);
722 DCHECK_GE(index, 0);
723 ash::LauncherItem item = model_->items()[index];
724 if (item.type == ash::TYPE_APP_SHORTCUT)
725 SetItemStatus(id, ash::STATUS_CLOSED);
726 else
727 LauncherItemClosed(id);
728 }
729
730 void ChromeLauncherController::OnShelfAlignmentChanged() {
731 const char* pref_value = NULL;
732 switch (ash::Shell::GetInstance()->GetShelfAlignment()) {
733 case ash::SHELF_ALIGNMENT_BOTTOM:
734 pref_value = ash::kShelfAlignmentBottom;
735 break;
736 case ash::SHELF_ALIGNMENT_LEFT:
737 pref_value = ash::kShelfAlignmentLeft;
738 break;
739 case ash::SHELF_ALIGNMENT_RIGHT:
740 pref_value = ash::kShelfAlignmentRight;
741 break;
742 }
743 profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value);
744 }
745
746 void ChromeLauncherController::PersistPinnedState() {
747 // It is a coding error to call PersistPinnedState() if the pinned apps are
748 // not user-editable. The code should check earlier and not perform any
749 // modification actions that trigger persisting the state.
750 if (!CanPin()) {
751 NOTREACHED() << "Can't pin but pinned state being updated";
752 return;
753 }
754
755 // Set kUseDefaultPinnedApps to false and use pinned apps list from prefs
756 // from now on.
757 profile_->GetPrefs()->SetBoolean(prefs::kUseDefaultPinnedApps, false);
758
759 // Mutating kPinnedLauncherApps is going to notify us and trigger us to
760 // process the change. We don't want that to happen so remove ourselves as a
761 // listener.
762 pref_change_registrar_.Remove(prefs::kPinnedLauncherApps, this);
763 {
764 ListPrefUpdate updater(profile_->GetPrefs(), prefs::kPinnedLauncherApps);
765 updater->Clear();
766 for (size_t i = 0; i < model_->items().size(); ++i) {
767 if (model_->items()[i].type == ash::TYPE_APP_SHORTCUT) {
768 ash::LauncherID id = model_->items()[i].id;
769 if (id_to_item_map_.find(id) != id_to_item_map_.end() &&
770 IsPinned(id)) {
771 base::DictionaryValue* app_value = ash::CreateAppDict(
772 id_to_item_map_[id].app_id);
773 if (app_value)
774 updater->Append(app_value);
775 }
776 }
777 }
778 }
779 pref_change_registrar_.Add(prefs::kPinnedLauncherApps, this);
780 }
781
782 void ChromeLauncherController::SetAppTabHelperForTest(AppTabHelper* helper) {
783 app_tab_helper_.reset(helper);
784 }
785
786 void ChromeLauncherController::SetAppIconLoaderForTest(AppIconLoader* loader) {
787 app_icon_loader_.reset(loader);
788 }
789
790 Profile* ChromeLauncherController::GetProfileForNewWindows() {
791 return ProfileManager::GetDefaultProfileOrOffTheRecord();
792 }
793
794 ash::LauncherItemStatus ChromeLauncherController::GetItemStatus(
795 ash::LauncherID id) const {
796 int index = model_->ItemIndexByID(id);
797 DCHECK_GE(index, 0);
798 const ash::LauncherItem& item = model_->items()[index];
799 return item.status;
800 }
801
802 void ChromeLauncherController::MarkAppPending(const std::string& app_id) {
803 for (IDToItemMap::const_iterator i = id_to_item_map_.begin();
804 i != id_to_item_map_.end(); ++i) {
805 if (i->second.item_type == TYPE_APP && i->second.app_id == app_id) {
806 if (GetItemStatus(i->first) == ash::STATUS_CLOSED)
807 SetItemStatus(i->first, ash::STATUS_IS_PENDING);
808
809 break;
810 }
811 }
812 }
813
814 void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) {
815 // If there is an item, do nothing and return.
816 if (IsAppPinned(app_id))
817 return;
818
819 // Otherwise, create an item for it.
820 CreateAppLauncherItem(NULL, app_id, ash::STATUS_CLOSED);
821 if (CanPin())
822 PersistPinnedState();
823 }
824
825 void ChromeLauncherController::DoUnpinAppsWithID(const std::string& app_id) {
826 for (IDToItemMap::iterator i = id_to_item_map_.begin();
827 i != id_to_item_map_.end(); ) {
828 IDToItemMap::iterator current(i);
829 ++i;
830 if (current->second.app_id == app_id && IsPinned(current->first))
831 Unpin(current->first);
832 }
833 }
834
835 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
836 // Construct a vector representation of to-be-pinned apps from the pref.
837 std::vector<std::string> pinned_apps;
838 const base::ListValue* pinned_apps_pref =
839 profile_->GetPrefs()->GetList(prefs::kPinnedLauncherApps);
840 for (base::ListValue::const_iterator it(pinned_apps_pref->begin());
841 it != pinned_apps_pref->end(); ++it) {
842 DictionaryValue* app = NULL;
843 std::string app_id;
844 if ((*it)->GetAsDictionary(&app) &&
845 app->GetString(ash::kPinnedAppsPrefAppIDPath, &app_id) &&
846 std::find(pinned_apps.begin(), pinned_apps.end(), app_id) ==
847 pinned_apps.end() &&
848 app_tab_helper_->IsValidID(app_id)) {
849 pinned_apps.push_back(app_id);
850 }
851 }
852
853 // Walk the model and |pinned_apps| from the pref lockstep, adding and
854 // removing items as necessary. NB: This code uses plain old indexing instead
855 // of iterators because of model mutations as part of the loop.
856 std::vector<std::string>::const_iterator pref_app_id(pinned_apps.begin());
857 int index = 0;
858 for (; index < model_->item_count() && pref_app_id != pinned_apps.end();
859 ++index) {
860 // If the next app launcher according to the pref is present in the model,
861 // delete all app launcher entries in between.
862 if (IsAppPinned(*pref_app_id)) {
863 for (; index < model_->item_count(); ++index) {
864 const ash::LauncherItem& item(model_->items()[index]);
865 if (item.type != ash::TYPE_APP_SHORTCUT)
866 continue;
867
868 IDToItemMap::const_iterator entry(id_to_item_map_.find(item.id));
869 if (entry != id_to_item_map_.end() &&
870 entry->second.app_id == *pref_app_id) {
871 // Current item will be kept. Reset its pending state and ensure
872 // its icon is loaded since it has to be valid to be in |pinned_apps|.
873 if (item.status == ash::STATUS_IS_PENDING) {
874 SetItemStatus(item.id, ash::STATUS_CLOSED);
875 app_icon_loader_->FetchImage(*pref_app_id);
876 }
877
878 ++pref_app_id;
879 break;
880 } else {
881 LauncherItemClosed(item.id);
882 --index;
883 }
884 }
885 // If the item wasn't found, that means id_to_item_map_ is out of sync.
886 DCHECK(index < model_->item_count());
887 } else {
888 // This app wasn't pinned before, insert a new entry.
889 ash::LauncherID id = InsertAppLauncherItem(NULL, *pref_app_id,
890 ash::STATUS_CLOSED, index);
891 index = model_->ItemIndexByID(id);
892 ++pref_app_id;
893 }
894 }
895
896 // Remove any trailing existing items.
897 while (index < model_->item_count()) {
898 const ash::LauncherItem& item(model_->items()[index]);
899 if (item.type == ash::TYPE_APP_SHORTCUT)
900 LauncherItemClosed(item.id);
901 else
902 ++index;
903 }
904
905 // Append unprocessed items from the pref to the end of the model.
906 for (; pref_app_id != pinned_apps.end(); ++pref_app_id)
907 CreateAppLauncherItem(NULL, *pref_app_id, ash::STATUS_CLOSED);
908 }
909
910 void ChromeLauncherController::SetShelfAutoHideBehaviorFromPrefs() {
911 const std::string behavior_value(
912 profile_->GetPrefs()->GetString(prefs::kShelfAutoHideBehavior));
913 ash::ShelfAutoHideBehavior behavior =
914 ash::SHELF_AUTO_HIDE_BEHAVIOR_DEFAULT;
915 if (behavior_value == ash::kShelfAutoHideBehaviorNever)
916 behavior = ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
917 else if (behavior_value == ash::kShelfAutoHideBehaviorAlways)
918 behavior = ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
919 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(behavior);
920 }
921
922 void ChromeLauncherController::SetShelfAlignmentFromPrefs() {
923 if (!CommandLine::ForCurrentProcess()->HasSwitch(
924 switches::kShowLauncherAlignmentMenu))
925 return;
926
927 const std::string alignment_value(
928 profile_->GetPrefs()->GetString(prefs::kShelfAlignment));
929 ash::ShelfAlignment alignment = ash::SHELF_ALIGNMENT_BOTTOM;
930 if (alignment_value == ash::kShelfAlignmentLeft)
931 alignment = ash::SHELF_ALIGNMENT_LEFT;
932 else if (alignment_value == ash::kShelfAlignmentRight)
933 alignment = ash::SHELF_ALIGNMENT_RIGHT;
934 ash::Shell::GetInstance()->SetShelfAlignment(alignment);
935 }
936
937 TabContents* ChromeLauncherController::GetLastActiveTabContents(
938 const std::string& app_id) {
939 AppIDToTabContentsListMap::const_iterator i =
940 app_id_to_tab_contents_list_.find(app_id);
941 if (i == app_id_to_tab_contents_list_.end())
942 return NULL;
943 DCHECK_GT(i->second.size(), 0u);
944 return *i->second.begin();
945 }
946
947 ash::LauncherID ChromeLauncherController::InsertAppLauncherItem(
948 BrowserLauncherItemController* controller,
949 const std::string& app_id,
950 ash::LauncherItemStatus status,
951 int index) {
952 ash::LauncherID id = model_->next_id();
953 DCHECK(id_to_item_map_.find(id) == id_to_item_map_.end());
954 id_to_item_map_[id].item_type = TYPE_APP;
955 id_to_item_map_[id].app_id = app_id;
956 id_to_item_map_[id].controller = controller;
957
958 ash::LauncherItem item;
959 if (!controller) {
960 if (status == ash::STATUS_CLOSED)
961 item.type = ash::TYPE_APP_SHORTCUT;
962 else
963 item.type = ash::TYPE_PLATFORM_APP;
964 } else if (controller->type() ==
965 BrowserLauncherItemController::TYPE_APP_PANEL ||
966 controller->type() ==
967 BrowserLauncherItemController::TYPE_EXTENSION_PANEL) {
968 item.type = ash::TYPE_APP_PANEL;
969 } else {
970 item.type = ash::TYPE_TABBED;
971 }
972 item.is_incognito = false;
973 item.image = Extension::GetDefaultIcon(true);
974 if (item.type == ash::TYPE_APP_SHORTCUT &&
975 !app_tab_helper_->IsValidID(app_id)) {
976 item.status = ash::STATUS_IS_PENDING;
977 } else {
978 TabContents* active_tab = GetLastActiveTabContents(app_id);
979 if (active_tab) {
980 Browser* browser = browser::FindBrowserWithWebContents(
981 active_tab->web_contents());
982 DCHECK(browser);
983 if (browser->window()->IsActive())
984 status = ash::STATUS_ACTIVE;
985 else
986 status = ash::STATUS_RUNNING;
987 }
988 item.status = status;
989 }
990 model_->AddAt(index, item);
991
992 if (!controller || controller->type() !=
993 BrowserLauncherItemController::TYPE_EXTENSION_PANEL) {
994 if (item.status != ash::STATUS_IS_PENDING)
995 app_icon_loader_->FetchImage(app_id);
996 }
997 return id;
998 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698