OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" | 5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" |
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 } | |
OLD | NEW |