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

Side by Side Diff: chrome/browser/chromeos/wm_overview_controller.cc

Issue 661237: This adds in the ability for Chrome to generate windows with snapshots of all... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 8 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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2010 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/chromeos/wm_overview_controller.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/linked_ptr.h"
11 #include "chrome/browser/browser.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chromeos/wm_ipc.h"
14 #include "chrome/browser/chromeos/wm_overview_snapshot.h"
15 #include "chrome/browser/renderer_host/render_view_host.h"
16 #include "chrome/browser/tab_contents/tab_contents.h"
17 #include "chrome/browser/tab_contents/tab_contents_view.h"
18 #include "chrome/browser/tab_contents/thumbnail_generator.h"
19 #include "chrome/browser/views/frame/browser_extender.h"
20 #include "chrome/browser/views/frame/browser_view.h"
21 #include "chrome/common/notification_service.h"
22 #include "views/widget/root_view.h"
23 #include "views/widget/widget_gtk.h"
24 #include "views/window/window.h"
25
26 using std::vector;
27
28 #if !defined(OS_CHROMEOS)
29 #error This file is only meant to be compiled for ChromeOS
30 #endif
31
32 namespace chromeos {
33
34 class BrowserListener : public TabStripModelObserver {
35 public:
36 BrowserListener(Browser* browser, WmOverviewController* parent);
37 ~BrowserListener();
38
39 // Begin TabStripModelObserver methods
40 virtual void TabInsertedAt(TabContents* contents,
41 int index,
42 bool foreground);
43 virtual void TabClosingAt(TabContents* contents, int index) {}
44 virtual void TabDetachedAt(TabContents* contents, int index);
45 virtual void TabMoved(TabContents* contents,
46 int from_index,
47 int to_index);
48 virtual void TabChangedAt(TabContents* contents, int index,
49 TabStripModelObserver::TabChangeType change_type);
50 virtual void TabStripEmpty();
51 virtual void TabDeselectedAt(TabContents* contents, int index) {}
52 virtual void TabSelectedAt(TabContents* old_contents,
53 TabContents* new_contents,
54 int index,
55 bool user_gesture);
56 // End TabStripModelObserver methods
57
58 // Returns the number of tabs in this child.
59 int count() const { return browser_->tabstrip_model()->count(); }
60
61 // Returns the browser that this child gets its data from.
62 Browser* browser() const { return browser_; }
63
64 // Removes all the snapshots and re-populates them from the browser.
65 void RecreateSnapshots();
66
67 // Updates the selected index and tab count on the toplevel window.
68 void UpdateSelectedIndex(int index);
69
70 // Finds the first cell with no snapshot and invokes ConfigureCell
71 // for it. Returns false if there are no more cells to configure on
72 // this listener.
73 bool ConfigureNextUnconfiguredSnapshot();
74
75 // Saves the currently selected tab.
76 void SaveCurrentTab() { original_selected_tab_ = browser_->selected_index(); }
77
78 // Reverts the selected browser tab to the tab that was selected
79 // when This BrowserListener was created, or the last time
80 // SaveCurrentTab was called.
81 void RestoreOriginalSelectedTab();
82
83 // Selects the tab at the given index.
84 void SelectTab(int index);
85
86 // Shows any snapshots that are not visible, and updates their
87 // bitmaps.
88 void ShowSnapshots();
89
90 private:
91 // Returns the tab contents from the tab model for this child at index.
92 TabContents* GetTabContentsAt(int index) const {
93 return browser_->tabstrip_model()->GetTabContentsAt(index);
94 }
95
96 // Configures a cell from the tab contents.
97 void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents);
98
99 // Configures a cell from the model.
100 void ConfigureCell(WmOverviewSnapshot* cell, int index) {
101 ConfigureCell(cell, GetTabContentsAt(index));
102 }
103
104 // Inserts a new snapshot, initialized from the model, at the given
105 // index, and renumbers any following snapshots.
106 void InsertSnapshot(int index);
107
108 // Removes the snapshot at index.
109 void ClearSnapshot(int index);
110
111 // Renumbers the index atom in the snapshots starting at the given
112 // index.
113 void RenumberSnapshots(int start_index);
114
115 Browser* browser_; // Not owned
116 WmOverviewController* controller_; // Not owned
117
118 // Widgets containing snapshot images for this browser.
119 typedef std::vector<WmOverviewSnapshot* > SnapshotVector;
120 SnapshotVector snapshots_;
121
122 // True if the snapshots are showing.
123 bool snapshots_showing_;
124
125 // The tab selected the last time SaveCurrentTab is called.
126 int original_selected_tab_;
127
128 DISALLOW_COPY_AND_ASSIGN(BrowserListener);
129 };
130
131 BrowserListener::BrowserListener(Browser* browser,
132 WmOverviewController* controller)
133 : browser_(browser),
134 controller_(controller),
135 snapshots_showing_(false),
136 original_selected_tab_(-1) {
137 CHECK(browser_);
138 CHECK(controller_);
139 browser_->tabstrip_model()->AddObserver(this);
140 }
141
142 BrowserListener::~BrowserListener() {
143 browser_->tabstrip_model()->RemoveObserver(this);
144 }
145
146 void BrowserListener::TabInsertedAt(TabContents* contents,
147 int index,
148 bool foreground) {
149 InsertSnapshot(index);
150 RenumberSnapshots(index);
151 UpdateSelectedIndex(browser_->selected_index());
152 }
153
154 void BrowserListener::TabDetachedAt(TabContents* contents, int index) {
155 ClearSnapshot(index);
156 UpdateSelectedIndex(browser_->selected_index());
157 RenumberSnapshots(index);
158 }
159
160 void BrowserListener::TabMoved(TabContents* contents,
161 int from_index,
162 int to_index) {
163 // Need to reorder tab in the snapshots list, and reset the window
164 // type atom on the affected snapshots (the one moved, and all the
165 // ones after it), so that their indices are correct.
166 WmOverviewSnapshot* snapshot = snapshots_[from_index];
167 snapshots_.erase(snapshots_.begin() + from_index);
168 snapshots_.insert(snapshots_.begin() + to_index, snapshot);
169
170 RenumberSnapshots(std::min(to_index, from_index));
171 }
172
173 void BrowserListener::TabChangedAt(
174 TabContents* contents,
175 int index,
176 TabStripModelObserver::TabChangeType change_type) {
177 snapshots_[index]->reload_snapshot();
178 controller_->StartDelayTimer();
179 }
180
181 void BrowserListener::TabStripEmpty() {
182 snapshots_.clear();
183 }
184
185 void BrowserListener::TabSelectedAt(TabContents* old_contents,
186 TabContents* new_contents,
187 int index,
188 bool user_gesture) {
189 UpdateSelectedIndex(index);
190 }
191
192 void BrowserListener::RecreateSnapshots() {
193 snapshots_.clear();
194
195 for (int i = 0; i < count(); ++i) {
196 InsertSnapshot(i);
197 }
198
199 RenumberSnapshots(0);
200 }
201
202 void BrowserListener::UpdateSelectedIndex(int index) {
203 // Get the window params and check to make sure that they are
204 // different from what we know before we set them, to avoid extra
205 // notifications.
206 std::vector<int> params;
207 WmIpc::WindowType type = WmIpc::instance()->GetWindowType(
208 GTK_WIDGET(browser_->window()->GetNativeHandle()),
209 &params);
210 DCHECK(type == WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL);
211 if (params.size() > 1) {
212 if (params[0] == browser_->tab_count() &&
213 params[0] == index)
214 return;
215 }
216
217 params.clear();
218 params.push_back(browser_->tab_count());
219 params.push_back(index);
220 WmIpc::instance()->SetWindowType(
221 GTK_WIDGET(browser_->window()->GetNativeHandle()),
222 WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL,
223 &params);
224 }
225
226 bool BrowserListener::ConfigureNextUnconfiguredSnapshot() {
227 for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
228 WmOverviewSnapshot* cell = snapshots_[i];
229 if (!cell->configured_snapshot()) {
230 ConfigureCell(cell, GetTabContentsAt(i));
231 return true;
232 }
233 }
234 return false;
235 }
236
237 void BrowserListener::RestoreOriginalSelectedTab() {
238 if (original_selected_tab_ >= 0) {
239 browser_->SelectTabContentsAt(original_selected_tab_, false);
240 }
241 }
242
243 void BrowserListener::ShowSnapshots() {
244 for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
245 WmOverviewSnapshot* snapshot = snapshots_[i];
246 snapshot->reload_snapshot();
247 if (!snapshot->IsVisible()) {
248 snapshot->Show();
249 }
250 }
251 }
252
253 void BrowserListener::SelectTab(int index) {
254 browser_->SelectTabContentsAt(index, true);
255 }
256
257 void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell,
258 TabContents* contents) {
259 if (contents) {
260 if (controller_->allow_show_snapshots()) {
261 ThumbnailGenerator* generator =
262 g_browser_process->GetThumbnailGenerator();
263 // TODO: Make sure that if the cell gets deleted before the
264 // callback is called that it sticks around until it gets
265 // called. (some kind of "in flight" list that uses linked_ptr
266 // to make sure they don't actually get deleted?) Also, make
267 // sure that any request for a thumbnail eventually returns
268 // (even if it has bogus data), so we don't leak orphaned cells.
269 ThumbnailGenerator::ThumbnailReadyCallback* callback =
270 NewCallback(cell, &WmOverviewSnapshot::SetImage);
271 generator->AskForThumbnail(contents->render_view_host(),
272 callback, cell->size());
273 }
274 } else {
275 // This happens because the contents haven't been loaded yet.
276
277 // Make sure we set the snapshot image to something, otherwise
278 // configured_snapshot remains false and ConfigureNextUnconfiguredSnapshot
279 // would get stuck.
280 if (controller_->allow_show_snapshots()) {
281 cell->SetImage(SkBitmap());
282 }
283 }
284 }
285
286 void BrowserListener::InsertSnapshot(int index) {
287 WmOverviewSnapshot* snapshot = new WmOverviewSnapshot;
288 gfx::Rect bounds =
289 static_cast<BrowserView*>(browser_->window())->GetClientAreaBounds();
290 gfx::Size size(bounds.width()/2, bounds.height()/2);
291 snapshot->Init(size, browser_, index);
292 snapshots_.insert(snapshots_.begin() + index, snapshot);
293 snapshot->reload_snapshot();
294 controller_->StartDelayTimer();
295 }
296
297 // Removes the snapshot at index.
298 void BrowserListener::ClearSnapshot(int index) {
299 snapshots_[index]->CloseNow();
300 snapshots_.erase(snapshots_.begin() + index);
301 }
302
303 void BrowserListener::RenumberSnapshots(int start_index) {
304 int changes = 0;
305 for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) {
306 if (snapshots_[i]->index() != static_cast<int>(i)) {
307 snapshots_[i]->UpdateIndex(browser_, i);
308 changes++;
309 }
310 }
311 }
312
313 ///////////////////////////////////
314 // WmOverviewController methods
315
316 // static
317 WmOverviewController* WmOverviewController::instance() {
318 static WmOverviewController* instance = NULL;
319 if (!instance) {
320 instance = Singleton<WmOverviewController>::get();
321 }
322 return instance;
323 }
324
325 WmOverviewController::WmOverviewController()
326 : allow_show_snapshots_(false) {
327 AddAllBrowsers();
328 BrowserList::AddObserver(this);
329 WmMessageListener::instance()->AddObserver(this);
330 }
331
332 WmOverviewController::~WmOverviewController() {
333 WmMessageListener::instance()->RemoveObserver(this);
334 BrowserList::RemoveObserver(this);
335 listeners_.clear();
336 }
337
338 void WmOverviewController::Observe(NotificationType type,
339 const NotificationSource& source,
340 const NotificationDetails& details) {
341 // Now that the browser window is ready, we create the snapshots.
342 if (type == NotificationType::BROWSER_WINDOW_READY) {
343 const Browser* browser = Source<const Browser>(source).ptr();
344 BrowserListenerVector::value_type new_listener(
345 new BrowserListener(const_cast<Browser*>(browser), this));
346 listeners_.push_back(new_listener);
347 new_listener->RecreateSnapshots();
348
349 // This makes sure that the new listener is in the right order (to
350 // match the order in the browser list).
351 AddAllBrowsers();
352 }
353 }
354
355 // Called immediately before a browser is removed from the list.
356 void WmOverviewController::OnBrowserRemoving(const Browser* browser) {
357 for (BrowserListenerVector::iterator i = listeners_.begin();
358 i != listeners_.end(); ++i) {
359 if ((*i)->browser() == browser) {
360 listeners_.erase(i);
361 return;
362 }
363 }
364 }
365
366 BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) {
367 gpointer data = NULL;
368 gdk_window_get_user_data(gdk_window, &data);
369 GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
370 if (widget) {
371 GtkWindow* gtk_window = GTK_WINDOW(widget);
372 return BrowserView::GetBrowserViewForNativeWindow(gtk_window);
373 } else {
374 return NULL;
375 }
376 }
377
378 void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
379 GdkWindow* window) {
380 switch (message.type()) {
381 case WmIpc::Message::CHROME_NOTIFY_LAYOUT_MODE: {
382 if (message.param(0) == 0 || BrowserList::size() == 0) {
383 Hide(message.param(1) != 0);
384 } else {
385 Show();
386 }
387 break;
388 }
389 case WmIpc::Message::CHROME_NOTIFY_TAB_SELECT: {
390 BrowserView* browser_window = GetBrowserViewForGdkWindow(window);
391 // Find out which listener this goes to, and send it there.
392 for (BrowserListenerVector::iterator i = listeners_.begin();
393 i != listeners_.end(); ++i) {
394 if ((*i)->browser()->window() == browser_window) {
395 (*i)->SelectTab(message.param(0));
396 break;
397 }
398 }
399 break;
400 }
401 default:
402 break;
403 }
404 }
405
406 void WmOverviewController::StartDelayTimer() {
407 // We leave the delay timer running if it already is -- this means
408 // we're rate limiting the number of times we can reconfigure the
409 // snapshots (at most once every 350ms). If we were to restart the
410 // delay timer, it could result in a very long delay until they get
411 // configured if tabs keep changing.
412 if (!delay_timer_.IsRunning()) {
413 configure_timer_.Stop();
414 // Note that this pause is kind of a hack: it's just long enough
415 // that the overview-mode transitions will have finished happening
416 // before we start refreshing snapshots, so we don't bog the CPU
417 // while it's trying to animate the overview transitions.
418 delay_timer_.Start(
419 base::TimeDelta::FromMilliseconds(350), this,
420 &WmOverviewController::StartConfiguring);
421 }
422 }
423
424 void WmOverviewController::RestoreTabSelections() {
425 for (BrowserListenerVector::iterator i = listeners_.begin();
426 i != listeners_.end(); ++i) {
427 (*i)->RestoreOriginalSelectedTab();
428 }
429 }
430
431 void WmOverviewController::SaveTabSelections() {
432 for (BrowserListenerVector::iterator i = listeners_.begin();
433 i != listeners_.end(); ++i) {
434 (*i)->SaveCurrentTab();
435 }
436 }
437
438 void WmOverviewController::Show() {
439 SaveTabSelections();
440 for (BrowserListenerVector::iterator i = listeners_.begin();
441 i != listeners_.end(); ++i) {
442 (*i)->ShowSnapshots();
443 }
444 allow_show_snapshots_ = false;
445 StartDelayTimer();
446 }
447
448 void WmOverviewController::Hide(bool cancelled) {
449 configure_timer_.Stop();
450 delay_timer_.Stop();
451 if (cancelled) {
452 RestoreTabSelections();
453 }
454 }
455
456 void WmOverviewController::StartConfiguring() {
457 allow_show_snapshots_ = true;
458 configure_timer_.Stop();
459 configure_timer_.Start(
460 base::TimeDelta::FromMilliseconds(10), this,
461 &WmOverviewController::ConfigureNextUnconfiguredSnapshot);
462 }
463
464 // Just configure one unconfigured cell. If there aren't any left,
465 // then stop the timer.
466 void WmOverviewController::ConfigureNextUnconfiguredSnapshot() {
467 for (BrowserListenerVector::size_type i = 0; i < listeners_.size(); ++i) {
468 BrowserListener* listener = listeners_[i].get();
469 if (listener->ConfigureNextUnconfiguredSnapshot())
470 return;
471 }
472 configure_timer_.Stop();
473 }
474
475 void WmOverviewController::AddAllBrowsers() {
476 // Make a copy so the old ones aren't deleted yet.
477 BrowserListenerVector old_listeners;
478
479 listeners_.swap(old_listeners);
480
481 // Iterator through the browser list, adding all the browsers in the
482 // new order. If they were in the old list of listeners, just copy
483 // that linked pointer, instead of making a new listener, so that we
484 // can avoid lots of spurious destroy/create messages.
485 BrowserList::const_iterator iterator = BrowserList::begin();
486 while (iterator != BrowserList::end()) {
487 BrowserListenerVector::value_type item(
488 BrowserListenerVector::value_type(NULL));
489 for (BrowserListenerVector::iterator old_iter = old_listeners.begin();
490 old_iter != old_listeners.end(); ++old_iter) {
491 if ((*old_iter)->browser() == *iterator) {
492 item = *old_iter;
493 break;
494 }
495 }
496
497 // This browser isn't owned by any listener, so create it.
498 if (item.get() == NULL) {
499 item = BrowserListenerVector::value_type(
500 new BrowserListener(*iterator, this));
501
502 // This browser didn't already exist, and so we haven't been
503 // watching it for tab insertions, so we need to create the
504 // snapshots associated with it.
505 item->RecreateSnapshots();
506 }
507 listeners_.push_back(item);
508 ++iterator;
509 }
510
511 // Make sure we get notifications for when browser windows are ready.
512 if (registrar_.IsEmpty())
513 registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
514 NotificationService::AllSources());
515 }
516
517 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/wm_overview_controller.h ('k') | chrome/browser/chromeos/wm_overview_snapshot.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698