1 // Copyright (c) 2011 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/memory/linked_ptr.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/chromeos/wm_ipc.h"
13 #include "chrome/browser/chromeos/wm_overview_favicon.h"
14 #include "chrome/browser/chromeos/wm_overview_snapshot.h"
15 #include "chrome/browser/chromeos/wm_overview_title.h"
16 #include "chrome/browser/tab_contents/thumbnail_generator.h"
17 #include "chrome/browser/tabs/tab_strip_model.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
20 #include "chrome/browser/ui/views/frame/browser_view.h"
21 #include "content/browser/renderer_host/render_view_host.h"
22 #include "content/browser/renderer_host/render_widget_host.h"
23 #include "content/browser/renderer_host/render_widget_host_view.h"
24 #include "content/browser/tab_contents/tab_contents.h"
25 #include "content/browser/tab_contents/tab_contents_view.h"
26 #include "content/common/notification_service.h"
27 #include "views/widget/root_view.h"
28 #include "views/widget/widget_gtk.h"
29 #include "views/window/window.h"
30
31 using std::vector;
32
33 #if !defined(OS_CHROMEOS)
34 #error This file is only meant to be compiled for ChromeOS
35 #endif
36
37 namespace chromeos {
38
39 // Use this timer to delay consecutive snapshots during the updating process.
40 // We will start the timer upon successfully retrieving a new snapshot, or if
41 // for some reason the current snapshot is still pending (while Chrome is
42 // still loading the page.)
43 static const int kDelayTimeMs = 10;
44
45 // This is the size of the web page when we lay it out for a snapshot.
46 static const int kSnapshotWebPageWidth = 1024;
47 static const int kSnapshotWebPageHeight = 1280;
48 static const double kSnapshotWebPageRatio =
49 static_cast<double>(kSnapshotWebPageWidth) / kSnapshotWebPageHeight;
50
51 // This is the maximum percentage of the original browser client area
52 // that a snapshot can take up.
53 static const double kSnapshotMaxSizeRatio = 0.77;
54
55 // This is the height of the title in pixels.
56 static const int kTitleHeight = 32;
57
58 // The number of additional padding pixels to remove from the title width.
59 static const int kFaviconPadding = 5;
60
61 class BrowserListener : public TabStripModelObserver {
62 public:
63 BrowserListener(Browser* browser, WmOverviewController* parent);
64 ~BrowserListener();
65
66 // Begin TabStripModelObserver methods
67 virtual void TabInsertedAt(TabContentsWrapper* contents,
68 int index,
69 bool foreground);
TabClosingAt(TabStripModel * tab_strip_model,TabContentsWrapper * contents,int index)70 virtual void TabClosingAt(TabStripModel* tab_strip_model,
71 TabContentsWrapper* contents,
72 int index) {}
73 virtual void TabDetachedAt(TabContentsWrapper* contents, int index);
74 virtual void TabMoved(TabContentsWrapper* contents,
75 int from_index,
76 int to_index);
77 virtual void TabChangedAt(TabContentsWrapper* contents, int index,
78 TabStripModelObserver::TabChangeType change_type);
79 virtual void TabStripEmpty();
TabDeselected(TabContentsWrapper * contents)80 virtual void TabDeselected(TabContentsWrapper* contents) {}
81 virtual void TabSelectedAt(TabContentsWrapper* old_contents,
82 TabContentsWrapper* new_contents,
83 int index,
84 bool user_gesture);
85 // End TabStripModelObserver methods
86
87 // Returns the number of tabs in this child.
count() const88 int count() const { return browser_->tabstrip_model()->count(); }
89
90 // Returns the browser that this child gets its data from.
browser() const91 Browser* browser() const { return browser_; }
92
93 // Removes all the snapshots and re-populates them from the browser.
94 void RecreateSnapshots();
95
96 // Mark the given snapshot as dirty, and start the delay timer.
97 void MarkSnapshotAsDirty(int index);
98
99 // Updates the selected index and tab count on the toplevel window.
100 void UpdateSelectedIndex(int index);
101
102 // Update the first "dirty" snapshot, which is ordered after (excluding)
103 // the snapshot whose index is given by |start_from|; When |start_from| is
104 // -1, search start at the begining of the list.
105 // Return the index of the snapshot which is actually updated; -1 if there
106 // are no more tab contents (after |start_from|) to configure on this
107 // listener.
108 int ConfigureNextUnconfiguredSnapshot(int start_from);
109
110 // Saves the currently selected tab.
SaveCurrentTab()111 void SaveCurrentTab() { original_selected_tab_ = browser_->active_index(); }
112
113 // Reverts the selected browser tab to the tab that was selected
114 // when This BrowserListener was created, or the last time
115 // SaveCurrentTab was called.
116 void RestoreOriginalSelectedTab();
117
118 // Selects the tab at the given index.
119 void SelectTab(int index, uint32 timestamp);
120
121 // Shows any snapshots that are not visible.
122 void ShowSnapshots();
123
124 // Callback for |AskForSnapshot|, start delay timer for next round.
125 void OnSnapshotReady(const SkBitmap& sk_bitmap);
126
127 // Returns the tab contents from the tab model for this child at index.
GetTabContentsAt(int index) const128 TabContents* GetTabContentsAt(int index) const {
129 return browser_->tabstrip_model()->GetTabContentsAt(index)->tab_contents();
130 }
131
132 private:
133 // Calculate the size of a cell based on the browser window's size.
134 gfx::Size CalculateCellSize();
135
136 // Configures a cell from the tab contents.
137 void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents);
138
139 // Configures a cell from the model.
ConfigureCell(WmOverviewSnapshot * cell,int index)140 void ConfigureCell(WmOverviewSnapshot* cell, int index) {
141 ConfigureCell(cell, GetTabContentsAt(index));
142 }
143
144 // Inserts a new snapshot, initialized from the model, at the given
145 // index, and renumbers any following snapshots.
146 void InsertSnapshot(int index);
147
148 // Removes the snapshot at index.
149 void ClearSnapshot(int index);
150
151 // Renumbers the index atom in the snapshots starting at the given
152 // index.
153 void RenumberSnapshots(int start_index);
154
155 Browser* browser_; // Not owned
156 WmOverviewController* controller_; // Not owned
157
158 // Which renderer host we are working on.
159 RenderWidgetHost* current_renderer_host_; // Not owned
160
161 // Widgets containing snapshot images for this browser. Note that
162 // these are all subclasses of WidgetGtk, and they are all added to
163 // parents, so they will be deleted by the parents when they are
164 // closed.
165 struct SnapshotNode {
166 WmOverviewSnapshot* snapshot; // Not owned
167 WmOverviewTitle* title; // Not owned
168 WmOverviewFavicon* favicon; // Not owned
169 };
170 typedef std::vector<SnapshotNode> SnapshotVector;
171 SnapshotVector snapshots_;
172
173 // Non-zero if we are currently setting the tab from within SelectTab.
174 // This is used to make sure we use the right timestamp when sending
175 // property changes that originated from the window manager.
176 uint32 select_tab_timestamp_;
177
178 // The tab selected the last time SaveCurrentTab is called.
179 int original_selected_tab_;
180
181 DISALLOW_COPY_AND_ASSIGN(BrowserListener);
182 };
183
BrowserListener(Browser * browser,WmOverviewController * controller)184 BrowserListener::BrowserListener(Browser* browser,
185 WmOverviewController* controller)
186 : browser_(browser),
187 controller_(controller),
188 current_renderer_host_(NULL),
189 select_tab_timestamp_(0),
190 original_selected_tab_(-1) {
191 CHECK(browser_);
192 CHECK(controller_);
193
194 browser_->tabstrip_model()->AddObserver(this);
195
196 // This browser didn't already exist, and so we haven't been
197 // watching it for tab insertions, so we need to create the
198 // snapshots associated with it.
199 RecreateSnapshots();
200 }
201
~BrowserListener()202 BrowserListener::~BrowserListener() {
203 browser_->tabstrip_model()->RemoveObserver(this);
204 }
205
TabInsertedAt(TabContentsWrapper * contents,int index,bool foreground)206 void BrowserListener::TabInsertedAt(TabContentsWrapper* contents,
207 int index,
208 bool foreground) {
209 InsertSnapshot(index);
210 RenumberSnapshots(index);
211 UpdateSelectedIndex(browser_->active_index());
212 }
213
TabDetachedAt(TabContentsWrapper * contents,int index)214 void BrowserListener::TabDetachedAt(TabContentsWrapper* contents, int index) {
215 ClearSnapshot(index);
216 UpdateSelectedIndex(browser_->active_index());
217 RenumberSnapshots(index);
218 }
219
TabMoved(TabContentsWrapper * contents,int from_index,int to_index)220 void BrowserListener::TabMoved(TabContentsWrapper* contents,
221 int from_index,
222 int to_index) {
223 // Need to reorder tab in the snapshots list, and reset the window
224 // type atom on the affected snapshots (the one moved, and all the
225 // ones after it), so that their indices are correct.
226 SnapshotNode node = snapshots_[from_index];
227 snapshots_.erase(snapshots_.begin() + from_index);
228 snapshots_.insert(snapshots_.begin() + to_index, node);
229
230 RenumberSnapshots(std::min(to_index, from_index));
231 UpdateSelectedIndex(browser_->active_index());
232 }
233
TabChangedAt(TabContentsWrapper * contents,int index,TabStripModelObserver::TabChangeType change_type)234 void BrowserListener::TabChangedAt(
235 TabContentsWrapper* contents,
236 int index,
237 TabStripModelObserver::TabChangeType change_type) {
238 if (change_type != TabStripModelObserver::LOADING_ONLY) {
239 snapshots_[index].title->SetTitle(contents->tab_contents()->GetTitle());
240 snapshots_[index].title->SetUrl(contents->tab_contents()->GetURL());
241 snapshots_[index].favicon->SetFavicon(
242 contents->tab_contents()->GetFavicon());
243 if (change_type != TabStripModelObserver::TITLE_NOT_LOADING)
244 MarkSnapshotAsDirty(index);
245 }
246 }
247
TabStripEmpty()248 void BrowserListener::TabStripEmpty() {
249 snapshots_.clear();
250 }
251
TabSelectedAt(TabContentsWrapper * old_contents,TabContentsWrapper * new_contents,int index,bool user_gesture)252 void BrowserListener::TabSelectedAt(TabContentsWrapper* old_contents,
253 TabContentsWrapper* new_contents,
254 int index,
255 bool user_gesture) {
256 if (old_contents == new_contents)
257 return;
258
259 UpdateSelectedIndex(index);
260 }
261
MarkSnapshotAsDirty(int index)262 void BrowserListener::MarkSnapshotAsDirty(int index) {
263 snapshots_[index].snapshot->reload_snapshot();
264 controller_->UpdateSnapshots();
265 }
266
RecreateSnapshots()267 void BrowserListener::RecreateSnapshots() {
268 snapshots_.clear();
269
270 for (int i = 0; i < count(); ++i)
271 InsertSnapshot(i);
272
273 RenumberSnapshots(0);
274 }
275
UpdateSelectedIndex(int index)276 void BrowserListener::UpdateSelectedIndex(int index) {
277 WmIpcWindowType type = WmIpc::instance()->GetWindowType(
278 GTK_WIDGET(browser_->window()->GetNativeHandle()), NULL);
279 // Make sure we only operate on toplevel windows.
280 if (type == WM_IPC_WINDOW_CHROME_TOPLEVEL) {
281 std::vector<int> params;
282 params.push_back(browser_->tab_count());
283 params.push_back(index);
284 params.push_back(select_tab_timestamp_ ? select_tab_timestamp_ :
285 gtk_get_current_event_time());
286 WmIpc::instance()->SetWindowType(
287 GTK_WIDGET(browser_->window()->GetNativeHandle()),
288 WM_IPC_WINDOW_CHROME_TOPLEVEL,
289 ¶ms);
290 }
291 }
292
ConfigureNextUnconfiguredSnapshot(int start_from)293 int BrowserListener::ConfigureNextUnconfiguredSnapshot(int start_from) {
294 for (SnapshotVector::size_type i = start_from + 1;
295 i < snapshots_.size(); ++i) {
296 WmOverviewSnapshot* cell = snapshots_[i].snapshot;
297 if (!cell->configured_snapshot()) {
298 ConfigureCell(cell, i);
299 return i;
300 }
301 }
302 return -1;
303 }
304
RestoreOriginalSelectedTab()305 void BrowserListener::RestoreOriginalSelectedTab() {
306 if (original_selected_tab_ >= 0) {
307 browser_->ActivateTabAt(original_selected_tab_, false);
308 UpdateSelectedIndex(browser_->active_index());
309 }
310 }
311
ShowSnapshots()312 void BrowserListener::ShowSnapshots() {
313 for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
314 const SnapshotNode& node = snapshots_[i];
315 if (!node.snapshot->IsVisible())
316 node.snapshot->Show();
317 if (!snapshots_[i].title->IsVisible())
318 node.title->Show();
319 if (!snapshots_[i].favicon->IsVisible())
320 node.favicon->Show();
321 }
322 }
323
SelectTab(int index,uint32 timestamp)324 void BrowserListener::SelectTab(int index, uint32 timestamp) {
325 // Ignore requests to switch to non-existent tabs (the window manager gets
326 // notified asynchronously about the number of tabs in each window, so there's
327 // no guarantee that the messages that it sends us will make sense).
328 if (index < 0 || index >= browser_->tab_count())
329 return;
330
331 uint32 old_value = select_tab_timestamp_;
332 select_tab_timestamp_ = timestamp;
333 browser_->ActivateTabAt(index, true);
334 select_tab_timestamp_ = old_value;
335 }
336
CalculateCellSize()337 gfx::Size BrowserListener::CalculateCellSize() {
338 // Make the page size and the cell size a fixed size for overview
339 // mode. The cell size is calculated based on the desired maximum
340 // size on the screen, so it's related to the width and height of
341 // the browser client area. In this way, when this snapshot gets
342 // to the window manager, it will already have the correct size,
343 // and will be scaled by 1.0, meaning that it won't be resampled
344 // and will not be blurry.
345 gfx::Rect bounds = static_cast<BrowserView*>(browser_->window())->
346 GetClientAreaBounds();
347 const gfx::Size max_size = gfx::Size(
348 bounds.width() * kSnapshotMaxSizeRatio,
349 bounds.height() * kSnapshotMaxSizeRatio);
350 const double max_size_ratio = static_cast<double>(max_size.width()) /
351 max_size.height();
352 gfx::Size cell_size;
353 if (kSnapshotWebPageRatio > max_size_ratio) {
354 const double scale_factor =
355 static_cast<double>(max_size.width())/ kSnapshotWebPageWidth;
356 cell_size = gfx::Size(max_size.width(),
357 kSnapshotWebPageHeight * scale_factor + 0.5);
358 } else {
359 const double scale_factor =
360 static_cast<double>(max_size.height())/ kSnapshotWebPageHeight;
361 cell_size = gfx::Size(kSnapshotWebPageWidth * scale_factor + 0.5,
362 max_size.height());
363 }
364 return cell_size;
365 }
366
OnSnapshotReady(const SkBitmap & sk_bitmap)367 void BrowserListener::OnSnapshotReady(const SkBitmap& sk_bitmap) {
368 for (int i = 0; i < count(); i++) {
369 RenderWidgetHostView* view =
370 GetTabContentsAt(i)->GetRenderWidgetHostView();
371 if (view && view->GetRenderWidgetHost() == current_renderer_host_) {
372 snapshots_[i].snapshot->SetImage(sk_bitmap);
373 current_renderer_host_ = NULL;
374
375 // Start timer for next round of snapshot updating.
376 controller_->StartDelayTimer();
377 return;
378 }
379 }
380 DCHECK(current_renderer_host_ == NULL);
381 }
382
ConfigureCell(WmOverviewSnapshot * cell,TabContents * contents)383 void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell,
384 TabContents* contents) {
385 if (contents) {
386 ThumbnailGenerator* generator =
387 g_browser_process->GetThumbnailGenerator();
388 // TODO: Make sure that if the cell gets deleted before the
389 // callback is called that it sticks around until it gets
390 // called. (some kind of "in flight" list that uses linked_ptr
391 // to make sure they don't actually get deleted?) Also, make
392 // sure that any request for a thumbnail eventually returns
393 // (even if it has bogus data), so we don't leak orphaned cells,
394 // which could happen if a tab is closed while it is being
395 // rendered.
396 ThumbnailGenerator::ThumbnailReadyCallback* callback =
397 NewCallback(this, &BrowserListener::OnSnapshotReady);
398
399 current_renderer_host_ = contents->render_view_host();
400 generator->AskForSnapshot(contents->render_view_host(),
401 false,
402 callback,
403 gfx::Size(kSnapshotWebPageWidth,
404 kSnapshotWebPageHeight),
405 CalculateCellSize());
406 } else {
407 // This happens because the contents haven't been loaded yet.
408
409 // Make sure we set the snapshot image to something, otherwise
410 // configured_snapshot remains false and
411 // ConfigureNextUnconfiguredSnapshot would get stuck.
412 current_renderer_host_ = NULL;
413 cell->SetImage(SkBitmap());
414 cell->reload_snapshot();
415 controller_->StartDelayTimer();
416 }
417 }
418
InsertSnapshot(int index)419 void BrowserListener::InsertSnapshot(int index) {
420 SnapshotNode node;
421 node.snapshot = new WmOverviewSnapshot;
422 gfx::Size cell_size = CalculateCellSize();
423 node.snapshot->Init(cell_size, browser_, index);
424
425 node.favicon = new WmOverviewFavicon;
426 node.favicon->Init(node.snapshot);
427 node.favicon->SetFavicon(browser_->GetTabContentsAt(index)->GetFavicon());
428
429 node.title = new WmOverviewTitle;
430 node.title->Init(gfx::Size(std::max(0, cell_size.width() -
431 WmOverviewFavicon::kIconSize -
432 kFaviconPadding),
433 kTitleHeight), node.snapshot);
434 node.title->SetTitle(browser_->GetTabContentsAt(index)->GetTitle());
435
436 snapshots_.insert(snapshots_.begin() + index, node);
437 node.snapshot->reload_snapshot();
438 controller_->UpdateSnapshots();
439 }
440
441 // Removes the snapshot at index.
ClearSnapshot(int index)442 void BrowserListener::ClearSnapshot(int index) {
443 snapshots_[index].snapshot->CloseNow();
444 snapshots_[index].title->CloseNow();
445 snapshots_[index].favicon->CloseNow();
446 snapshots_.erase(snapshots_.begin() + index);
447 }
448
RenumberSnapshots(int start_index)449 void BrowserListener::RenumberSnapshots(int start_index) {
450 for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) {
451 if (snapshots_[i].snapshot->index() != static_cast<int>(i))
452 snapshots_[i].snapshot->UpdateIndex(browser_, i);
453 }
454 }
455
456 ///////////////////////////////////
457 // WmOverviewController methods
458
459 // static
GetInstance()460 WmOverviewController* WmOverviewController::GetInstance() {
461 static WmOverviewController* instance = NULL;
462 if (!instance) {
463 instance = Singleton<WmOverviewController>::get();
464 }
465 return instance;
466 }
467
WmOverviewController()468 WmOverviewController::WmOverviewController()
469 : layout_mode_(ACTIVE_MODE),
470 updating_snapshots_(false),
471 browser_listener_index_(0),
472 tab_contents_index_(-1) {
473 AddAllBrowsers();
474
475 if (registrar_.IsEmpty()) {
476 // Make sure we get notifications for when the tab contents are
477 // connected, so we know when a new browser has been created.
478 registrar_.Add(this,
479 NotificationType::TAB_CONTENTS_CONNECTED,
480 NotificationService::AllSources());
481
482 // Ask for notification when the snapshot source image has changed
483 // and needs to be refreshed.
484 registrar_.Add(this,
485 NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED,
486 NotificationService::AllSources());
487 }
488
489 BrowserList::AddObserver(this);
490 WmMessageListener::GetInstance()->AddObserver(this);
491 }
492
~WmOverviewController()493 WmOverviewController::~WmOverviewController() {
494 WmMessageListener::GetInstance()->RemoveObserver(this);
495 BrowserList::RemoveObserver(this);
496 listeners_.clear();
497 }
498
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)499 void WmOverviewController::Observe(NotificationType type,
500 const NotificationSource& source,
501 const NotificationDetails& details) {
502 switch (type.value) {
503 // Now that the tab contents are ready, we create the listeners
504 // and snapshots for any new browsers out there. This actually
505 // results in us traversing the list of browsers more often than
506 // necessary (whenever a tab is connected, as opposed to only when
507 // a new browser is created), but other notifications aren't
508 // sufficient to know when the first tab of a new browser has its
509 // dimensions set. The implementation of AddAllBrowsers avoids
510 // doing anything to already-existing browsers, so it's not a huge
511 // problem, but still, it would be nice if there were a more
512 // appropriate (browser-level) notification.
513 case NotificationType::TAB_CONTENTS_CONNECTED:
514 AddAllBrowsers();
515 break;
516
517 case NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED: {
518 // It's OK to do this in active mode too -- nothing will happen
519 // except invalidation of the snapshot, because the delay timer
520 // won't start until we're in overview mode.
521 RenderWidgetHost* renderer = Details<RenderViewHost>(details).ptr();
522 SnapshotImageChanged(renderer);
523 break;
524 }
525 default:
526 // Do nothing.
527 break;
528 }
529 }
530
SnapshotImageChanged(RenderWidgetHost * renderer)531 void WmOverviewController::SnapshotImageChanged(RenderWidgetHost* renderer) {
532 // Find out which TabContents this renderer is attached to, and then
533 // invalidate the associated snapshot so it'll update.
534 BrowserListenerVector::iterator iter = listeners_.begin();
535 while (iter != listeners_.end()) {
536 for (int i = 0; i < (*iter)->count(); i++) {
537 RenderWidgetHostView* view =
538 (*iter)->GetTabContentsAt(i)->GetRenderWidgetHostView();
539 if (view && view->GetRenderWidgetHost() == renderer) {
540 (*iter)->MarkSnapshotAsDirty(i);
541 return;
542 }
543 }
544 ++iter;
545 }
546 DLOG(ERROR) << "SnapshotImageChanged, but we do not know which it is?";
547 }
548
OnBrowserRemoved(const Browser * browser)549 void WmOverviewController::OnBrowserRemoved(const Browser* browser) {
550 for (BrowserListenerVector::iterator i = listeners_.begin();
551 i != listeners_.end(); ++i) {
552 if ((*i)->browser() == browser) {
553 listeners_.erase(i);
554 return;
555 }
556 }
557 }
558
GetBrowserViewForGdkWindow(GdkWindow * gdk_window)559 BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) {
560 gpointer data = NULL;
561 gdk_window_get_user_data(gdk_window, &data);
562 GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
563 if (widget) {
564 GtkWindow* gtk_window = GTK_WINDOW(widget);
565 return BrowserView::GetBrowserViewForNativeWindow(gtk_window);
566 } else {
567 return NULL;
568 }
569 }
570
ProcessWmMessage(const WmIpc::Message & message,GdkWindow * window)571 void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
572 GdkWindow* window) {
573 switch (message.type()) {
574 case WM_IPC_MESSAGE_CHROME_NOTIFY_LAYOUT_MODE: {
575 layout_mode_ = message.param(0) == 0 ? ACTIVE_MODE : OVERVIEW_MODE;
576 if (layout_mode_ == ACTIVE_MODE || BrowserList::size() == 0) {
577 Hide(message.param(1) != 0);
578 } else {
579 Show();
580 }
581 break;
582 }
583 case WM_IPC_MESSAGE_CHROME_NOTIFY_TAB_SELECT: {
584 BrowserView* browser_window = GetBrowserViewForGdkWindow(window);
585 // Find out which listener this goes to, and send it there.
586 for (BrowserListenerVector::iterator i = listeners_.begin();
587 i != listeners_.end(); ++i) {
588 if ((*i)->browser()->window() == browser_window) {
589 // param(0): index of the tab to select.
590 // param(1): timestamp of the event.
591 (*i)->SelectTab(message.param(0), message.param(1));
592 break;
593 }
594 }
595 break;
596 }
597 default:
598 break;
599 }
600 }
601
StartDelayTimer()602 void WmOverviewController::StartDelayTimer() {
603 // We're rate limiting the number of times we can reconfigure the
604 // snapshots. If we were to restart the delay timer, it could
605 // result in a very long delay until they get configured if tabs
606 // keep changing.
607 updating_snapshots_ = false;
608 if (layout_mode_ == OVERVIEW_MODE) {
609 delay_timer_.Start(
610 base::TimeDelta::FromMilliseconds(kDelayTimeMs), this,
611 &WmOverviewController::UpdateSnapshots);
612 }
613 }
614
RestoreTabSelections()615 void WmOverviewController::RestoreTabSelections() {
616 for (BrowserListenerVector::iterator i = listeners_.begin();
617 i != listeners_.end(); ++i) {
618 (*i)->RestoreOriginalSelectedTab();
619 }
620 }
621
SaveTabSelections()622 void WmOverviewController::SaveTabSelections() {
623 for (BrowserListenerVector::iterator i = listeners_.begin();
624 i != listeners_.end(); ++i) {
625 (*i)->SaveCurrentTab();
626 }
627 }
628
Show()629 void WmOverviewController::Show() {
630 SaveTabSelections();
631
632 for (BrowserListenerVector::iterator i = listeners_.begin();
633 i != listeners_.end(); ++i) {
634 (*i)->ShowSnapshots();
635 }
636
637 // TODO(jiesun): Make the focused tab as the start point.
638 browser_listener_index_ = 0;
639 tab_contents_index_ = -1;
640 UpdateSnapshots();
641 }
642
Hide(bool cancelled)643 void WmOverviewController::Hide(bool cancelled) {
644 delay_timer_.Stop();
645 updating_snapshots_ = false;
646 if (cancelled) {
647 RestoreTabSelections();
648 }
649 }
650
UpdateSnapshots()651 void WmOverviewController::UpdateSnapshots() {
652
653 // Only updating snapshots during overview mode.
654 if (layout_mode_ != OVERVIEW_MODE)
655 return;
656
657 // Only reloading snapshots when not already started.
658 if (updating_snapshots_ || delay_timer_.IsRunning())
659 return;
660
661 // Only update one snapshot in round-robin mode.
662 // Start delay timer after each update unless all snapshots had been updated.
663 if (!listeners_.size())
664 return;
665
666 if (int(listeners_.size()) <= browser_listener_index_) {
667 DLOG(INFO) << "Browser listener(s) have disappeared since last update";
668 browser_listener_index_ = 0;
669 tab_contents_index_ = -1;
670 } else {
671 BrowserListener* listener = listeners_[browser_listener_index_].get();
672 if (listener->count() <= tab_contents_index_) {
673 DLOG(INFO) << "Tab content(s) have disappeared since last update";
674 tab_contents_index_ = -1;
675 }
676 }
677
678 int browser_listener_index = browser_listener_index_;
679 int tab_contents_index = tab_contents_index_;
680
681 bool loop_back = false;
682 while (1) {
683 BrowserListener* listener = listeners_[browser_listener_index].get();
684 tab_contents_index =
685 listener->ConfigureNextUnconfiguredSnapshot(tab_contents_index);
686 if (tab_contents_index >= 0) {
687 updating_snapshots_ = true; // Prevent future parallel updates.
688 browser_listener_index_ = browser_listener_index;
689 tab_contents_index_ = tab_contents_index;
690 return;
691 }
692
693 // Found next one;
694 browser_listener_index++;
695 browser_listener_index = browser_listener_index % listeners_.size();
696 tab_contents_index = -1;
697
698 if (loop_back)
699 break;
700 loop_back = browser_listener_index == browser_listener_index_;
701 }
702
703 // All snapshots have been fully updated.
704 updating_snapshots_ = false;
705 }
706
AddAllBrowsers()707 void WmOverviewController::AddAllBrowsers() {
708 // Make a copy so the old ones aren't deleted yet.
709 BrowserListenerVector old_listeners;
710
711 listeners_.swap(old_listeners);
712
713 // Iterator through the browser list, adding all the browsers in the
714 // new order. If they were in the old list of listeners, just copy
715 // that linked pointer, instead of making a new listener, so that we
716 // can avoid lots of spurious destroy/create messages.
717 BrowserList::const_iterator iterator = BrowserList::begin();
718 while (iterator != BrowserList::end()) {
719 // Don't add a browser to the list if that type of browser doesn't
720 // have snapshots in overview mode.
721 if ((*iterator)->type() != Browser::TYPE_NORMAL &&
722 (*iterator)->type() != Browser::TYPE_APP) {
723 ++iterator;
724 continue;
725 }
726
727 BrowserListenerVector::value_type item(
728 BrowserListenerVector::value_type(NULL));
729 for (BrowserListenerVector::iterator old_iter = old_listeners.begin();
730 old_iter != old_listeners.end(); ++old_iter) {
731 if ((*old_iter)->browser() == *iterator) {
732 item = *old_iter;
733 break;
734 }
735 }
736
737 // This browser isn't tracked by any listener, so create it.
738 if (item.get() == NULL) {
739 item = BrowserListenerVector::value_type(
740 new BrowserListener(*iterator, this));
741 }
742 listeners_.push_back(item);
743 ++iterator;
744 }
745 }
746
747 } // namespace chromeos
748