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