OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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.h" |
| 12 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/chromeos/wm_ipc.h" |
| 14 #include "chrome/browser/chromeos/wm_overview_snapshot.h" |
| 15 #include "chrome/browser/renderer_host/render_view_host.h" |
| 16 #include "chrome/browser/tab_contents/tab_contents.h" |
| 17 #include "chrome/browser/tab_contents/tab_contents_view.h" |
| 18 #include "chrome/browser/tab_contents/thumbnail_generator.h" |
| 19 #include "chrome/browser/views/frame/browser_extender.h" |
| 20 #include "chrome/browser/views/frame/browser_view.h" |
| 21 #include "chrome/common/notification_service.h" |
| 22 #include "views/widget/root_view.h" |
| 23 #include "views/widget/widget_gtk.h" |
| 24 #include "views/window/window.h" |
| 25 |
| 26 using std::vector; |
| 27 |
| 28 #if !defined(OS_CHROMEOS) |
| 29 #error This file is only meant to be compiled for ChromeOS |
| 30 #endif |
| 31 |
| 32 namespace chromeos { |
| 33 |
| 34 class BrowserListener : public TabStripModelObserver { |
| 35 public: |
| 36 BrowserListener(Browser* browser, WmOverviewController* parent); |
| 37 ~BrowserListener(); |
| 38 |
| 39 // Begin TabStripModelObserver methods |
| 40 virtual void TabInsertedAt(TabContents* contents, |
| 41 int index, |
| 42 bool foreground); |
| 43 virtual void TabClosingAt(TabContents* contents, int index) {} |
| 44 virtual void TabDetachedAt(TabContents* contents, int index); |
| 45 virtual void TabMoved(TabContents* contents, |
| 46 int from_index, |
| 47 int to_index); |
| 48 virtual void TabChangedAt(TabContents* contents, int index, |
| 49 TabStripModelObserver::TabChangeType change_type); |
| 50 virtual void TabStripEmpty(); |
| 51 virtual void TabDeselectedAt(TabContents* contents, int index) {} |
| 52 virtual void TabSelectedAt(TabContents* old_contents, |
| 53 TabContents* new_contents, |
| 54 int index, |
| 55 bool user_gesture); |
| 56 // End TabStripModelObserver methods |
| 57 |
| 58 // Returns the number of tabs in this child. |
| 59 int count() const { return browser_->tabstrip_model()->count(); } |
| 60 |
| 61 // Returns the browser that this child gets its data from. |
| 62 Browser* browser() const { return browser_; } |
| 63 |
| 64 // Removes all the snapshots and re-populates them from the browser. |
| 65 void RecreateSnapshots(); |
| 66 |
| 67 // Updates the selected index and tab count on the toplevel window. |
| 68 void UpdateSelectedIndex(int index); |
| 69 |
| 70 // Finds the first cell with no snapshot and invokes ConfigureCell |
| 71 // for it. Returns false if there are no more cells to configure on |
| 72 // this listener. |
| 73 bool ConfigureNextUnconfiguredSnapshot(); |
| 74 |
| 75 // Saves the currently selected tab. |
| 76 void SaveCurrentTab() { original_selected_tab_ = browser_->selected_index(); } |
| 77 |
| 78 // Reverts the selected browser tab to the tab that was selected |
| 79 // when This BrowserListener was created, or the last time |
| 80 // SaveCurrentTab was called. |
| 81 void RestoreOriginalSelectedTab(); |
| 82 |
| 83 // Selects the tab at the given index. |
| 84 void SelectTab(int index); |
| 85 |
| 86 // Shows any snapshots that are not visible, and updates their |
| 87 // bitmaps. |
| 88 void ShowSnapshots(); |
| 89 |
| 90 private: |
| 91 // Returns the tab contents from the tab model for this child at index. |
| 92 TabContents* GetTabContentsAt(int index) const { |
| 93 return browser_->tabstrip_model()->GetTabContentsAt(index); |
| 94 } |
| 95 |
| 96 // Configures a cell from the tab contents. |
| 97 void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents); |
| 98 |
| 99 // Configures a cell from the model. |
| 100 void ConfigureCell(WmOverviewSnapshot* cell, int index) { |
| 101 ConfigureCell(cell, GetTabContentsAt(index)); |
| 102 } |
| 103 |
| 104 // Inserts a new snapshot, initialized from the model, at the given |
| 105 // index, and renumbers any following snapshots. |
| 106 void InsertSnapshot(int index); |
| 107 |
| 108 // Removes the snapshot at index. |
| 109 void ClearSnapshot(int index); |
| 110 |
| 111 // Renumbers the index atom in the snapshots starting at the given |
| 112 // index. |
| 113 void RenumberSnapshots(int start_index); |
| 114 |
| 115 Browser* browser_; // Not owned |
| 116 WmOverviewController* controller_; // Not owned |
| 117 |
| 118 // Widgets containing snapshot images for this browser. |
| 119 typedef std::vector<WmOverviewSnapshot* > SnapshotVector; |
| 120 SnapshotVector snapshots_; |
| 121 |
| 122 // True if the snapshots are showing. |
| 123 bool snapshots_showing_; |
| 124 |
| 125 // The tab selected the last time SaveCurrentTab is called. |
| 126 int original_selected_tab_; |
| 127 |
| 128 DISALLOW_COPY_AND_ASSIGN(BrowserListener); |
| 129 }; |
| 130 |
| 131 BrowserListener::BrowserListener(Browser* browser, |
| 132 WmOverviewController* controller) |
| 133 : browser_(browser), |
| 134 controller_(controller), |
| 135 snapshots_showing_(false), |
| 136 original_selected_tab_(-1) { |
| 137 CHECK(browser_); |
| 138 CHECK(controller_); |
| 139 browser_->tabstrip_model()->AddObserver(this); |
| 140 } |
| 141 |
| 142 BrowserListener::~BrowserListener() { |
| 143 browser_->tabstrip_model()->RemoveObserver(this); |
| 144 } |
| 145 |
| 146 void BrowserListener::TabInsertedAt(TabContents* contents, |
| 147 int index, |
| 148 bool foreground) { |
| 149 InsertSnapshot(index); |
| 150 RenumberSnapshots(index); |
| 151 UpdateSelectedIndex(browser_->selected_index()); |
| 152 } |
| 153 |
| 154 void BrowserListener::TabDetachedAt(TabContents* contents, int index) { |
| 155 ClearSnapshot(index); |
| 156 UpdateSelectedIndex(browser_->selected_index()); |
| 157 RenumberSnapshots(index); |
| 158 } |
| 159 |
| 160 void BrowserListener::TabMoved(TabContents* contents, |
| 161 int from_index, |
| 162 int to_index) { |
| 163 // Need to reorder tab in the snapshots list, and reset the window |
| 164 // type atom on the affected snapshots (the one moved, and all the |
| 165 // ones after it), so that their indices are correct. |
| 166 WmOverviewSnapshot* snapshot = snapshots_[from_index]; |
| 167 snapshots_.erase(snapshots_.begin() + from_index); |
| 168 snapshots_.insert(snapshots_.begin() + to_index, snapshot); |
| 169 |
| 170 RenumberSnapshots(std::min(to_index, from_index)); |
| 171 } |
| 172 |
| 173 void BrowserListener::TabChangedAt( |
| 174 TabContents* contents, |
| 175 int index, |
| 176 TabStripModelObserver::TabChangeType change_type) { |
| 177 snapshots_[index]->reload_snapshot(); |
| 178 controller_->StartDelayTimer(); |
| 179 } |
| 180 |
| 181 void BrowserListener::TabStripEmpty() { |
| 182 snapshots_.clear(); |
| 183 } |
| 184 |
| 185 void BrowserListener::TabSelectedAt(TabContents* old_contents, |
| 186 TabContents* new_contents, |
| 187 int index, |
| 188 bool user_gesture) { |
| 189 UpdateSelectedIndex(index); |
| 190 } |
| 191 |
| 192 void BrowserListener::RecreateSnapshots() { |
| 193 snapshots_.clear(); |
| 194 |
| 195 for (int i = 0; i < count(); ++i) { |
| 196 InsertSnapshot(i); |
| 197 } |
| 198 |
| 199 RenumberSnapshots(0); |
| 200 } |
| 201 |
| 202 void BrowserListener::UpdateSelectedIndex(int index) { |
| 203 // Get the window params and check to make sure that they are |
| 204 // different from what we know before we set them, to avoid extra |
| 205 // notifications. |
| 206 std::vector<int> params; |
| 207 WmIpc::WindowType type = WmIpc::instance()->GetWindowType( |
| 208 GTK_WIDGET(browser_->window()->GetNativeHandle()), |
| 209 ¶ms); |
| 210 DCHECK(type == WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL); |
| 211 if (params.size() > 1) { |
| 212 if (params[0] == browser_->tab_count() && |
| 213 params[0] == index) |
| 214 return; |
| 215 } |
| 216 |
| 217 params.clear(); |
| 218 params.push_back(browser_->tab_count()); |
| 219 params.push_back(index); |
| 220 WmIpc::instance()->SetWindowType( |
| 221 GTK_WIDGET(browser_->window()->GetNativeHandle()), |
| 222 WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL, |
| 223 ¶ms); |
| 224 } |
| 225 |
| 226 bool BrowserListener::ConfigureNextUnconfiguredSnapshot() { |
| 227 for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) { |
| 228 WmOverviewSnapshot* cell = snapshots_[i]; |
| 229 if (!cell->configured_snapshot()) { |
| 230 ConfigureCell(cell, GetTabContentsAt(i)); |
| 231 return true; |
| 232 } |
| 233 } |
| 234 return false; |
| 235 } |
| 236 |
| 237 void BrowserListener::RestoreOriginalSelectedTab() { |
| 238 if (original_selected_tab_ >= 0) { |
| 239 browser_->SelectTabContentsAt(original_selected_tab_, false); |
| 240 } |
| 241 } |
| 242 |
| 243 void BrowserListener::ShowSnapshots() { |
| 244 for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) { |
| 245 WmOverviewSnapshot* snapshot = snapshots_[i]; |
| 246 snapshot->reload_snapshot(); |
| 247 if (!snapshot->IsVisible()) { |
| 248 snapshot->Show(); |
| 249 } |
| 250 } |
| 251 } |
| 252 |
| 253 void BrowserListener::SelectTab(int index) { |
| 254 browser_->SelectTabContentsAt(index, true); |
| 255 } |
| 256 |
| 257 void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell, |
| 258 TabContents* contents) { |
| 259 if (contents) { |
| 260 if (controller_->allow_show_snapshots()) { |
| 261 ThumbnailGenerator* generator = |
| 262 g_browser_process->GetThumbnailGenerator(); |
| 263 // TODO: Make sure that if the cell gets deleted before the |
| 264 // callback is called that it sticks around until it gets |
| 265 // called. (some kind of "in flight" list that uses linked_ptr |
| 266 // to make sure they don't actually get deleted?) Also, make |
| 267 // sure that any request for a thumbnail eventually returns |
| 268 // (even if it has bogus data), so we don't leak orphaned cells. |
| 269 ThumbnailGenerator::ThumbnailReadyCallback* callback = |
| 270 NewCallback(cell, &WmOverviewSnapshot::SetImage); |
| 271 generator->AskForThumbnail(contents->render_view_host(), |
| 272 callback, cell->size()); |
| 273 } |
| 274 } else { |
| 275 // This happens because the contents haven't been loaded yet. |
| 276 |
| 277 // Make sure we set the snapshot image to something, otherwise |
| 278 // configured_snapshot remains false and ConfigureNextUnconfiguredSnapshot |
| 279 // would get stuck. |
| 280 if (controller_->allow_show_snapshots()) { |
| 281 cell->SetImage(SkBitmap()); |
| 282 } |
| 283 } |
| 284 } |
| 285 |
| 286 void BrowserListener::InsertSnapshot(int index) { |
| 287 WmOverviewSnapshot* snapshot = new WmOverviewSnapshot; |
| 288 gfx::Rect bounds = |
| 289 static_cast<BrowserView*>(browser_->window())->GetClientAreaBounds(); |
| 290 gfx::Size size(bounds.width()/2, bounds.height()/2); |
| 291 snapshot->Init(size, browser_, index); |
| 292 snapshots_.insert(snapshots_.begin() + index, snapshot); |
| 293 snapshot->reload_snapshot(); |
| 294 controller_->StartDelayTimer(); |
| 295 } |
| 296 |
| 297 // Removes the snapshot at index. |
| 298 void BrowserListener::ClearSnapshot(int index) { |
| 299 snapshots_[index]->CloseNow(); |
| 300 snapshots_.erase(snapshots_.begin() + index); |
| 301 } |
| 302 |
| 303 void BrowserListener::RenumberSnapshots(int start_index) { |
| 304 int changes = 0; |
| 305 for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) { |
| 306 if (snapshots_[i]->index() != static_cast<int>(i)) { |
| 307 snapshots_[i]->UpdateIndex(browser_, i); |
| 308 changes++; |
| 309 } |
| 310 } |
| 311 } |
| 312 |
| 313 /////////////////////////////////// |
| 314 // WmOverviewController methods |
| 315 |
| 316 // static |
| 317 WmOverviewController* WmOverviewController::instance() { |
| 318 static WmOverviewController* instance = NULL; |
| 319 if (!instance) { |
| 320 instance = Singleton<WmOverviewController>::get(); |
| 321 } |
| 322 return instance; |
| 323 } |
| 324 |
| 325 WmOverviewController::WmOverviewController() |
| 326 : allow_show_snapshots_(false) { |
| 327 AddAllBrowsers(); |
| 328 BrowserList::AddObserver(this); |
| 329 WmMessageListener::instance()->AddObserver(this); |
| 330 } |
| 331 |
| 332 WmOverviewController::~WmOverviewController() { |
| 333 WmMessageListener::instance()->RemoveObserver(this); |
| 334 BrowserList::RemoveObserver(this); |
| 335 listeners_.clear(); |
| 336 } |
| 337 |
| 338 void WmOverviewController::Observe(NotificationType type, |
| 339 const NotificationSource& source, |
| 340 const NotificationDetails& details) { |
| 341 // Now that the browser window is ready, we create the snapshots. |
| 342 if (type == NotificationType::BROWSER_WINDOW_READY) { |
| 343 const Browser* browser = Source<const Browser>(source).ptr(); |
| 344 BrowserListenerVector::value_type new_listener( |
| 345 new BrowserListener(const_cast<Browser*>(browser), this)); |
| 346 listeners_.push_back(new_listener); |
| 347 new_listener->RecreateSnapshots(); |
| 348 |
| 349 // This makes sure that the new listener is in the right order (to |
| 350 // match the order in the browser list). |
| 351 AddAllBrowsers(); |
| 352 } |
| 353 } |
| 354 |
| 355 // Called immediately before a browser is removed from the list. |
| 356 void WmOverviewController::OnBrowserRemoving(const Browser* browser) { |
| 357 for (BrowserListenerVector::iterator i = listeners_.begin(); |
| 358 i != listeners_.end(); ++i) { |
| 359 if ((*i)->browser() == browser) { |
| 360 listeners_.erase(i); |
| 361 return; |
| 362 } |
| 363 } |
| 364 } |
| 365 |
| 366 BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) { |
| 367 gpointer data = NULL; |
| 368 gdk_window_get_user_data(gdk_window, &data); |
| 369 GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); |
| 370 if (widget) { |
| 371 GtkWindow* gtk_window = GTK_WINDOW(widget); |
| 372 return BrowserView::GetBrowserViewForNativeWindow(gtk_window); |
| 373 } else { |
| 374 return NULL; |
| 375 } |
| 376 } |
| 377 |
| 378 void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message, |
| 379 GdkWindow* window) { |
| 380 switch (message.type()) { |
| 381 case WmIpc::Message::CHROME_NOTIFY_LAYOUT_MODE: { |
| 382 if (message.param(0) == 0 || BrowserList::size() == 0) { |
| 383 Hide(message.param(1) != 0); |
| 384 } else { |
| 385 Show(); |
| 386 } |
| 387 break; |
| 388 } |
| 389 case WmIpc::Message::CHROME_NOTIFY_TAB_SELECT: { |
| 390 BrowserView* browser_window = GetBrowserViewForGdkWindow(window); |
| 391 // Find out which listener this goes to, and send it there. |
| 392 for (BrowserListenerVector::iterator i = listeners_.begin(); |
| 393 i != listeners_.end(); ++i) { |
| 394 if ((*i)->browser()->window() == browser_window) { |
| 395 (*i)->SelectTab(message.param(0)); |
| 396 break; |
| 397 } |
| 398 } |
| 399 break; |
| 400 } |
| 401 default: |
| 402 break; |
| 403 } |
| 404 } |
| 405 |
| 406 void WmOverviewController::StartDelayTimer() { |
| 407 // We leave the delay timer running if it already is -- this means |
| 408 // we're rate limiting the number of times we can reconfigure the |
| 409 // snapshots (at most once every 350ms). If we were to restart the |
| 410 // delay timer, it could result in a very long delay until they get |
| 411 // configured if tabs keep changing. |
| 412 if (!delay_timer_.IsRunning()) { |
| 413 configure_timer_.Stop(); |
| 414 // Note that this pause is kind of a hack: it's just long enough |
| 415 // that the overview-mode transitions will have finished happening |
| 416 // before we start refreshing snapshots, so we don't bog the CPU |
| 417 // while it's trying to animate the overview transitions. |
| 418 delay_timer_.Start( |
| 419 base::TimeDelta::FromMilliseconds(350), this, |
| 420 &WmOverviewController::StartConfiguring); |
| 421 } |
| 422 } |
| 423 |
| 424 void WmOverviewController::RestoreTabSelections() { |
| 425 for (BrowserListenerVector::iterator i = listeners_.begin(); |
| 426 i != listeners_.end(); ++i) { |
| 427 (*i)->RestoreOriginalSelectedTab(); |
| 428 } |
| 429 } |
| 430 |
| 431 void WmOverviewController::SaveTabSelections() { |
| 432 for (BrowserListenerVector::iterator i = listeners_.begin(); |
| 433 i != listeners_.end(); ++i) { |
| 434 (*i)->SaveCurrentTab(); |
| 435 } |
| 436 } |
| 437 |
| 438 void WmOverviewController::Show() { |
| 439 SaveTabSelections(); |
| 440 for (BrowserListenerVector::iterator i = listeners_.begin(); |
| 441 i != listeners_.end(); ++i) { |
| 442 (*i)->ShowSnapshots(); |
| 443 } |
| 444 allow_show_snapshots_ = false; |
| 445 StartDelayTimer(); |
| 446 } |
| 447 |
| 448 void WmOverviewController::Hide(bool cancelled) { |
| 449 configure_timer_.Stop(); |
| 450 delay_timer_.Stop(); |
| 451 if (cancelled) { |
| 452 RestoreTabSelections(); |
| 453 } |
| 454 } |
| 455 |
| 456 void WmOverviewController::StartConfiguring() { |
| 457 allow_show_snapshots_ = true; |
| 458 configure_timer_.Stop(); |
| 459 configure_timer_.Start( |
| 460 base::TimeDelta::FromMilliseconds(10), this, |
| 461 &WmOverviewController::ConfigureNextUnconfiguredSnapshot); |
| 462 } |
| 463 |
| 464 // Just configure one unconfigured cell. If there aren't any left, |
| 465 // then stop the timer. |
| 466 void WmOverviewController::ConfigureNextUnconfiguredSnapshot() { |
| 467 for (BrowserListenerVector::size_type i = 0; i < listeners_.size(); ++i) { |
| 468 BrowserListener* listener = listeners_[i].get(); |
| 469 if (listener->ConfigureNextUnconfiguredSnapshot()) |
| 470 return; |
| 471 } |
| 472 configure_timer_.Stop(); |
| 473 } |
| 474 |
| 475 void WmOverviewController::AddAllBrowsers() { |
| 476 // Make a copy so the old ones aren't deleted yet. |
| 477 BrowserListenerVector old_listeners; |
| 478 |
| 479 listeners_.swap(old_listeners); |
| 480 |
| 481 // Iterator through the browser list, adding all the browsers in the |
| 482 // new order. If they were in the old list of listeners, just copy |
| 483 // that linked pointer, instead of making a new listener, so that we |
| 484 // can avoid lots of spurious destroy/create messages. |
| 485 BrowserList::const_iterator iterator = BrowserList::begin(); |
| 486 while (iterator != BrowserList::end()) { |
| 487 BrowserListenerVector::value_type item( |
| 488 BrowserListenerVector::value_type(NULL)); |
| 489 for (BrowserListenerVector::iterator old_iter = old_listeners.begin(); |
| 490 old_iter != old_listeners.end(); ++old_iter) { |
| 491 if ((*old_iter)->browser() == *iterator) { |
| 492 item = *old_iter; |
| 493 break; |
| 494 } |
| 495 } |
| 496 |
| 497 // This browser isn't owned by any listener, so create it. |
| 498 if (item.get() == NULL) { |
| 499 item = BrowserListenerVector::value_type( |
| 500 new BrowserListener(*iterator, this)); |
| 501 |
| 502 // This browser didn't already exist, and so we haven't been |
| 503 // watching it for tab insertions, so we need to create the |
| 504 // snapshots associated with it. |
| 505 item->RecreateSnapshots(); |
| 506 } |
| 507 listeners_.push_back(item); |
| 508 ++iterator; |
| 509 } |
| 510 |
| 511 // Make sure we get notifications for when browser windows are ready. |
| 512 if (registrar_.IsEmpty()) |
| 513 registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY, |
| 514 NotificationService::AllSources()); |
| 515 } |
| 516 |
| 517 } // namespace chromeos |
OLD | NEW |