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
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 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.
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_->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.
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* 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
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_->active_index());
212}
213
214void BrowserListener::TabDetachedAt(TabContentsWrapper* contents, int index) {
215  ClearSnapshot(index);
216  UpdateSelectedIndex(browser_->active_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_->active_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].favicon->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  if (old_contents == new_contents)
257    return;
258
259  UpdateSelectedIndex(index);
260}
261
262void BrowserListener::MarkSnapshotAsDirty(int index) {
263  snapshots_[index].snapshot->reload_snapshot();
264  controller_->UpdateSnapshots();
265}
266
267void BrowserListener::RecreateSnapshots() {
268  snapshots_.clear();
269
270  for (int i = 0; i < count(); ++i)
271    InsertSnapshot(i);
272
273  RenumberSnapshots(0);
274}
275
276void 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        &params);
290  }
291}
292
293int 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
305void BrowserListener::RestoreOriginalSelectedTab() {
306  if (original_selected_tab_ >= 0) {
307    browser_->ActivateTabAt(original_selected_tab_, false);
308    UpdateSelectedIndex(browser_->active_index());
309  }
310}
311
312void 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
324void 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
337gfx::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
367void 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
383void 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
419void 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.
442void 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
449void 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
460WmOverviewController* WmOverviewController::GetInstance() {
461  static WmOverviewController* instance = NULL;
462  if (!instance) {
463    instance = Singleton<WmOverviewController>::get();
464  }
465  return instance;
466}
467
468WmOverviewController::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
493WmOverviewController::~WmOverviewController() {
494  WmMessageListener::GetInstance()->RemoveObserver(this);
495  BrowserList::RemoveObserver(this);
496  listeners_.clear();
497}
498
499void 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
531void 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
549void 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
559BrowserView* 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
571void 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
602void 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
615void WmOverviewController::RestoreTabSelections() {
616  for (BrowserListenerVector::iterator i = listeners_.begin();
617       i != listeners_.end(); ++i) {
618    (*i)->RestoreOriginalSelectedTab();
619  }
620}
621
622void WmOverviewController::SaveTabSelections() {
623  for (BrowserListenerVector::iterator i = listeners_.begin();
624       i != listeners_.end(); ++i) {
625    (*i)->SaveCurrentTab();
626  }
627}
628
629void 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
643void WmOverviewController::Hide(bool cancelled) {
644  delay_timer_.Stop();
645  updating_snapshots_ = false;
646  if (cancelled) {
647    RestoreTabSelections();
648  }
649}
650
651void 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
707void 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