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 ¶ms); 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