OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/views/aura/launcher/launcher_updater.h" | |
6 | |
7 #include "ash/launcher/launcher.h" | |
8 #include "ash/launcher/launcher_model.h" | |
9 #include "ash/shell.h" | |
10 #include "ash/wm/window_util.h" | |
11 #include "chrome/browser/extensions/extension_service.h" | |
12 #include "chrome/browser/extensions/extension_tab_helper.h" | |
13 #include "chrome/browser/favicon/favicon_tab_helper.h" | |
14 #include "chrome/browser/tabs/tab_strip_model.h" | |
15 #include "chrome/browser/ui/browser.h" | |
16 #include "chrome/browser/ui/browser_window.h" | |
17 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
18 #include "chrome/browser/ui/views/aura/launcher/chrome_launcher_delegate.h" | |
19 #include "chrome/browser/web_applications/web_app.h" | |
20 #include "content/public/browser/web_contents.h" | |
21 #include "grit/ui_resources.h" | |
22 #include "ui/aura/window.h" | |
23 #include "ui/base/resource/resource_bundle.h" | |
24 | |
25 LauncherUpdater::AppTabDetails::AppTabDetails() : id(0) { | |
26 } | |
27 | |
28 LauncherUpdater::AppTabDetails::~AppTabDetails() { | |
29 } | |
30 | |
31 LauncherUpdater::LauncherUpdater(aura::Window* window, | |
32 TabStripModel* tab_model, | |
33 ChromeLauncherDelegate* delegate, | |
34 Type type, | |
35 const std::string& app_id) | |
36 : window_(window), | |
37 tab_model_(tab_model), | |
38 launcher_delegate_(delegate), | |
39 type_(type), | |
40 app_id_(app_id), | |
41 item_id_(-1) { | |
42 } | |
43 | |
44 LauncherUpdater::~LauncherUpdater() { | |
45 tab_model_->RemoveObserver(this); | |
46 if (item_id_ != -1) | |
47 launcher_delegate_->LauncherItemClosed(item_id_); | |
48 for (AppTabMap::iterator i = app_map_.begin(); i != app_map_.end(); ++i) | |
49 launcher_delegate_->LauncherItemClosed(i->second.id); | |
50 } | |
51 | |
52 void LauncherUpdater::Init() { | |
53 tab_model_->AddObserver(this); | |
54 if (type_ == TYPE_APP) { | |
55 // App type never changes, create the launcher item immediately. | |
56 item_id_ = launcher_delegate_->CreateAppLauncherItem( | |
57 this, app_id_, ChromeLauncherDelegate::APP_TYPE_WINDOW); | |
58 } else { | |
59 // Determine if we have any tabs that should get launcher items. | |
60 std::vector<TabContentsWrapper*> app_tabs; | |
61 for (int i = 0; i < tab_model_->count(); ++i) { | |
62 TabContentsWrapper* tab = tab_model_->GetTabContentsAt(i); | |
63 if (!launcher_delegate_->GetAppID(tab).empty()) | |
64 app_tabs.push_back(tab); | |
65 } | |
66 | |
67 if (static_cast<int>(app_tabs.size()) != tab_model_->count()) | |
68 CreateTabbedItem(); | |
69 | |
70 // Create items for the app tabs. | |
71 for (size_t i = 0; i < app_tabs.size(); ++i) | |
72 AddAppItem(app_tabs[i]); | |
73 } | |
74 UpdateLauncher(tab_model_->GetActiveTabContents()); | |
75 } | |
76 | |
77 // static | |
78 LauncherUpdater* LauncherUpdater::Create(Browser* browser) { | |
79 Type type; | |
80 std::string app_id; | |
81 if (browser->type() == Browser::TYPE_TABBED) { | |
82 type = TYPE_TABBED; | |
83 } else if (browser->is_app()) { | |
84 type = TYPE_APP; | |
85 app_id = web_app::GetExtensionIdFromApplicationName(browser->app_name()); | |
86 } else { | |
87 return NULL; | |
88 } | |
89 LauncherUpdater* icon_updater = new LauncherUpdater( | |
90 browser->window()->GetNativeHandle(), browser->tabstrip_model(), | |
91 ChromeLauncherDelegate::instance(), type, app_id); | |
92 icon_updater->Init(); | |
93 return icon_updater; | |
94 } | |
95 | |
96 TabContentsWrapper* LauncherUpdater::GetTab(ash::LauncherID id) { | |
97 for (AppTabMap::const_iterator i = app_map_.begin(); i != app_map_.end(); | |
98 ++i) { | |
99 if (i->second.id == id) | |
100 return i->first; | |
101 } | |
102 return NULL; | |
103 } | |
104 | |
105 void LauncherUpdater::ActiveTabChanged(TabContentsWrapper* old_contents, | |
106 TabContentsWrapper* new_contents, | |
107 int index, | |
108 bool user_gesture) { | |
109 // Update immediately on a tab change. | |
110 UpdateLauncher(new_contents); | |
111 } | |
112 | |
113 void LauncherUpdater::TabChangedAt( | |
114 TabContentsWrapper* tab, | |
115 int index, | |
116 TabStripModelObserver::TabChangeType change_type) { | |
117 if (type_ == TYPE_TABBED && | |
118 (change_type == TabStripModelObserver::LOADING_ONLY || | |
119 change_type == TabStripModelObserver::ALL)) { | |
120 UpdateAppTabState(tab, UPDATE_TAB_CHANGED); | |
121 } | |
122 | |
123 if (index != tab_model_->active_index() || | |
124 !(change_type != TabStripModelObserver::LOADING_ONLY && | |
125 change_type != TabStripModelObserver::TITLE_NOT_LOADING)) { | |
126 return; | |
127 } | |
128 | |
129 if (tab->favicon_tab_helper()->FaviconIsValid()) { | |
130 // We have the favicon, update immediately. | |
131 UpdateLauncher(tab); | |
132 } else { | |
133 // Let the model know we're waiting. We delay updating as otherwise the user | |
134 // sees flicker as we fetch the favicon. | |
135 int item_index = launcher_model()->ItemIndexByID(item_id_); | |
136 if (item_index == -1) | |
137 return; | |
138 launcher_model()->SetPendingUpdate(item_index); | |
139 } | |
140 } | |
141 | |
142 void LauncherUpdater::TabInsertedAt(TabContentsWrapper* contents, | |
143 int index, | |
144 bool foreground) { | |
145 if (type_ == TYPE_APP) | |
146 return; | |
147 | |
148 UpdateAppTabState(contents, UPDATE_TAB_INSERTED); | |
149 } | |
150 | |
151 void LauncherUpdater::TabReplacedAt(TabStripModel* tab_strip_model, | |
152 TabContentsWrapper* old_contents, | |
153 TabContentsWrapper* new_contents, | |
154 int index) { | |
155 AppTabMap::iterator i = app_map_.find(old_contents); | |
156 if (i != app_map_.end()) { | |
157 AppTabDetails details = i->second; | |
158 app_map_.erase(i); | |
159 i = app_map_.end(); | |
160 app_map_[new_contents] = details; | |
161 UpdateAppTabState(new_contents, UPDATE_TAB_CHANGED); | |
162 } | |
163 } | |
164 | |
165 void LauncherUpdater::TabDetachedAt(TabContentsWrapper* contents, int index) { | |
166 if (type_ == TYPE_APP) | |
167 return; | |
168 | |
169 UpdateAppTabState(contents, UPDATE_TAB_REMOVED); | |
170 if (tab_model_->count() <= 3) { | |
171 // We can't rely on the active tab at this point as the model hasn't fully | |
172 // adjusted itself. We can rely on the count though. | |
173 int item_index = launcher_model()->ItemIndexByID(item_id_); | |
174 if (item_index == -1) | |
175 return; | |
176 | |
177 if (launcher_model()->items()[item_index].type == ash::TYPE_TABBED) { | |
178 ash::LauncherItem new_item(launcher_model()->items()[item_index]); | |
179 new_item.num_tabs = tab_model_->count(); | |
180 launcher_model()->Set(item_index, new_item); | |
181 } | |
182 } | |
183 } | |
184 | |
185 void LauncherUpdater::UpdateLauncher(TabContentsWrapper* tab) { | |
186 if (type_ == TYPE_APP) | |
187 return; // TYPE_APP is entirely maintained by ChromeLauncherDelegate. | |
188 | |
189 if (!tab) | |
190 return; // Assume the window is going to be closed if there are no tabs. | |
191 | |
192 int item_index = launcher_model()->ItemIndexByID(item_id_); | |
193 if (item_index == -1) | |
194 return; | |
195 | |
196 ash::LauncherItem item; | |
197 if (launcher_model()->items()[item_index].type == ash::TYPE_APP) { | |
198 // Use the app icon if we can. | |
199 if (tab->extension_tab_helper()->GetExtensionAppIcon()) | |
200 item.image = *tab->extension_tab_helper()->GetExtensionAppIcon(); | |
201 else | |
202 item.image = tab->favicon_tab_helper()->GetFavicon(); | |
203 } else { | |
204 item.num_tabs = tab_model_->count(); | |
205 if (tab->favicon_tab_helper()->ShouldDisplayFavicon()) { | |
206 item.image = tab->favicon_tab_helper()->GetFavicon(); | |
207 if (item.image.empty()) { | |
208 item.image = *ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
209 IDR_DEFAULT_FAVICON); | |
210 } | |
211 } | |
212 } | |
213 launcher_model()->Set(item_index, item); | |
214 } | |
215 | |
216 void LauncherUpdater::UpdateAppTabState(TabContentsWrapper* tab, | |
217 UpdateType update_type) { | |
218 bool showing_app_item = app_map_.find(tab) != app_map_.end(); | |
219 std::string app_id = update_type == UPDATE_TAB_REMOVED ? | |
220 std::string() : launcher_delegate_->GetAppID(tab); | |
221 bool show_app = !app_id.empty(); | |
222 if (showing_app_item == show_app) { | |
223 if (!show_app) { | |
224 if (item_id_ == -1 && update_type == UPDATE_TAB_INSERTED) { | |
225 // A new non-app tab was added and we have no app tabs. Add one now. | |
226 CreateTabbedItem(); | |
227 } else if (item_id_ != -1 && update_type == UPDATE_TAB_REMOVED && | |
228 tab_model_->count() == (static_cast<int>(app_map_.size()))) { | |
229 launcher_delegate_->LauncherItemClosed(item_id_); | |
230 item_id_ = -1; | |
231 } | |
232 return; | |
233 } | |
234 | |
235 if (app_id != app_map_[tab].app_id) { | |
236 // The extension changed. | |
237 app_map_[tab].app_id = app_id; | |
238 launcher_delegate_->AppIDChanged(app_map_[tab].id, app_id); | |
239 } | |
240 return; | |
241 } | |
242 | |
243 if (showing_app_item) { | |
244 // Going from showing to not showing. | |
245 ash::LauncherID launcher_id(app_map_[tab].id); | |
246 app_map_.erase(tab); | |
247 int model_index = launcher_model()->ItemIndexByID(launcher_id); | |
248 DCHECK_NE(-1, model_index); | |
249 if (item_id_ == -1 && | |
250 (update_type != UPDATE_TAB_REMOVED || | |
251 (tab_model_->count() != 1 && | |
252 tab_model_->count() == (static_cast<int>(app_map_.size()) + 1)))) { | |
253 if (!launcher_delegate_->IsPinned(launcher_id)) { | |
254 // Swap the item for a tabbed item. | |
255 item_id_ = launcher_id; | |
256 launcher_delegate_->ConvertAppToTabbed(item_id_); | |
257 } else { | |
258 // If the app is pinned we have to leave it and create a new tabbed | |
259 // item. | |
260 launcher_delegate_->LauncherItemClosed(launcher_id); | |
261 CreateTabbedItem(); | |
262 } | |
263 ash::LauncherItem item(ash::TYPE_TABBED); | |
264 item.num_tabs = tab_model_->count(); | |
265 launcher_model()->Set(launcher_model()->ItemIndexByID(item_id_), item); | |
266 } else { | |
267 // We have a tabbed item, so we can remove the the app item. | |
268 launcher_delegate_->LauncherItemClosed(launcher_id); | |
269 } | |
270 } else { | |
271 // Going from not showing to showing. | |
272 if (item_id_ != -1 && | |
273 static_cast<int>(app_map_.size()) + 1 == tab_model_->count()) { | |
274 if (launcher_delegate_->HasClosedAppItem( | |
275 launcher_delegate_->GetAppID(tab), | |
276 ChromeLauncherDelegate::APP_TYPE_TAB)) { | |
277 // There's a closed item we can use. Close the tabbed item and add an | |
278 // app item, which will end up using the closed item. | |
279 launcher_delegate_->LauncherItemClosed(item_id_); | |
280 AddAppItem(tab); | |
281 } else { | |
282 // All the tabs are app tabs, replace the tabbed item with the app. | |
283 launcher_delegate_->ConvertTabbedToApp( | |
284 item_id_, | |
285 launcher_delegate_->GetAppID(tab), | |
286 ChromeLauncherDelegate::APP_TYPE_TAB); | |
287 RegisterAppItem(item_id_, tab); | |
288 } | |
289 item_id_ = -1; | |
290 } else { | |
291 AddAppItem(tab); | |
292 } | |
293 } | |
294 } | |
295 | |
296 void LauncherUpdater::AddAppItem(TabContentsWrapper* tab) { | |
297 ash::LauncherID id = launcher_delegate_->CreateAppLauncherItem( | |
298 this, | |
299 launcher_delegate_->GetAppID(tab), | |
300 ChromeLauncherDelegate::APP_TYPE_TAB); | |
301 RegisterAppItem(id, tab); | |
302 } | |
303 | |
304 void LauncherUpdater::RegisterAppItem(ash::LauncherID id, | |
305 TabContentsWrapper* tab) { | |
306 AppTabDetails details; | |
307 details.id = id; | |
308 details.app_id = launcher_delegate_->GetAppID(tab); | |
309 app_map_[tab] = details; | |
310 } | |
311 | |
312 void LauncherUpdater::CreateTabbedItem() { | |
313 DCHECK_EQ(-1, item_id_); | |
314 item_id_ = launcher_delegate_->CreateTabbedLauncherItem(this); | |
315 } | |
316 | |
317 bool LauncherUpdater::ContainsID(ash::LauncherID id, TabContentsWrapper** tab) { | |
318 if (item_id_ == id) | |
319 return true; | |
320 for (AppTabMap::const_iterator i = app_map_.begin(); i != app_map_.end(); | |
321 ++i) { | |
322 if (i->second.id == id) { | |
323 *tab = i->first; | |
324 return true; | |
325 } | |
326 } | |
327 return false; | |
328 } | |
329 | |
330 ash::LauncherModel* LauncherUpdater::launcher_model() { | |
331 return launcher_delegate_->model(); | |
332 } | |
OLD | NEW |