wm_overview_controller.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/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_fav_icon.h"
14#include "chrome/browser/chromeos/wm_overview_snapshot.h"
15#include "chrome/browser/chromeos/wm_overview_title.h"
16#include "chrome/browser/renderer_host/render_view_host.h"
17#include "chrome/browser/renderer_host/render_widget_host.h"
18#include "chrome/browser/renderer_host/render_widget_host_view.h"
19#include "chrome/browser/tab_contents/tab_contents.h"
20#include "chrome/browser/tab_contents/tab_contents_view.h"
21#include "chrome/browser/tab_contents/thumbnail_generator.h"
22#include "chrome/browser/tabs/tab_strip_model.h"
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
25#include "chrome/browser/ui/views/frame/browser_view.h"
26#include "chrome/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
31using std::vector;
32
33#if !defined(OS_CHROMEOS)
34#error This file is only meant to be compiled for ChromeOS
35#endif
36
37namespace 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.)
43static const int kDelayTimeMs = 10;
44
45// This is the size of the web page when we lay it out for a snapshot.
46static const int kSnapshotWebPageWidth = 1024;
47static const int kSnapshotWebPageHeight = 1280;
48static 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.
53static const double kSnapshotMaxSizeRatio = 0.77;
54
55// This is the height of the title in pixels.
56static const int kTitleHeight = 32;
57
58// The number of additional padding pixels to remove from the title width.
59static const int kFavIconPadding = 5;
60
61class 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);
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();
80  virtual void TabDeselectedAt(TabContentsWrapper* contents, int index) {}
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.
88  int count() const { return browser_->tabstrip_model()->count(); }
89
90  // Returns the browser that this child gets its data from.
91  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.
111  void SaveCurrentTab() { original_selected_tab_ = browser_->selected_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.
128  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.
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* fav_icon;  // 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
184BrowserListener::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
202BrowserListener::~BrowserListener() {
203  browser_->tabstrip_model()->RemoveObserver(this);
204}
205
206void BrowserListener::TabInsertedAt(TabContentsWrapper* contents,
207                                    int index,
208                                    bool foreground) {
209  InsertSnapshot(index);
210  RenumberSnapshots(index);
211  UpdateSelectedIndex(browser_->selected_index());
212}
213
214void BrowserListener::TabDetachedAt(TabContentsWrapper* contents, int index) {
215  ClearSnapshot(index);
216  UpdateSelectedIndex(browser_->selected_index());
217  RenumberSnapshots(index);
218}
219
220void 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_->selected_index());
232}
233
234void 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].fav_icon->SetFavIcon(
242        contents->tab_contents()->GetFavIcon());
243    if (change_type != TabStripModelObserver::TITLE_NOT_LOADING)
244      MarkSnapshotAsDirty(index);
245  }
246}
247
248void BrowserListener::TabStripEmpty() {
249  snapshots_.clear();
250}
251
252void BrowserListener::TabSelectedAt(TabContentsWrapper* old_contents,
253                                    TabContentsWrapper* new_contents,
254                                    int index,
255                                    bool user_gesture) {
256  UpdateSelectedIndex(index);
257}
258
259void BrowserListener::MarkSnapshotAsDirty(int index) {
260  snapshots_[index].snapshot->reload_snapshot();
261  controller_->UpdateSnapshots();
262}
263
264void BrowserListener::RecreateSnapshots() {
265  snapshots_.clear();
266
267  for (int i = 0; i < count(); ++i)
268    InsertSnapshot(i);
269
270  RenumberSnapshots(0);
271}
272
273void BrowserListener::UpdateSelectedIndex(int index) {
274  WmIpcWindowType type = WmIpc::instance()->GetWindowType(
275      GTK_WIDGET(browser_->window()->GetNativeHandle()), NULL);
276  // Make sure we only operate on toplevel windows.
277  if (type == WM_IPC_WINDOW_CHROME_TOPLEVEL) {
278    std::vector<int> params;
279    params.push_back(browser_->tab_count());
280    params.push_back(index);
281    params.push_back(select_tab_timestamp_ ? select_tab_timestamp_ :
282                     gtk_get_current_event_time());
283    WmIpc::instance()->SetWindowType(
284        GTK_WIDGET(browser_->window()->GetNativeHandle()),
285        WM_IPC_WINDOW_CHROME_TOPLEVEL,
286        &params);
287  }
288}
289
290int BrowserListener::ConfigureNextUnconfiguredSnapshot(int start_from) {
291  for (SnapshotVector::size_type i = start_from + 1;
292      i < snapshots_.size(); ++i) {
293    WmOverviewSnapshot* cell = snapshots_[i].snapshot;
294    if (!cell->configured_snapshot()) {
295      ConfigureCell(cell, i);
296      return i;
297    }
298  }
299  return -1;
300}
301
302void BrowserListener::RestoreOriginalSelectedTab() {
303  if (original_selected_tab_ >= 0) {
304    browser_->SelectTabContentsAt(original_selected_tab_, false);
305    UpdateSelectedIndex(browser_->selected_index());
306  }
307}
308
309void BrowserListener::ShowSnapshots() {
310  for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
311    const SnapshotNode& node = snapshots_[i];
312    if (!node.snapshot->IsVisible())
313      node.snapshot->Show();
314    if (!snapshots_[i].title->IsVisible())
315      node.title->Show();
316    if (!snapshots_[i].fav_icon->IsVisible())
317      node.fav_icon->Show();
318  }
319}
320
321void BrowserListener::SelectTab(int index, uint32 timestamp) {
322  // Ignore requests to switch to non-existent tabs (the window manager gets
323  // notified asynchronously about the number of tabs in each window, so there's
324  // no guarantee that the messages that it sends us will make sense).
325  if (index < 0 || index >= browser_->tab_count())
326    return;
327
328  uint32 old_value = select_tab_timestamp_;
329  select_tab_timestamp_ = timestamp;
330  browser_->SelectTabContentsAt(index, true);
331  select_tab_timestamp_ = old_value;
332}
333
334gfx::Size BrowserListener::CalculateCellSize() {
335  // Make the page size and the cell size a fixed size for overview
336  // mode.  The cell size is calculated based on the desired maximum
337  // size on the screen, so it's related to the width and height of
338  // the browser client area.  In this way, when this snapshot gets
339  // to the window manager, it will already have the correct size,
340  // and will be scaled by 1.0, meaning that it won't be resampled
341  // and will not be blurry.
342  gfx::Rect bounds = static_cast<BrowserView*>(browser_->window())->
343                     GetClientAreaBounds();
344  const gfx::Size max_size = gfx::Size(
345      bounds.width() * kSnapshotMaxSizeRatio,
346      bounds.height() * kSnapshotMaxSizeRatio);
347  const double max_size_ratio = static_cast<double>(max_size.width()) /
348                                max_size.height();
349  gfx::Size cell_size;
350  if (kSnapshotWebPageRatio > max_size_ratio) {
351    const double scale_factor =
352        static_cast<double>(max_size.width())/ kSnapshotWebPageWidth;
353    cell_size = gfx::Size(max_size.width(),
354                          kSnapshotWebPageHeight * scale_factor + 0.5);
355  } else {
356    const double scale_factor =
357        static_cast<double>(max_size.height())/ kSnapshotWebPageHeight;
358    cell_size = gfx::Size(kSnapshotWebPageWidth * scale_factor + 0.5,
359                          max_size.height());
360  }
361  return cell_size;
362}
363
364void BrowserListener::OnSnapshotReady(const SkBitmap& sk_bitmap) {
365  for (int i = 0; i < count(); i++) {
366    RenderWidgetHostView* view =
367        GetTabContentsAt(i)->GetRenderWidgetHostView();
368    if (view && view->GetRenderWidgetHost() == current_renderer_host_) {
369      snapshots_[i].snapshot->SetImage(sk_bitmap);
370      current_renderer_host_ = NULL;
371
372      // Start timer for next round of snapshot updating.
373      controller_->StartDelayTimer();
374      return;
375    }
376  }
377  DCHECK(current_renderer_host_ == NULL);
378}
379
380void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell,
381                                    TabContents* contents) {
382  if (contents) {
383    ThumbnailGenerator* generator =
384        g_browser_process->GetThumbnailGenerator();
385    // TODO: Make sure that if the cell gets deleted before the
386    // callback is called that it sticks around until it gets
387    // called.  (some kind of "in flight" list that uses linked_ptr
388    // to make sure they don't actually get deleted?)  Also, make
389    // sure that any request for a thumbnail eventually returns
390    // (even if it has bogus data), so we don't leak orphaned cells,
391    // which could happen if a tab is closed while it is being
392    // rendered.
393    ThumbnailGenerator::ThumbnailReadyCallback* callback =
394        NewCallback(this, &BrowserListener::OnSnapshotReady);
395
396    current_renderer_host_ = contents->render_view_host();
397    generator->AskForSnapshot(contents->render_view_host(),
398                              false,
399                              callback,
400                              gfx::Size(kSnapshotWebPageWidth,
401                                        kSnapshotWebPageHeight),
402                              CalculateCellSize());
403  } else {
404    // This happens because the contents haven't been loaded yet.
405
406    // Make sure we set the snapshot image to something, otherwise
407    // configured_snapshot remains false and
408    // ConfigureNextUnconfiguredSnapshot would get stuck.
409    current_renderer_host_ = NULL;
410    cell->SetImage(SkBitmap());
411    cell->reload_snapshot();
412    controller_->StartDelayTimer();
413  }
414}
415
416void BrowserListener::InsertSnapshot(int index) {
417  SnapshotNode node;
418  node.snapshot = new WmOverviewSnapshot;
419  gfx::Size cell_size = CalculateCellSize();
420  node.snapshot->Init(cell_size, browser_, index);
421
422  node.fav_icon = new WmOverviewFavIcon;
423  node.fav_icon->Init(node.snapshot);
424  node.fav_icon->SetFavIcon(browser_->GetTabContentsAt(index)->GetFavIcon());
425
426  node.title = new WmOverviewTitle;
427  node.title->Init(gfx::Size(cell_size.width() -
428                             WmOverviewFavIcon::kIconSize - kFavIconPadding,
429                             kTitleHeight), node.snapshot);
430  node.title->SetTitle(browser_->GetTabContentsAt(index)->GetTitle());
431
432  snapshots_.insert(snapshots_.begin() + index, node);
433  node.snapshot->reload_snapshot();
434  controller_->UpdateSnapshots();
435}
436
437// Removes the snapshot at index.
438void BrowserListener::ClearSnapshot(int index) {
439  snapshots_[index].snapshot->CloseNow();
440  snapshots_[index].title->CloseNow();
441  snapshots_[index].fav_icon->CloseNow();
442  snapshots_.erase(snapshots_.begin() + index);
443}
444
445void BrowserListener::RenumberSnapshots(int start_index) {
446  for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) {
447    if (snapshots_[i].snapshot->index() != static_cast<int>(i))
448      snapshots_[i].snapshot->UpdateIndex(browser_, i);
449  }
450}
451
452///////////////////////////////////
453// WmOverviewController methods
454
455// static
456WmOverviewController* WmOverviewController::GetInstance() {
457  static WmOverviewController* instance = NULL;
458  if (!instance) {
459    instance = Singleton<WmOverviewController>::get();
460  }
461  return instance;
462}
463
464WmOverviewController::WmOverviewController()
465    : layout_mode_(ACTIVE_MODE),
466      updating_snapshots_(false),
467      browser_listener_index_(0),
468      tab_contents_index_(-1) {
469  AddAllBrowsers();
470
471  if (registrar_.IsEmpty()) {
472    // Make sure we get notifications for when the tab contents are
473    // connected, so we know when a new browser has been created.
474    registrar_.Add(this,
475                   NotificationType::TAB_CONTENTS_CONNECTED,
476                   NotificationService::AllSources());
477
478    // Ask for notification when the snapshot source image has changed
479    // and needs to be refreshed.
480    registrar_.Add(this,
481                   NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED,
482                   NotificationService::AllSources());
483  }
484
485  BrowserList::AddObserver(this);
486  WmMessageListener::GetInstance()->AddObserver(this);
487}
488
489WmOverviewController::~WmOverviewController() {
490  WmMessageListener::GetInstance()->RemoveObserver(this);
491  BrowserList::RemoveObserver(this);
492  listeners_.clear();
493}
494
495void WmOverviewController::Observe(NotificationType type,
496                                   const NotificationSource& source,
497                                   const NotificationDetails& details) {
498  switch (type.value) {
499    // Now that the tab contents are ready, we create the listeners
500    // and snapshots for any new browsers out there.  This actually
501    // results in us traversing the list of browsers more often than
502    // necessary (whenever a tab is connected, as opposed to only when
503    // a new browser is created), but other notifications aren't
504    // sufficient to know when the first tab of a new browser has its
505    // dimensions set.  The implementation of AddAllBrowsers avoids
506    // doing anything to already-existing browsers, so it's not a huge
507    // problem, but still, it would be nice if there were a more
508    // appropriate (browser-level) notification.
509    case NotificationType::TAB_CONTENTS_CONNECTED:
510      AddAllBrowsers();
511      break;
512
513    case NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED: {
514      // It's OK to do this in active mode too -- nothing will happen
515      // except invalidation of the snapshot, because the delay timer
516      // won't start until we're in overview mode.
517      RenderWidgetHost* renderer = Details<RenderViewHost>(details).ptr();
518      SnapshotImageChanged(renderer);
519      break;
520    }
521    default:
522      // Do nothing.
523      break;
524  }
525}
526
527void WmOverviewController::SnapshotImageChanged(RenderWidgetHost* renderer) {
528  // Find out which TabContents this renderer is attached to, and then
529  // invalidate the associated snapshot so it'll update.
530  BrowserListenerVector::iterator iter = listeners_.begin();
531  while (iter != listeners_.end()) {
532    for (int i = 0; i < (*iter)->count(); i++) {
533      RenderWidgetHostView* view =
534          (*iter)->GetTabContentsAt(i)->GetRenderWidgetHostView();
535      if (view && view->GetRenderWidgetHost() == renderer) {
536        (*iter)->MarkSnapshotAsDirty(i);
537        return;
538      }
539    }
540    ++iter;
541  }
542  DLOG(ERROR) << "SnapshotImageChanged, but we do not know which it is?";
543}
544
545void WmOverviewController::OnBrowserRemoved(const Browser* browser) {
546  for (BrowserListenerVector::iterator i = listeners_.begin();
547       i != listeners_.end(); ++i) {
548    if ((*i)->browser() == browser) {
549      listeners_.erase(i);
550      return;
551    }
552  }
553}
554
555BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) {
556  gpointer data = NULL;
557  gdk_window_get_user_data(gdk_window, &data);
558  GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
559  if (widget) {
560    GtkWindow* gtk_window = GTK_WINDOW(widget);
561    return BrowserView::GetBrowserViewForNativeWindow(gtk_window);
562  } else {
563    return NULL;
564  }
565}
566
567void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
568                                            GdkWindow* window) {
569  switch (message.type()) {
570    case WM_IPC_MESSAGE_CHROME_NOTIFY_LAYOUT_MODE: {
571      layout_mode_ = message.param(0) == 0 ? ACTIVE_MODE : OVERVIEW_MODE;
572      if (layout_mode_ == ACTIVE_MODE || BrowserList::size() == 0) {
573        Hide(message.param(1) != 0);
574      } else {
575        Show();
576      }
577      break;
578    }
579    case WM_IPC_MESSAGE_CHROME_NOTIFY_TAB_SELECT: {
580      BrowserView* browser_window = GetBrowserViewForGdkWindow(window);
581      // Find out which listener this goes to, and send it there.
582      for (BrowserListenerVector::iterator i = listeners_.begin();
583           i != listeners_.end(); ++i) {
584        if ((*i)->browser()->window() == browser_window) {
585          // param(0): index of the tab to select.
586          // param(1): timestamp of the event.
587          (*i)->SelectTab(message.param(0), message.param(1));
588          break;
589        }
590      }
591      break;
592    }
593    default:
594      break;
595  }
596}
597
598void WmOverviewController::StartDelayTimer() {
599  // We're rate limiting the number of times we can reconfigure the
600  // snapshots.  If we were to restart the delay timer, it could
601  // result in a very long delay until they get configured if tabs
602  // keep changing.
603  updating_snapshots_ = false;
604  if (layout_mode_ == OVERVIEW_MODE) {
605    delay_timer_.Start(
606        base::TimeDelta::FromMilliseconds(kDelayTimeMs), this,
607        &WmOverviewController::UpdateSnapshots);
608  }
609}
610
611void WmOverviewController::RestoreTabSelections() {
612  for (BrowserListenerVector::iterator i = listeners_.begin();
613       i != listeners_.end(); ++i) {
614    (*i)->RestoreOriginalSelectedTab();
615  }
616}
617
618void WmOverviewController::SaveTabSelections() {
619  for (BrowserListenerVector::iterator i = listeners_.begin();
620       i != listeners_.end(); ++i) {
621    (*i)->SaveCurrentTab();
622  }
623}
624
625void WmOverviewController::Show() {
626  SaveTabSelections();
627
628  for (BrowserListenerVector::iterator i = listeners_.begin();
629       i != listeners_.end(); ++i) {
630    (*i)->ShowSnapshots();
631  }
632
633  // TODO(jiesun): Make the focused tab as the start point.
634  browser_listener_index_ = 0;
635  tab_contents_index_ = -1;
636  UpdateSnapshots();
637}
638
639void WmOverviewController::Hide(bool cancelled) {
640  delay_timer_.Stop();
641  updating_snapshots_ = false;
642  if (cancelled) {
643    RestoreTabSelections();
644  }
645}
646
647void WmOverviewController::UpdateSnapshots() {
648
649  // Only updating snapshots during overview mode.
650  if (layout_mode_ != OVERVIEW_MODE)
651    return;
652
653  // Only reloading snapshots when not already started.
654  if (updating_snapshots_ || delay_timer_.IsRunning())
655    return;
656
657  // Only update one snapshot in round-robin mode.
658  // Start delay timer after each update unless all snapshots had been updated.
659  if (!listeners_.size())
660    return;
661
662  if (int(listeners_.size()) <= browser_listener_index_) {
663    DLOG(INFO) << "Browser listener(s) have disappeared since last update";
664    browser_listener_index_ = 0;
665    tab_contents_index_ = -1;
666  } else {
667    BrowserListener* listener = listeners_[browser_listener_index_].get();
668    if (listener->count() <= tab_contents_index_) {
669      DLOG(INFO) << "Tab content(s) have disappeared since last update";
670      tab_contents_index_ = -1;
671    }
672  }
673
674  int browser_listener_index = browser_listener_index_;
675  int tab_contents_index = tab_contents_index_;
676
677  bool loop_back = false;
678  while (1) {
679    BrowserListener* listener = listeners_[browser_listener_index].get();
680    tab_contents_index =
681        listener->ConfigureNextUnconfiguredSnapshot(tab_contents_index);
682    if (tab_contents_index >= 0) {
683      updating_snapshots_ = true;  // Prevent future parallel updates.
684      browser_listener_index_ = browser_listener_index;
685      tab_contents_index_ = tab_contents_index;
686      return;
687    }
688
689    // Found next one;
690    browser_listener_index++;
691    browser_listener_index = browser_listener_index % listeners_.size();
692    tab_contents_index = -1;
693
694    if (loop_back)
695      break;
696    loop_back = browser_listener_index == browser_listener_index_;
697  }
698
699  // All snapshots have been fully updated.
700  updating_snapshots_ = false;
701}
702
703void WmOverviewController::AddAllBrowsers() {
704  // Make a copy so the old ones aren't deleted yet.
705  BrowserListenerVector old_listeners;
706
707  listeners_.swap(old_listeners);
708
709  // Iterator through the browser list, adding all the browsers in the
710  // new order.  If they were in the old list of listeners, just copy
711  // that linked pointer, instead of making a new listener, so that we
712  // can avoid lots of spurious destroy/create messages.
713  BrowserList::const_iterator iterator = BrowserList::begin();
714  while (iterator != BrowserList::end()) {
715    // Don't add a browser to the list if that type of browser doesn't
716    // have snapshots in overview mode.
717    if ((*iterator)->type() != Browser::TYPE_NORMAL &&
718        (*iterator)->type() != Browser::TYPE_APP) {
719      ++iterator;
720      continue;
721    }
722
723    BrowserListenerVector::value_type item(
724        BrowserListenerVector::value_type(NULL));
725    for (BrowserListenerVector::iterator old_iter = old_listeners.begin();
726         old_iter != old_listeners.end(); ++old_iter) {
727      if ((*old_iter)->browser() == *iterator) {
728        item = *old_iter;
729        break;
730      }
731    }
732
733    // This browser isn't tracked by any listener, so create it.
734    if (item.get() == NULL) {
735      item = BrowserListenerVector::value_type(
736          new BrowserListener(*iterator, this));
737    }
738    listeners_.push_back(item);
739    ++iterator;
740  }
741}
742
743}  // namespace chromeos
744