| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/wm_overview_controller.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/memory/linked_ptr.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chromeos/wm_ipc.h" |
| #include "chrome/browser/chromeos/wm_overview_favicon.h" |
| #include "chrome/browser/chromeos/wm_overview_snapshot.h" |
| #include "chrome/browser/chromeos/wm_overview_title.h" |
| #include "chrome/browser/tab_contents/thumbnail_generator.h" |
| #include "chrome/browser/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/renderer_host/render_widget_host.h" |
| #include "content/browser/renderer_host/render_widget_host_view.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/browser/tab_contents/tab_contents_view.h" |
| #include "content/common/notification_service.h" |
| #include "views/widget/root_view.h" |
| #include "views/widget/widget_gtk.h" |
| #include "views/window/window.h" |
| |
| using std::vector; |
| |
| #if !defined(OS_CHROMEOS) |
| #error This file is only meant to be compiled for ChromeOS |
| #endif |
| |
| namespace chromeos { |
| |
| // Use this timer to delay consecutive snapshots during the updating process. |
| // We will start the timer upon successfully retrieving a new snapshot, or if |
| // for some reason the current snapshot is still pending (while Chrome is |
| // still loading the page.) |
| static const int kDelayTimeMs = 10; |
| |
| // This is the size of the web page when we lay it out for a snapshot. |
| static const int kSnapshotWebPageWidth = 1024; |
| static const int kSnapshotWebPageHeight = 1280; |
| static const double kSnapshotWebPageRatio = |
| static_cast<double>(kSnapshotWebPageWidth) / kSnapshotWebPageHeight; |
| |
| // This is the maximum percentage of the original browser client area |
| // that a snapshot can take up. |
| static const double kSnapshotMaxSizeRatio = 0.77; |
| |
| // This is the height of the title in pixels. |
| static const int kTitleHeight = 32; |
| |
| // The number of additional padding pixels to remove from the title width. |
| static const int kFaviconPadding = 5; |
| |
| class BrowserListener : public TabStripModelObserver { |
| public: |
| BrowserListener(Browser* browser, WmOverviewController* parent); |
| ~BrowserListener(); |
| |
| // Begin TabStripModelObserver methods |
| virtual void TabInsertedAt(TabContentsWrapper* contents, |
| int index, |
| bool foreground); |
| virtual void TabClosingAt(TabStripModel* tab_strip_model, |
| TabContentsWrapper* contents, |
| int index) {} |
| virtual void TabDetachedAt(TabContentsWrapper* contents, int index); |
| virtual void TabMoved(TabContentsWrapper* contents, |
| int from_index, |
| int to_index); |
| virtual void TabChangedAt(TabContentsWrapper* contents, int index, |
| TabStripModelObserver::TabChangeType change_type); |
| virtual void TabStripEmpty(); |
| virtual void TabDeselected(TabContentsWrapper* contents) {} |
| virtual void TabSelectedAt(TabContentsWrapper* old_contents, |
| TabContentsWrapper* new_contents, |
| int index, |
| bool user_gesture); |
| // End TabStripModelObserver methods |
| |
| // Returns the number of tabs in this child. |
| int count() const { return browser_->tabstrip_model()->count(); } |
| |
| // Returns the browser that this child gets its data from. |
| Browser* browser() const { return browser_; } |
| |
| // Removes all the snapshots and re-populates them from the browser. |
| void RecreateSnapshots(); |
| |
| // Mark the given snapshot as dirty, and start the delay timer. |
| void MarkSnapshotAsDirty(int index); |
| |
| // Updates the selected index and tab count on the toplevel window. |
| void UpdateSelectedIndex(int index); |
| |
| // Update the first "dirty" snapshot, which is ordered after (excluding) |
| // the snapshot whose index is given by |start_from|; When |start_from| is |
| // -1, search start at the begining of the list. |
| // Return the index of the snapshot which is actually updated; -1 if there |
| // are no more tab contents (after |start_from|) to configure on this |
| // listener. |
| int ConfigureNextUnconfiguredSnapshot(int start_from); |
| |
| // Saves the currently selected tab. |
| void SaveCurrentTab() { original_selected_tab_ = browser_->active_index(); } |
| |
| // Reverts the selected browser tab to the tab that was selected |
| // when This BrowserListener was created, or the last time |
| // SaveCurrentTab was called. |
| void RestoreOriginalSelectedTab(); |
| |
| // Selects the tab at the given index. |
| void SelectTab(int index, uint32 timestamp); |
| |
| // Shows any snapshots that are not visible. |
| void ShowSnapshots(); |
| |
| // Callback for |AskForSnapshot|, start delay timer for next round. |
| void OnSnapshotReady(const SkBitmap& sk_bitmap); |
| |
| // Returns the tab contents from the tab model for this child at index. |
| TabContents* GetTabContentsAt(int index) const { |
| return browser_->tabstrip_model()->GetTabContentsAt(index)->tab_contents(); |
| } |
| |
| private: |
| // Calculate the size of a cell based on the browser window's size. |
| gfx::Size CalculateCellSize(); |
| |
| // Configures a cell from the tab contents. |
| void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents); |
| |
| // Configures a cell from the model. |
| void ConfigureCell(WmOverviewSnapshot* cell, int index) { |
| ConfigureCell(cell, GetTabContentsAt(index)); |
| } |
| |
| // Inserts a new snapshot, initialized from the model, at the given |
| // index, and renumbers any following snapshots. |
| void InsertSnapshot(int index); |
| |
| // Removes the snapshot at index. |
| void ClearSnapshot(int index); |
| |
| // Renumbers the index atom in the snapshots starting at the given |
| // index. |
| void RenumberSnapshots(int start_index); |
| |
| Browser* browser_; // Not owned |
| WmOverviewController* controller_; // Not owned |
| |
| // Which renderer host we are working on. |
| RenderWidgetHost* current_renderer_host_; // Not owned |
| |
| // Widgets containing snapshot images for this browser. Note that |
| // these are all subclasses of WidgetGtk, and they are all added to |
| // parents, so they will be deleted by the parents when they are |
| // closed. |
| struct SnapshotNode { |
| WmOverviewSnapshot* snapshot; // Not owned |
| WmOverviewTitle* title; // Not owned |
| WmOverviewFavicon* favicon; // Not owned |
| }; |
| typedef std::vector<SnapshotNode> SnapshotVector; |
| SnapshotVector snapshots_; |
| |
| // Non-zero if we are currently setting the tab from within SelectTab. |
| // This is used to make sure we use the right timestamp when sending |
| // property changes that originated from the window manager. |
| uint32 select_tab_timestamp_; |
| |
| // The tab selected the last time SaveCurrentTab is called. |
| int original_selected_tab_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BrowserListener); |
| }; |
| |
| BrowserListener::BrowserListener(Browser* browser, |
| WmOverviewController* controller) |
| : browser_(browser), |
| controller_(controller), |
| current_renderer_host_(NULL), |
| select_tab_timestamp_(0), |
| original_selected_tab_(-1) { |
| CHECK(browser_); |
| CHECK(controller_); |
| |
| browser_->tabstrip_model()->AddObserver(this); |
| |
| // This browser didn't already exist, and so we haven't been |
| // watching it for tab insertions, so we need to create the |
| // snapshots associated with it. |
| RecreateSnapshots(); |
| } |
| |
| BrowserListener::~BrowserListener() { |
| browser_->tabstrip_model()->RemoveObserver(this); |
| } |
| |
| void BrowserListener::TabInsertedAt(TabContentsWrapper* contents, |
| int index, |
| bool foreground) { |
| InsertSnapshot(index); |
| RenumberSnapshots(index); |
| UpdateSelectedIndex(browser_->active_index()); |
| } |
| |
| void BrowserListener::TabDetachedAt(TabContentsWrapper* contents, int index) { |
| ClearSnapshot(index); |
| UpdateSelectedIndex(browser_->active_index()); |
| RenumberSnapshots(index); |
| } |
| |
| void BrowserListener::TabMoved(TabContentsWrapper* contents, |
| int from_index, |
| int to_index) { |
| // Need to reorder tab in the snapshots list, and reset the window |
| // type atom on the affected snapshots (the one moved, and all the |
| // ones after it), so that their indices are correct. |
| SnapshotNode node = snapshots_[from_index]; |
| snapshots_.erase(snapshots_.begin() + from_index); |
| snapshots_.insert(snapshots_.begin() + to_index, node); |
| |
| RenumberSnapshots(std::min(to_index, from_index)); |
| UpdateSelectedIndex(browser_->active_index()); |
| } |
| |
| void BrowserListener::TabChangedAt( |
| TabContentsWrapper* contents, |
| int index, |
| TabStripModelObserver::TabChangeType change_type) { |
| if (change_type != TabStripModelObserver::LOADING_ONLY) { |
| snapshots_[index].title->SetTitle(contents->tab_contents()->GetTitle()); |
| snapshots_[index].title->SetUrl(contents->tab_contents()->GetURL()); |
| snapshots_[index].favicon->SetFavicon( |
| contents->tab_contents()->GetFavicon()); |
| if (change_type != TabStripModelObserver::TITLE_NOT_LOADING) |
| MarkSnapshotAsDirty(index); |
| } |
| } |
| |
| void BrowserListener::TabStripEmpty() { |
| snapshots_.clear(); |
| } |
| |
| void BrowserListener::TabSelectedAt(TabContentsWrapper* old_contents, |
| TabContentsWrapper* new_contents, |
| int index, |
| bool user_gesture) { |
| if (old_contents == new_contents) |
| return; |
| |
| UpdateSelectedIndex(index); |
| } |
| |
| void BrowserListener::MarkSnapshotAsDirty(int index) { |
| snapshots_[index].snapshot->reload_snapshot(); |
| controller_->UpdateSnapshots(); |
| } |
| |
| void BrowserListener::RecreateSnapshots() { |
| snapshots_.clear(); |
| |
| for (int i = 0; i < count(); ++i) |
| InsertSnapshot(i); |
| |
| RenumberSnapshots(0); |
| } |
| |
| void BrowserListener::UpdateSelectedIndex(int index) { |
| WmIpcWindowType type = WmIpc::instance()->GetWindowType( |
| GTK_WIDGET(browser_->window()->GetNativeHandle()), NULL); |
| // Make sure we only operate on toplevel windows. |
| if (type == WM_IPC_WINDOW_CHROME_TOPLEVEL) { |
| std::vector<int> params; |
| params.push_back(browser_->tab_count()); |
| params.push_back(index); |
| params.push_back(select_tab_timestamp_ ? select_tab_timestamp_ : |
| gtk_get_current_event_time()); |
| WmIpc::instance()->SetWindowType( |
| GTK_WIDGET(browser_->window()->GetNativeHandle()), |
| WM_IPC_WINDOW_CHROME_TOPLEVEL, |
| ¶ms); |
| } |
| } |
| |
| int BrowserListener::ConfigureNextUnconfiguredSnapshot(int start_from) { |
| for (SnapshotVector::size_type i = start_from + 1; |
| i < snapshots_.size(); ++i) { |
| WmOverviewSnapshot* cell = snapshots_[i].snapshot; |
| if (!cell->configured_snapshot()) { |
| ConfigureCell(cell, i); |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| void BrowserListener::RestoreOriginalSelectedTab() { |
| if (original_selected_tab_ >= 0) { |
| browser_->ActivateTabAt(original_selected_tab_, false); |
| UpdateSelectedIndex(browser_->active_index()); |
| } |
| } |
| |
| void BrowserListener::ShowSnapshots() { |
| for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) { |
| const SnapshotNode& node = snapshots_[i]; |
| if (!node.snapshot->IsVisible()) |
| node.snapshot->Show(); |
| if (!snapshots_[i].title->IsVisible()) |
| node.title->Show(); |
| if (!snapshots_[i].favicon->IsVisible()) |
| node.favicon->Show(); |
| } |
| } |
| |
| void BrowserListener::SelectTab(int index, uint32 timestamp) { |
| // Ignore requests to switch to non-existent tabs (the window manager gets |
| // notified asynchronously about the number of tabs in each window, so there's |
| // no guarantee that the messages that it sends us will make sense). |
| if (index < 0 || index >= browser_->tab_count()) |
| return; |
| |
| uint32 old_value = select_tab_timestamp_; |
| select_tab_timestamp_ = timestamp; |
| browser_->ActivateTabAt(index, true); |
| select_tab_timestamp_ = old_value; |
| } |
| |
| gfx::Size BrowserListener::CalculateCellSize() { |
| // Make the page size and the cell size a fixed size for overview |
| // mode. The cell size is calculated based on the desired maximum |
| // size on the screen, so it's related to the width and height of |
| // the browser client area. In this way, when this snapshot gets |
| // to the window manager, it will already have the correct size, |
| // and will be scaled by 1.0, meaning that it won't be resampled |
| // and will not be blurry. |
| gfx::Rect bounds = static_cast<BrowserView*>(browser_->window())-> |
| GetClientAreaBounds(); |
| const gfx::Size max_size = gfx::Size( |
| bounds.width() * kSnapshotMaxSizeRatio, |
| bounds.height() * kSnapshotMaxSizeRatio); |
| const double max_size_ratio = static_cast<double>(max_size.width()) / |
| max_size.height(); |
| gfx::Size cell_size; |
| if (kSnapshotWebPageRatio > max_size_ratio) { |
| const double scale_factor = |
| static_cast<double>(max_size.width())/ kSnapshotWebPageWidth; |
| cell_size = gfx::Size(max_size.width(), |
| kSnapshotWebPageHeight * scale_factor + 0.5); |
| } else { |
| const double scale_factor = |
| static_cast<double>(max_size.height())/ kSnapshotWebPageHeight; |
| cell_size = gfx::Size(kSnapshotWebPageWidth * scale_factor + 0.5, |
| max_size.height()); |
| } |
| return cell_size; |
| } |
| |
| void BrowserListener::OnSnapshotReady(const SkBitmap& sk_bitmap) { |
| for (int i = 0; i < count(); i++) { |
| RenderWidgetHostView* view = |
| GetTabContentsAt(i)->GetRenderWidgetHostView(); |
| if (view && view->GetRenderWidgetHost() == current_renderer_host_) { |
| snapshots_[i].snapshot->SetImage(sk_bitmap); |
| current_renderer_host_ = NULL; |
| |
| // Start timer for next round of snapshot updating. |
| controller_->StartDelayTimer(); |
| return; |
| } |
| } |
| DCHECK(current_renderer_host_ == NULL); |
| } |
| |
| void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell, |
| TabContents* contents) { |
| if (contents) { |
| ThumbnailGenerator* generator = |
| g_browser_process->GetThumbnailGenerator(); |
| // TODO: Make sure that if the cell gets deleted before the |
| // callback is called that it sticks around until it gets |
| // called. (some kind of "in flight" list that uses linked_ptr |
| // to make sure they don't actually get deleted?) Also, make |
| // sure that any request for a thumbnail eventually returns |
| // (even if it has bogus data), so we don't leak orphaned cells, |
| // which could happen if a tab is closed while it is being |
| // rendered. |
| ThumbnailGenerator::ThumbnailReadyCallback* callback = |
| NewCallback(this, &BrowserListener::OnSnapshotReady); |
| |
| current_renderer_host_ = contents->render_view_host(); |
| generator->AskForSnapshot(contents->render_view_host(), |
| false, |
| callback, |
| gfx::Size(kSnapshotWebPageWidth, |
| kSnapshotWebPageHeight), |
| CalculateCellSize()); |
| } else { |
| // This happens because the contents haven't been loaded yet. |
| |
| // Make sure we set the snapshot image to something, otherwise |
| // configured_snapshot remains false and |
| // ConfigureNextUnconfiguredSnapshot would get stuck. |
| current_renderer_host_ = NULL; |
| cell->SetImage(SkBitmap()); |
| cell->reload_snapshot(); |
| controller_->StartDelayTimer(); |
| } |
| } |
| |
| void BrowserListener::InsertSnapshot(int index) { |
| SnapshotNode node; |
| node.snapshot = new WmOverviewSnapshot; |
| gfx::Size cell_size = CalculateCellSize(); |
| node.snapshot->Init(cell_size, browser_, index); |
| |
| node.favicon = new WmOverviewFavicon; |
| node.favicon->Init(node.snapshot); |
| node.favicon->SetFavicon(browser_->GetTabContentsAt(index)->GetFavicon()); |
| |
| node.title = new WmOverviewTitle; |
| node.title->Init(gfx::Size(std::max(0, cell_size.width() - |
| WmOverviewFavicon::kIconSize - |
| kFaviconPadding), |
| kTitleHeight), node.snapshot); |
| node.title->SetTitle(browser_->GetTabContentsAt(index)->GetTitle()); |
| |
| snapshots_.insert(snapshots_.begin() + index, node); |
| node.snapshot->reload_snapshot(); |
| controller_->UpdateSnapshots(); |
| } |
| |
| // Removes the snapshot at index. |
| void BrowserListener::ClearSnapshot(int index) { |
| snapshots_[index].snapshot->CloseNow(); |
| snapshots_[index].title->CloseNow(); |
| snapshots_[index].favicon->CloseNow(); |
| snapshots_.erase(snapshots_.begin() + index); |
| } |
| |
| void BrowserListener::RenumberSnapshots(int start_index) { |
| for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) { |
| if (snapshots_[i].snapshot->index() != static_cast<int>(i)) |
| snapshots_[i].snapshot->UpdateIndex(browser_, i); |
| } |
| } |
| |
| /////////////////////////////////// |
| // WmOverviewController methods |
| |
| // static |
| WmOverviewController* WmOverviewController::GetInstance() { |
| static WmOverviewController* instance = NULL; |
| if (!instance) { |
| instance = Singleton<WmOverviewController>::get(); |
| } |
| return instance; |
| } |
| |
| WmOverviewController::WmOverviewController() |
| : layout_mode_(ACTIVE_MODE), |
| updating_snapshots_(false), |
| browser_listener_index_(0), |
| tab_contents_index_(-1) { |
| AddAllBrowsers(); |
| |
| if (registrar_.IsEmpty()) { |
| // Make sure we get notifications for when the tab contents are |
| // connected, so we know when a new browser has been created. |
| registrar_.Add(this, |
| NotificationType::TAB_CONTENTS_CONNECTED, |
| NotificationService::AllSources()); |
| |
| // Ask for notification when the snapshot source image has changed |
| // and needs to be refreshed. |
| registrar_.Add(this, |
| NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED, |
| NotificationService::AllSources()); |
| } |
| |
| BrowserList::AddObserver(this); |
| WmMessageListener::GetInstance()->AddObserver(this); |
| } |
| |
| WmOverviewController::~WmOverviewController() { |
| WmMessageListener::GetInstance()->RemoveObserver(this); |
| BrowserList::RemoveObserver(this); |
| listeners_.clear(); |
| } |
| |
| void WmOverviewController::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type.value) { |
| // Now that the tab contents are ready, we create the listeners |
| // and snapshots for any new browsers out there. This actually |
| // results in us traversing the list of browsers more often than |
| // necessary (whenever a tab is connected, as opposed to only when |
| // a new browser is created), but other notifications aren't |
| // sufficient to know when the first tab of a new browser has its |
| // dimensions set. The implementation of AddAllBrowsers avoids |
| // doing anything to already-existing browsers, so it's not a huge |
| // problem, but still, it would be nice if there were a more |
| // appropriate (browser-level) notification. |
| case NotificationType::TAB_CONTENTS_CONNECTED: |
| AddAllBrowsers(); |
| break; |
| |
| case NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED: { |
| // It's OK to do this in active mode too -- nothing will happen |
| // except invalidation of the snapshot, because the delay timer |
| // won't start until we're in overview mode. |
| RenderWidgetHost* renderer = Details<RenderViewHost>(details).ptr(); |
| SnapshotImageChanged(renderer); |
| break; |
| } |
| default: |
| // Do nothing. |
| break; |
| } |
| } |
| |
| void WmOverviewController::SnapshotImageChanged(RenderWidgetHost* renderer) { |
| // Find out which TabContents this renderer is attached to, and then |
| // invalidate the associated snapshot so it'll update. |
| BrowserListenerVector::iterator iter = listeners_.begin(); |
| while (iter != listeners_.end()) { |
| for (int i = 0; i < (*iter)->count(); i++) { |
| RenderWidgetHostView* view = |
| (*iter)->GetTabContentsAt(i)->GetRenderWidgetHostView(); |
| if (view && view->GetRenderWidgetHost() == renderer) { |
| (*iter)->MarkSnapshotAsDirty(i); |
| return; |
| } |
| } |
| ++iter; |
| } |
| DLOG(ERROR) << "SnapshotImageChanged, but we do not know which it is?"; |
| } |
| |
| void WmOverviewController::OnBrowserRemoved(const Browser* browser) { |
| for (BrowserListenerVector::iterator i = listeners_.begin(); |
| i != listeners_.end(); ++i) { |
| if ((*i)->browser() == browser) { |
| listeners_.erase(i); |
| return; |
| } |
| } |
| } |
| |
| BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) { |
| gpointer data = NULL; |
| gdk_window_get_user_data(gdk_window, &data); |
| GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); |
| if (widget) { |
| GtkWindow* gtk_window = GTK_WINDOW(widget); |
| return BrowserView::GetBrowserViewForNativeWindow(gtk_window); |
| } else { |
| return NULL; |
| } |
| } |
| |
| void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message, |
| GdkWindow* window) { |
| switch (message.type()) { |
| case WM_IPC_MESSAGE_CHROME_NOTIFY_LAYOUT_MODE: { |
| layout_mode_ = message.param(0) == 0 ? ACTIVE_MODE : OVERVIEW_MODE; |
| if (layout_mode_ == ACTIVE_MODE || BrowserList::size() == 0) { |
| Hide(message.param(1) != 0); |
| } else { |
| Show(); |
| } |
| break; |
| } |
| case WM_IPC_MESSAGE_CHROME_NOTIFY_TAB_SELECT: { |
| BrowserView* browser_window = GetBrowserViewForGdkWindow(window); |
| // Find out which listener this goes to, and send it there. |
| for (BrowserListenerVector::iterator i = listeners_.begin(); |
| i != listeners_.end(); ++i) { |
| if ((*i)->browser()->window() == browser_window) { |
| // param(0): index of the tab to select. |
| // param(1): timestamp of the event. |
| (*i)->SelectTab(message.param(0), message.param(1)); |
| break; |
| } |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| void WmOverviewController::StartDelayTimer() { |
| // We're rate limiting the number of times we can reconfigure the |
| // snapshots. If we were to restart the delay timer, it could |
| // result in a very long delay until they get configured if tabs |
| // keep changing. |
| updating_snapshots_ = false; |
| if (layout_mode_ == OVERVIEW_MODE) { |
| delay_timer_.Start( |
| base::TimeDelta::FromMilliseconds(kDelayTimeMs), this, |
| &WmOverviewController::UpdateSnapshots); |
| } |
| } |
| |
| void WmOverviewController::RestoreTabSelections() { |
| for (BrowserListenerVector::iterator i = listeners_.begin(); |
| i != listeners_.end(); ++i) { |
| (*i)->RestoreOriginalSelectedTab(); |
| } |
| } |
| |
| void WmOverviewController::SaveTabSelections() { |
| for (BrowserListenerVector::iterator i = listeners_.begin(); |
| i != listeners_.end(); ++i) { |
| (*i)->SaveCurrentTab(); |
| } |
| } |
| |
| void WmOverviewController::Show() { |
| SaveTabSelections(); |
| |
| for (BrowserListenerVector::iterator i = listeners_.begin(); |
| i != listeners_.end(); ++i) { |
| (*i)->ShowSnapshots(); |
| } |
| |
| // TODO(jiesun): Make the focused tab as the start point. |
| browser_listener_index_ = 0; |
| tab_contents_index_ = -1; |
| UpdateSnapshots(); |
| } |
| |
| void WmOverviewController::Hide(bool cancelled) { |
| delay_timer_.Stop(); |
| updating_snapshots_ = false; |
| if (cancelled) { |
| RestoreTabSelections(); |
| } |
| } |
| |
| void WmOverviewController::UpdateSnapshots() { |
| |
| // Only updating snapshots during overview mode. |
| if (layout_mode_ != OVERVIEW_MODE) |
| return; |
| |
| // Only reloading snapshots when not already started. |
| if (updating_snapshots_ || delay_timer_.IsRunning()) |
| return; |
| |
| // Only update one snapshot in round-robin mode. |
| // Start delay timer after each update unless all snapshots had been updated. |
| if (!listeners_.size()) |
| return; |
| |
| if (int(listeners_.size()) <= browser_listener_index_) { |
| DLOG(INFO) << "Browser listener(s) have disappeared since last update"; |
| browser_listener_index_ = 0; |
| tab_contents_index_ = -1; |
| } else { |
| BrowserListener* listener = listeners_[browser_listener_index_].get(); |
| if (listener->count() <= tab_contents_index_) { |
| DLOG(INFO) << "Tab content(s) have disappeared since last update"; |
| tab_contents_index_ = -1; |
| } |
| } |
| |
| int browser_listener_index = browser_listener_index_; |
| int tab_contents_index = tab_contents_index_; |
| |
| bool loop_back = false; |
| while (1) { |
| BrowserListener* listener = listeners_[browser_listener_index].get(); |
| tab_contents_index = |
| listener->ConfigureNextUnconfiguredSnapshot(tab_contents_index); |
| if (tab_contents_index >= 0) { |
| updating_snapshots_ = true; // Prevent future parallel updates. |
| browser_listener_index_ = browser_listener_index; |
| tab_contents_index_ = tab_contents_index; |
| return; |
| } |
| |
| // Found next one; |
| browser_listener_index++; |
| browser_listener_index = browser_listener_index % listeners_.size(); |
| tab_contents_index = -1; |
| |
| if (loop_back) |
| break; |
| loop_back = browser_listener_index == browser_listener_index_; |
| } |
| |
| // All snapshots have been fully updated. |
| updating_snapshots_ = false; |
| } |
| |
| void WmOverviewController::AddAllBrowsers() { |
| // Make a copy so the old ones aren't deleted yet. |
| BrowserListenerVector old_listeners; |
| |
| listeners_.swap(old_listeners); |
| |
| // Iterator through the browser list, adding all the browsers in the |
| // new order. If they were in the old list of listeners, just copy |
| // that linked pointer, instead of making a new listener, so that we |
| // can avoid lots of spurious destroy/create messages. |
| BrowserList::const_iterator iterator = BrowserList::begin(); |
| while (iterator != BrowserList::end()) { |
| // Don't add a browser to the list if that type of browser doesn't |
| // have snapshots in overview mode. |
| if ((*iterator)->type() != Browser::TYPE_NORMAL && |
| (*iterator)->type() != Browser::TYPE_APP) { |
| ++iterator; |
| continue; |
| } |
| |
| BrowserListenerVector::value_type item( |
| BrowserListenerVector::value_type(NULL)); |
| for (BrowserListenerVector::iterator old_iter = old_listeners.begin(); |
| old_iter != old_listeners.end(); ++old_iter) { |
| if ((*old_iter)->browser() == *iterator) { |
| item = *old_iter; |
| break; |
| } |
| } |
| |
| // This browser isn't tracked by any listener, so create it. |
| if (item.get() == NULL) { |
| item = BrowserListenerVector::value_type( |
| new BrowserListener(*iterator, this)); |
| } |
| listeners_.push_back(item); |
| ++iterator; |
| } |
| } |
| |
| } // namespace chromeos |