summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser_init.cc12
-rw-r--r--chrome/browser/chromeos/frame/browser_view.cc16
-rw-r--r--chrome/browser/chromeos/panels/panel_browsertest.cc2
-rw-r--r--chrome/browser/chromeos/wm_ipc.cc14
-rw-r--r--chrome/browser/chromeos/wm_ipc.h95
-rw-r--r--chrome/browser/chromeos/wm_overview_controller.cc517
-rw-r--r--chrome/browser/chromeos/wm_overview_controller.h147
-rw-r--r--chrome/browser/chromeos/wm_overview_snapshot.cc80
-rw-r--r--chrome/browser/chromeos/wm_overview_snapshot.h59
-rw-r--r--chrome/browser/renderer_host/backing_store_x.cc17
-rw-r--r--chrome/browser/renderer_host/render_widget_helper.h2
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc16
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h15
-rw-r--r--chrome/browser/renderer_host/render_widget_host_painting_observer.h14
-rw-r--r--chrome/browser/renderer_host/render_widget_host_unittest.cc50
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator.cc123
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator.h34
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator_unittest.cc6
18 files changed, 1129 insertions, 90 deletions
diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc
index a5e1706..8da58f6 100644
--- a/chrome/browser/browser_init.cc
+++ b/chrome/browser/browser_init.cc
@@ -21,6 +21,7 @@
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/first_run.h"
#include "chrome/browser/net/dns_global.h"
+#include "chrome/browser/net/url_fixer_upper.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
@@ -30,13 +31,12 @@
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/shell_integration.h"
-#include "chrome/browser/tabs/pinned_tab_codec.h"
+#include "chrome/browser/status_icons/status_tray_manager.h"
#include "chrome/browser/tab_contents/infobar_delegate.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
-#include "chrome/browser/net/url_fixer_upper.h"
-#include "chrome/browser/status_icons/status_tray_manager.h"
+#include "chrome/browser/tabs/pinned_tab_codec.h"
#include "chrome/browser/user_data_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
@@ -68,7 +68,6 @@
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/browser_notification_observers.h"
-#include "chrome/browser/dom_ui/mediaplayer_ui.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/mount_library.h"
#include "chrome/browser/chromeos/cros/power_library.h"
@@ -76,6 +75,8 @@
#include "chrome/browser/chromeos/low_battery_observer.h"
#include "chrome/browser/chromeos/usb_mount_observer.h"
#include "chrome/browser/chromeos/wm_message_listener.h"
+#include "chrome/browser/chromeos/wm_overview_controller.h"
+#include "chrome/browser/dom_ui/mediaplayer_ui.h"
#endif
namespace {
@@ -398,6 +399,9 @@ bool BrowserInit::LaunchBrowser(
// of what window has focus.
chromeos::WmMessageListener::instance();
+ // Create the WmOverviewController so it can register with the listener.
+ chromeos::WmOverviewController::instance();
+
// Install the GView request interceptor that will redirect requests
// of compatible documents (PDF, etc) to the GView document viewer.
const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc
index d6aff2f..b3925c1 100644
--- a/chrome/browser/chromeos/frame/browser_view.cc
+++ b/chrome/browser/chromeos/frame/browser_view.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <string>
+#include <vector>
#include "app/menus/simple_menu_model.h"
#include "app/theme_provider.h"
@@ -392,16 +393,29 @@ void BrowserView::Init() {
otr_avatar_icon_->SetImage(GetOTRAvatarIcon());
otr_avatar_icon_->SetID(VIEW_ID_OTR_AVATAR);
AddChildView(otr_avatar_icon_);
+
+ // Make sure the window is set to the right type.
+ std::vector<int> params;
+ params.push_back(browser()->tab_count());
+ params.push_back(browser()->selected_index());
+ WmIpc::instance()->SetWindowType(
+ GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()),
+ WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL,
+ &params);
}
void BrowserView::Show() {
bool was_visible = frame()->GetWindow()->IsVisible();
::BrowserView::Show();
if (!was_visible) {
+ // Have to update the tab count and selected index to reflect reality.
+ std::vector<int> params;
+ params.push_back(browser()->tab_count());
+ params.push_back(browser()->selected_index());
WmIpc::instance()->SetWindowType(
GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()),
WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL,
- NULL);
+ &params);
}
}
diff --git a/chrome/browser/chromeos/panels/panel_browsertest.cc b/chrome/browser/chromeos/panels/panel_browsertest.cc
index 1ec09b2..4b01924 100644
--- a/chrome/browser/chromeos/panels/panel_browsertest.cc
+++ b/chrome/browser/chromeos/panels/panel_browsertest.cc
@@ -64,7 +64,7 @@ IN_PROC_BROWSER_TEST_F(PanelTest, PanelOpenSmall) {
EXPECT_EQ(
WmIpc::WINDOW_TYPE_CHROME_PANEL_CONTENT,
WmIpc::instance()->GetWindowType(
- GTK_WIDGET(new_browser->window()->GetNativeHandle())));
+ GTK_WIDGET(new_browser->window()->GetNativeHandle()), NULL));
}
// Large popups should open as new tab.
diff --git a/chrome/browser/chromeos/wm_ipc.cc b/chrome/browser/chromeos/wm_ipc.cc
index da1ed06..f3e298b 100644
--- a/chrome/browser/chromeos/wm_ipc.cc
+++ b/chrome/browser/chromeos/wm_ipc.cc
@@ -84,12 +84,18 @@ bool WmIpc::SetWindowType(GtkWidget* widget,
type_to_atom_[ATOM_CHROME_WINDOW_TYPE], values);
}
-WmIpc::WindowType WmIpc::GetWindowType(GtkWidget* widget) {
- int type;
- if (x11_util::GetIntProperty(
+WmIpc::WindowType WmIpc::GetWindowType(GtkWidget* widget,
+ std::vector<int>* params) {
+ std::vector<int> properties;
+ if (x11_util::GetIntArrayProperty(
x11_util::GetX11WindowFromGtkWidget(widget),
atom_to_string_[type_to_atom_[ATOM_CHROME_WINDOW_TYPE]],
- &type)) {
+ &properties)) {
+ int type = properties.front();
+ if (params) {
+ params->clear();
+ params->insert(params->begin(), properties.begin() + 1, properties.end());
+ }
return static_cast<WindowType>(type);
} else {
return WINDOW_TYPE_UNKNOWN;
diff --git a/chrome/browser/chromeos/wm_ipc.h b/chrome/browser/chromeos/wm_ipc.h
index 2d35c0b..7df76a29 100644
--- a/chrome/browser/chromeos/wm_ipc.h
+++ b/chrome/browser/chromeos/wm_ipc.h
@@ -41,20 +41,14 @@ class WmIpc {
WINDOW_TYPE_UNKNOWN = 0,
// A top-level Chrome window.
+ // param[0]: The number of tabs currently in this Chrome window.
+ // param[1]: The index of the currently selected tab in this
+ // Chrome window.
WINDOW_TYPE_CHROME_TOPLEVEL,
- // A window showing scaled-down views of all of the tabs within a
- // Chrome window.
- WINDOW_TYPE_CHROME_TAB_SUMMARY,
-
- // A tab that's been detached from a Chrome window and is currently
- // being dragged.
- // param[0]: Cursor's initial X position at the start of the drag
- // param[1]: Cursor's initial Y position
- // param[2]: X component of cursor's offset from upper-left corner of
- // tab at start of drag
- // param[3]: Y component of cursor's offset
- WINDOW_TYPE_CHROME_FLOATING_TAB,
+ // Vestiges of the old windows-across-the-bottom overview mode.
+ DEPRECATED_WINDOW_TYPE_CHROME_TAB_SUMMARY,
+ DEPRECATED_WINDOW_TYPE_CHROME_FLOATING_TAB,
// The contents of a popup window.
// param[0]: X ID of associated titlebar, which must be mapped before
@@ -66,8 +60,8 @@ class WmIpc {
// drawn above the panel when it's expanded.
WINDOW_TYPE_CHROME_PANEL_TITLEBAR,
- // A small window that when clicked creates a new browser window.
- WINDOW_TYPE_CREATE_BROWSER_WINDOW,
+ // Vestiges of an earlier UI design.
+ DEPRECATED_WINDOW_TYPE_CREATE_BROWSER_WINDOW,
// A Chrome info bubble (e.g. the bookmark bubble). These are
// transient RGBA windows; we skip the usual transient behavior of
@@ -76,8 +70,7 @@ class WmIpc {
// A window showing a view of a tab within a Chrome window.
// param[0]: X ID of toplevel window that owns it.
- // param[1]: index of this tab in the tab order (range is 0 to
- // sum of all tabs in all browsers).
+ // param[1]: index of this tab in the toplevel window that owns it.
WINDOW_TYPE_CHROME_TAB_SNAPSHOT,
// The following types are used for the windows that represent a user that
@@ -118,27 +111,10 @@ class WmIpc {
enum Type {
UNKNOWN = 0,
- // Notify Chrome when a floating tab has entered or left a tab
- // summary window. Sent to the summary window.
- // param[0]: X ID of the floating tab window
- // param[1]: state (0 means left, 1 means entered or currently in)
- // param[2]: X coordinate relative to summary window
- // param[3]: Y coordinate
- CHROME_NOTIFY_FLOATING_TAB_OVER_TAB_SUMMARY,
-
- // Notify Chrome when a floating tab has entered or left a top-level
- // window. Sent to the window being entered/left.
- // param[0]: X ID of the floating tab window
- // param[1]: state (0 means left, 1 means entered)
- CHROME_NOTIFY_FLOATING_TAB_OVER_TOPLEVEL,
-
- // Instruct a top-level Chrome window to change the visibility of its
- // tab summary window.
- // param[0]: desired visibility (0 means hide, 1 means show)
- // param[1]: X position (relative to the left edge of the root
- // window) of the center of the top-level window. Only
- // relevant for "show" messages
- CHROME_SET_TAB_SUMMARY_VISIBILITY,
+ // Vestiges of the old windows-across-the-bottom overview mode.
+ DEPRECATED_CHROME_NOTIFY_FLOATING_TAB_OVER_TAB_SUMMARY,
+ DEPRECATED_CHROME_NOTIFY_FLOATING_TAB_OVER_TOPLEVEL,
+ DEPRECATED_CHROME_SET_TAB_SUMMARY_VISIBILITY,
// Tell the WM to collapse or expand a panel.
// param[0]: X ID of the panel window
@@ -148,16 +124,12 @@ class WmIpc {
// Notify Chrome that the panel state has changed. Sent to the panel
// window.
// param[0]: new state (0 means collapsed, 1 means expanded)
+ // TODO: Deprecate this; Chrome can just watch for changes to the
+ // _CHROME_STATE property to get the same information.
CHROME_NOTIFY_PANEL_STATE,
- // Instruct the WM to move a floating tab. The passed-in position is
- // that of the cursor; the tab's composited window is displaced based
- // on the cursor's offset from the upper-left corner of the tab at
- // the start of the drag.
- // param[0]: X ID of the floating tab window
- // param[1]: X coordinate to which the tab should be moved
- // param[2]: Y coordinate
- WM_MOVE_FLOATING_TAB,
+ // From the old windows-across-the-bottom overview mode.
+ DEPRECATED_WM_MOVE_FLOATING_TAB,
// Notify the WM that a panel has been dragged.
// param[0]: X ID of the panel's content window
@@ -180,18 +152,21 @@ class WmIpc {
// param[0]: X ID of the panel's content window
WM_NOTIFY_PANEL_DRAG_COMPLETE,
- // Deprecated. Send a _NET_ACTIVE_WINDOW client message to focus a window
- // instead (e.g. using gtk_window_present()).
+ // Deprecated. Send a _NET_ACTIVE_WINDOW client message to focus a
+ // window instead (e.g. using gtk_window_present()).
DEPRECATED_WM_FOCUS_WINDOW,
// Notify Chrome that the layout mode (for example, overview or
- // focused) has changed.
- // param[0]: new mode (0 means focused, 1 means overview)
+ // active) has changed. Since overview mode can be "cancelled"
+ // (user hits escape to revert), we have an extra parameter to
+ // indicate this.
+ // param[0]: new mode (0 means active mode, 1 means overview mode)
+ // param[1]: was mode cancelled? (0 = no, 1 = yes)
CHROME_NOTIFY_LAYOUT_MODE,
- // Instruct the WM to enter overview mode.
+ // Deprecated. Instruct the WM to enter overview mode.
// param[0]: X ID of the window to show the tab overview for.
- WM_SWITCH_TO_OVERVIEW_MODE,
+ DEPRECATED_WM_SWITCH_TO_OVERVIEW_MODE,
// Let the WM know which version of this file Chrome is using. It's
// difficult to make changes synchronously to Chrome and the WM (our
@@ -218,11 +193,11 @@ class WmIpc {
// param[0]: version of this protocol currently supported
WM_NOTIFY_IPC_VERSION,
- // Notify Chrome when a tab snapshot has been 'magnified' in the
- // overview. Sent to the top level window.
- // param[0]: X ID of the tab snapshot window
- // param[1]: state (0 means end magnify, 1 means begin magnify)
- CHROME_NOTIFY_TAB_SNAPSHOT_MAGNIFY,
+ // Notify Chrome when a tab has been selected in the overview.
+ // Sent to the toplevel window associated with the magnified
+ // tab.
+ // param[0]: tab index of newly selected tab.
+ CHROME_NOTIFY_TAB_SELECT,
// Forces the window manager to hide the login windows.
WM_HIDE_LOGIN,
@@ -295,9 +270,11 @@ class WmIpc {
WindowType type,
const std::vector<int>* params);
- // Gets the type of the window. The caller is responsible for trapping
- // errors from the X server.
- WmIpc::WindowType GetWindowType(GtkWidget* widget);
+ // Gets the type of the window, and any associated parameters. The
+ // caller is responsible for trapping errors from the X server. If
+ // the parameters are not interesting to the caller, NULL may be
+ // passed for |params|.
+ WmIpc::WindowType GetWindowType(GtkWidget* widget, std::vector<int>* params);
// Sends a message to the WM.
void SendMessage(const Message& msg);
diff --git a/chrome/browser/chromeos/wm_overview_controller.cc b/chrome/browser/chromeos/wm_overview_controller.cc
new file mode 100644
index 0000000..01a0888
--- /dev/null
+++ b/chrome/browser/chromeos/wm_overview_controller.cc
@@ -0,0 +1,517 @@
+// Copyright (c) 2010 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/linked_ptr.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/wm_ipc.h"
+#include "chrome/browser/chromeos/wm_overview_snapshot.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/browser/tab_contents/thumbnail_generator.h"
+#include "chrome/browser/views/frame/browser_extender.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/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 {
+
+class BrowserListener : public TabStripModelObserver {
+ public:
+ BrowserListener(Browser* browser, WmOverviewController* parent);
+ ~BrowserListener();
+
+ // Begin TabStripModelObserver methods
+ virtual void TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground);
+ virtual void TabClosingAt(TabContents* contents, int index) {}
+ virtual void TabDetachedAt(TabContents* contents, int index);
+ virtual void TabMoved(TabContents* contents,
+ int from_index,
+ int to_index);
+ virtual void TabChangedAt(TabContents* contents, int index,
+ TabStripModelObserver::TabChangeType change_type);
+ virtual void TabStripEmpty();
+ virtual void TabDeselectedAt(TabContents* contents, int index) {}
+ virtual void TabSelectedAt(TabContents* old_contents,
+ TabContents* 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();
+
+ // Updates the selected index and tab count on the toplevel window.
+ void UpdateSelectedIndex(int index);
+
+ // Finds the first cell with no snapshot and invokes ConfigureCell
+ // for it. Returns false if there are no more cells to configure on
+ // this listener.
+ bool ConfigureNextUnconfiguredSnapshot();
+
+ // Saves the currently selected tab.
+ void SaveCurrentTab() { original_selected_tab_ = browser_->selected_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);
+
+ // Shows any snapshots that are not visible, and updates their
+ // bitmaps.
+ void ShowSnapshots();
+
+ private:
+ // Returns the tab contents from the tab model for this child at index.
+ TabContents* GetTabContentsAt(int index) const {
+ return browser_->tabstrip_model()->GetTabContentsAt(index);
+ }
+
+ // 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
+
+ // Widgets containing snapshot images for this browser.
+ typedef std::vector<WmOverviewSnapshot* > SnapshotVector;
+ SnapshotVector snapshots_;
+
+ // True if the snapshots are showing.
+ bool snapshots_showing_;
+
+ // 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),
+ snapshots_showing_(false),
+ original_selected_tab_(-1) {
+ CHECK(browser_);
+ CHECK(controller_);
+ browser_->tabstrip_model()->AddObserver(this);
+}
+
+BrowserListener::~BrowserListener() {
+ browser_->tabstrip_model()->RemoveObserver(this);
+}
+
+void BrowserListener::TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground) {
+ InsertSnapshot(index);
+ RenumberSnapshots(index);
+ UpdateSelectedIndex(browser_->selected_index());
+}
+
+void BrowserListener::TabDetachedAt(TabContents* contents, int index) {
+ ClearSnapshot(index);
+ UpdateSelectedIndex(browser_->selected_index());
+ RenumberSnapshots(index);
+}
+
+void BrowserListener::TabMoved(TabContents* 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.
+ WmOverviewSnapshot* snapshot = snapshots_[from_index];
+ snapshots_.erase(snapshots_.begin() + from_index);
+ snapshots_.insert(snapshots_.begin() + to_index, snapshot);
+
+ RenumberSnapshots(std::min(to_index, from_index));
+}
+
+void BrowserListener::TabChangedAt(
+ TabContents* contents,
+ int index,
+ TabStripModelObserver::TabChangeType change_type) {
+ snapshots_[index]->reload_snapshot();
+ controller_->StartDelayTimer();
+}
+
+void BrowserListener::TabStripEmpty() {
+ snapshots_.clear();
+}
+
+void BrowserListener::TabSelectedAt(TabContents* old_contents,
+ TabContents* new_contents,
+ int index,
+ bool user_gesture) {
+ UpdateSelectedIndex(index);
+}
+
+void BrowserListener::RecreateSnapshots() {
+ snapshots_.clear();
+
+ for (int i = 0; i < count(); ++i) {
+ InsertSnapshot(i);
+ }
+
+ RenumberSnapshots(0);
+}
+
+void BrowserListener::UpdateSelectedIndex(int index) {
+ // Get the window params and check to make sure that they are
+ // different from what we know before we set them, to avoid extra
+ // notifications.
+ std::vector<int> params;
+ WmIpc::WindowType type = WmIpc::instance()->GetWindowType(
+ GTK_WIDGET(browser_->window()->GetNativeHandle()),
+ &params);
+ DCHECK(type == WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL);
+ if (params.size() > 1) {
+ if (params[0] == browser_->tab_count() &&
+ params[0] == index)
+ return;
+ }
+
+ params.clear();
+ params.push_back(browser_->tab_count());
+ params.push_back(index);
+ WmIpc::instance()->SetWindowType(
+ GTK_WIDGET(browser_->window()->GetNativeHandle()),
+ WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL,
+ &params);
+}
+
+bool BrowserListener::ConfigureNextUnconfiguredSnapshot() {
+ for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
+ WmOverviewSnapshot* cell = snapshots_[i];
+ if (!cell->configured_snapshot()) {
+ ConfigureCell(cell, GetTabContentsAt(i));
+ return true;
+ }
+ }
+ return false;
+}
+
+void BrowserListener::RestoreOriginalSelectedTab() {
+ if (original_selected_tab_ >= 0) {
+ browser_->SelectTabContentsAt(original_selected_tab_, false);
+ }
+}
+
+void BrowserListener::ShowSnapshots() {
+ for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
+ WmOverviewSnapshot* snapshot = snapshots_[i];
+ snapshot->reload_snapshot();
+ if (!snapshot->IsVisible()) {
+ snapshot->Show();
+ }
+ }
+}
+
+void BrowserListener::SelectTab(int index) {
+ browser_->SelectTabContentsAt(index, true);
+}
+
+void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell,
+ TabContents* contents) {
+ if (contents) {
+ if (controller_->allow_show_snapshots()) {
+ 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.
+ ThumbnailGenerator::ThumbnailReadyCallback* callback =
+ NewCallback(cell, &WmOverviewSnapshot::SetImage);
+ generator->AskForThumbnail(contents->render_view_host(),
+ callback, cell->size());
+ }
+ } 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.
+ if (controller_->allow_show_snapshots()) {
+ cell->SetImage(SkBitmap());
+ }
+ }
+}
+
+void BrowserListener::InsertSnapshot(int index) {
+ WmOverviewSnapshot* snapshot = new WmOverviewSnapshot;
+ gfx::Rect bounds =
+ static_cast<BrowserView*>(browser_->window())->GetClientAreaBounds();
+ gfx::Size size(bounds.width()/2, bounds.height()/2);
+ snapshot->Init(size, browser_, index);
+ snapshots_.insert(snapshots_.begin() + index, snapshot);
+ snapshot->reload_snapshot();
+ controller_->StartDelayTimer();
+}
+
+// Removes the snapshot at index.
+void BrowserListener::ClearSnapshot(int index) {
+ snapshots_[index]->CloseNow();
+ snapshots_.erase(snapshots_.begin() + index);
+}
+
+void BrowserListener::RenumberSnapshots(int start_index) {
+ int changes = 0;
+ for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) {
+ if (snapshots_[i]->index() != static_cast<int>(i)) {
+ snapshots_[i]->UpdateIndex(browser_, i);
+ changes++;
+ }
+ }
+}
+
+///////////////////////////////////
+// WmOverviewController methods
+
+// static
+WmOverviewController* WmOverviewController::instance() {
+ static WmOverviewController* instance = NULL;
+ if (!instance) {
+ instance = Singleton<WmOverviewController>::get();
+ }
+ return instance;
+}
+
+WmOverviewController::WmOverviewController()
+ : allow_show_snapshots_(false) {
+ AddAllBrowsers();
+ BrowserList::AddObserver(this);
+ WmMessageListener::instance()->AddObserver(this);
+}
+
+WmOverviewController::~WmOverviewController() {
+ WmMessageListener::instance()->RemoveObserver(this);
+ BrowserList::RemoveObserver(this);
+ listeners_.clear();
+}
+
+void WmOverviewController::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // Now that the browser window is ready, we create the snapshots.
+ if (type == NotificationType::BROWSER_WINDOW_READY) {
+ const Browser* browser = Source<const Browser>(source).ptr();
+ BrowserListenerVector::value_type new_listener(
+ new BrowserListener(const_cast<Browser*>(browser), this));
+ listeners_.push_back(new_listener);
+ new_listener->RecreateSnapshots();
+
+ // This makes sure that the new listener is in the right order (to
+ // match the order in the browser list).
+ AddAllBrowsers();
+ }
+}
+
+// Called immediately before a browser is removed from the list.
+void WmOverviewController::OnBrowserRemoving(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 WmIpc::Message::CHROME_NOTIFY_LAYOUT_MODE: {
+ if (message.param(0) == 0 || BrowserList::size() == 0) {
+ Hide(message.param(1) != 0);
+ } else {
+ Show();
+ }
+ break;
+ }
+ case WmIpc::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) {
+ (*i)->SelectTab(message.param(0));
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void WmOverviewController::StartDelayTimer() {
+ // We leave the delay timer running if it already is -- this means
+ // we're rate limiting the number of times we can reconfigure the
+ // snapshots (at most once every 350ms). If we were to restart the
+ // delay timer, it could result in a very long delay until they get
+ // configured if tabs keep changing.
+ if (!delay_timer_.IsRunning()) {
+ configure_timer_.Stop();
+ // Note that this pause is kind of a hack: it's just long enough
+ // that the overview-mode transitions will have finished happening
+ // before we start refreshing snapshots, so we don't bog the CPU
+ // while it's trying to animate the overview transitions.
+ delay_timer_.Start(
+ base::TimeDelta::FromMilliseconds(350), this,
+ &WmOverviewController::StartConfiguring);
+ }
+}
+
+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();
+ }
+ allow_show_snapshots_ = false;
+ StartDelayTimer();
+}
+
+void WmOverviewController::Hide(bool cancelled) {
+ configure_timer_.Stop();
+ delay_timer_.Stop();
+ if (cancelled) {
+ RestoreTabSelections();
+ }
+}
+
+void WmOverviewController::StartConfiguring() {
+ allow_show_snapshots_ = true;
+ configure_timer_.Stop();
+ configure_timer_.Start(
+ base::TimeDelta::FromMilliseconds(10), this,
+ &WmOverviewController::ConfigureNextUnconfiguredSnapshot);
+}
+
+// Just configure one unconfigured cell. If there aren't any left,
+// then stop the timer.
+void WmOverviewController::ConfigureNextUnconfiguredSnapshot() {
+ for (BrowserListenerVector::size_type i = 0; i < listeners_.size(); ++i) {
+ BrowserListener* listener = listeners_[i].get();
+ if (listener->ConfigureNextUnconfiguredSnapshot())
+ return;
+ }
+ configure_timer_.Stop();
+}
+
+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()) {
+ 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 owned by any listener, so create it.
+ if (item.get() == NULL) {
+ item = BrowserListenerVector::value_type(
+ new BrowserListener(*iterator, 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.
+ item->RecreateSnapshots();
+ }
+ listeners_.push_back(item);
+ ++iterator;
+ }
+
+ // Make sure we get notifications for when browser windows are ready.
+ if (registrar_.IsEmpty())
+ registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
+ NotificationService::AllSources());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/wm_overview_controller.h b/chrome/browser/chromeos/wm_overview_controller.h
new file mode 100644
index 0000000..b37f965
--- /dev/null
+++ b/chrome/browser/chromeos/wm_overview_controller.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_
+#define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_
+
+#include <vector>
+
+#include "base/linked_ptr.h"
+#include "base/singleton.h"
+#include "base/timer.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/chromeos/wm_message_listener.h"
+#include "chrome/common/notification_registrar.h"
+#include "gfx/rect.h"
+
+namespace views {
+class Widget;
+}
+
+class Animation;
+class Browser;
+
+namespace chromeos {
+
+class BrowserListener;
+class WmOverviewSnapshot;
+
+// WmOverviewController is responsible for managing a list of objects
+// that listen to the browsers (BrowserListeners, defined in the
+// source file for this class) for changes, and keep a list of
+// snapshot images in sync with the browser tab contents.
+//
+// As tabs are added/removed from the browsers, the number of snapshot
+// windows changes to match.
+//
+// As obtaining and setting snapshots is expensive we delay setting
+// the snapshot. The delay is controlled by delay_timer_. Once the
+// timer fires another timer is started (configure_timer_). This timer
+// invokes ConfigureNextUnconfiguredCell on the BrowserListener, which
+// obtains and sets the snapshot of the next uncofigured
+// cell. ConfigureNextUnconfiguredCell only configures one cell at a
+// time until they are all configured.
+
+class WmOverviewController : public BrowserList::Observer,
+ public WmMessageListener::Observer,
+ public NotificationObserver {
+ public:
+ // This class is a singleton.
+ static WmOverviewController* instance();
+
+ // BrowserList::Observer methods
+ // This is called immediately after a browser is added to the list.
+ void OnBrowserAdded(const Browser* browser) {}
+
+ // This is called immediately before a browser is removed from the list.
+ void OnBrowserRemoving(const Browser* browser);
+ // End BrowserList::Observer methods
+
+ // WmMessageListener::Observer methods
+ // This is called immediately after a browser is added to the list.
+ void ProcessWmMessage(const WmIpc::Message& message,
+ GdkWindow* window);
+ // End WmMessageListener::Observer methods
+
+ // NotificationObserver methods
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+ // End NotificationObserver methods
+
+ // Used by the BrowserListeners to configure their snapshots.
+ const gfx::Rect& monitor_bounds() const { return monitor_bounds_; }
+
+ // Tells the listeners whether or not they're allowed to show
+ // snapshots yet.
+ bool allow_show_snapshots() const { return allow_show_snapshots_; }
+
+ // Starts the delay timer, and once the delay is over, configures
+ // any unconfigured snapshots one at a time until none are left to
+ // be configured.
+ void StartDelayTimer();
+
+ private:
+ friend struct DefaultSingletonTraits<WmOverviewController>;
+
+ // This class is a singleton.
+ WmOverviewController();
+ ~WmOverviewController();
+
+ // Restores tab selections on all browsers to what they were when
+ // Show was last called. Used when cancelling overview mode.
+ void RestoreTabSelections();
+
+ // Saves the currently selected tabs in the snapshots so that they
+ // can be restored later with RestoreTabSelections.
+ void SaveTabSelections();
+
+ // Show the snapshot windows, saving current tab selections.
+ void Show();
+
+ // Hide the snapshot windows. When |cancelled| is true, then the
+ // tab selections that were saved when the snapshot windows were
+ // shown are restored.
+ void Hide(bool cancelled);
+
+ // Invoked by delay_timer_. Sets allow_show_snapshots_ to true and starts
+ // configure_timer_.
+ void StartConfiguring();
+
+ // Configure the next unconfigured snapshot window owned by any of
+ // the listeners.
+ void ConfigureNextUnconfiguredSnapshot();
+
+ // Add browser listeners for all existing browsers, reusing any that
+ // were already there.
+ void AddAllBrowsers();
+
+ // This is so we can register for notifications.
+ NotificationRegistrar registrar_;
+
+ // This is a vector of listeners that listen to all the browsers.
+ typedef std::vector<linked_ptr<BrowserListener> > BrowserListenerVector;
+ BrowserListenerVector listeners_;
+
+ // This is the bounds of the monitor we're being displayed on. This
+ // is used to adjust the size of snapshots so they'll fit.
+ gfx::Rect monitor_bounds_;
+
+ // This indicates whether we should actually set the snapshots so we
+ // don't do it when we don't need to. It is initially false, then
+ // set to true by StartConfiguring.
+ bool allow_show_snapshots_;
+
+ // See description above class for details.
+ base::OneShotTimer<WmOverviewController> delay_timer_;
+
+ // See description above class for details.
+ base::RepeatingTimer<WmOverviewController> configure_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WmOverviewController);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/wm_overview_snapshot.cc b/chrome/browser/chromeos/wm_overview_snapshot.cc
new file mode 100644
index 0000000..97c2a8e
--- /dev/null
+++ b/chrome/browser/chromeos/wm_overview_snapshot.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2010 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_snapshot.h"
+
+#include <vector>
+
+#include "app/x11_util.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/chromeos/wm_ipc.h"
+#include "views/border.h"
+#include "views/controls/image_view.h"
+#include "views/controls/label.h"
+#include "views/grid_layout.h"
+
+using views::ColumnSet;
+using views::GridLayout;
+using std::vector;
+
+#if !defined(OS_CHROMEOS)
+#error This file is only meant to be compiled for ChromeOS
+#endif
+
+namespace chromeos {
+
+WmOverviewSnapshot::WmOverviewSnapshot()
+ : WidgetGtk(TYPE_WINDOW),
+ snapshot_view_(NULL),
+ index_(-1),
+ configured_snapshot_(false) {
+}
+
+void WmOverviewSnapshot::Init(const gfx::Size& size,
+ Browser* browser,
+ int index) {
+ snapshot_view_ = new views::ImageView();
+ MakeTransparent();
+
+ snapshot_view_->set_background(
+ views::Background::CreateSolidBackground(SK_ColorWHITE));
+ snapshot_view_->set_border(
+ views::Border::CreateSolidBorder(1, SkColorSetRGB(176, 176, 176)));
+
+ WidgetGtk::Init(NULL, gfx::Rect(gfx::Point(0,0), size));
+
+ SetContentsView(snapshot_view_);
+
+ UpdateIndex(browser, index);
+}
+
+
+void WmOverviewSnapshot::UpdateIndex(Browser* browser, int index) {
+ vector<int> params;
+ params.push_back(x11_util::GetX11WindowFromGtkWidget(
+ GTK_WIDGET(browser->window()->GetNativeHandle())));
+ params.push_back(index);
+ WmIpc::instance()->SetWindowType(
+ GetNativeView(),
+ WmIpc::WINDOW_TYPE_CHROME_TAB_SNAPSHOT,
+ &params);
+ index_ = index;
+}
+
+void WmOverviewSnapshot::SetImage(const SkBitmap& image) {
+ CHECK(snapshot_view_) << "Init not called before setting image.";
+ snapshot_view_->SetImage(image);
+
+ // Reset the bounds to the size of the image.
+ gfx::Rect bounds;
+ GetBounds(&bounds, false);
+ bounds.set_width(image.width());
+ bounds.set_height(image.height());
+ SetBounds(bounds);
+
+ configured_snapshot_ = true;
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/wm_overview_snapshot.h b/chrome/browser/chromeos/wm_overview_snapshot.h
new file mode 100644
index 0000000..7bc3699
--- /dev/null
+++ b/chrome/browser/chromeos/wm_overview_snapshot.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_
+#define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "views/view.h"
+#include "views/widget/widget_gtk.h"
+#include "views/controls/image_view.h"
+
+class Browser;
+
+namespace chromeos {
+
+// A single snapshot displayed by WmOverviewController.
+// WmOverviewSnapshot contains a label, favicon and snapshot.
+class WmOverviewSnapshot : public views::WidgetGtk {
+ public:
+ WmOverviewSnapshot();
+ void Init(const gfx::Size& size, Browser* browser, int index);
+
+ void SetImage(const SkBitmap& image);
+
+ void UpdateIndex(Browser* browser, int index);
+ int index() const { return index_; }
+
+ // Returns the size of the snapshot widget.
+ gfx::Size size() const {
+ gfx::Rect rect;
+ GetBounds(&rect, false);
+ return rect.size();
+ }
+
+ // Has the snapshot been configured? This is true after SetSnapshot
+ // is invoked.
+ bool configured_snapshot() const { return configured_snapshot_; }
+
+ // This resets the configured_snapshot flag for this snapshot so it will
+ // get reloaded the next time we check.
+ void reload_snapshot() { configured_snapshot_ = false; }
+
+ private:
+ // This control is the contents view for this widget.
+ views::ImageView* snapshot_view_;
+
+ // This is the tab index of the snapshot in the associated browser.
+ int index_;
+
+ // This indicates whether or not the snapshot has been configured.
+ bool configured_snapshot_;
+
+ DISALLOW_COPY_AND_ASSIGN(WmOverviewSnapshot);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_
diff --git a/chrome/browser/renderer_host/backing_store_x.cc b/chrome/browser/renderer_host/backing_store_x.cc
index d88dca7..f10e589 100644
--- a/chrome/browser/renderer_host/backing_store_x.cc
+++ b/chrome/browser/renderer_host/backing_store_x.cc
@@ -326,6 +326,7 @@ bool BackingStoreX::CopyFromBackingStore(const gfx::Rect& rect,
// TODO(jhawkins): Need to convert the image data if the image bits per pixel
// is not 32.
+ // Note that this also initializes the output bitmap as opaque.
if (!output->initialize(width, height, true) ||
image->bits_per_pixel != 32) {
if (shared_memory_support_ != x11_util::SHARED_MEMORY_NONE)
@@ -335,15 +336,19 @@ bool BackingStoreX::CopyFromBackingStore(const gfx::Rect& rect,
return false;
}
- // The X image might have a different row stride, so iterate through it and
- // copy each row out, only up to the pixels we're actually using.
- // This code assumes a visual mode where a pixel is represented using
- // a byte for each component.
+ // The X image might have a different row stride, so iterate through
+ // it and copy each row out, only up to the pixels we're actually
+ // using. This code assumes a visual mode where a pixel is
+ // represented using a 32-bit unsigned int, with a byte per component.
SkBitmap bitmap = output->getTopPlatformDevice().accessBitmap(true);
for (int y = 0; y < height; y++) {
+ const uint32* src_row = reinterpret_cast<uint32*>(
+ &image->data[image->bytes_per_line * y]);
uint32* dest_row = bitmap.getAddr32(0, y);
- const char* src_row = &image->data[image->bytes_per_line * y];
- memcpy(dest_row, src_row, width * 4);
+ for (int x = 0; x < width; ++x, ++dest_row) {
+ // Force alpha to be 0xff, because otherwise it causes rendering problems.
+ *dest_row = src_row[x] | 0xff000000;
+ }
}
if (shared_memory_support_ != x11_util::SHARED_MEMORY_NONE)
diff --git a/chrome/browser/renderer_host/render_widget_helper.h b/chrome/browser/renderer_host/render_widget_helper.h
index 53b336c..33ca744 100644
--- a/chrome/browser/renderer_host/render_widget_helper.h
+++ b/chrome/browser/renderer_host/render_widget_helper.h
@@ -188,7 +188,7 @@ class RenderWidgetHelper
#endif
// A map of live paint messages. Must hold pending_paints_lock_ to access.
- // The PaintMsgProxy objects are not owned by this map. (See PaintMsgProxy
+ // The UpdateMsgProxy objects are not owned by this map. (See UpdateMsgProxy
// for details about how the lifetime of instances are managed.)
UpdateMsgProxyMap pending_paints_;
Lock pending_paints_lock_;
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index 6b20935..59b2231 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -127,6 +127,7 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewGone, OnMsgRenderViewGone)
IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose)
IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnMsgPaintAtSizeAck)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnMsgUpdateRect)
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateVideo, OnMsgCreateVideo)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateVideo, OnMsgUpdateVideo)
@@ -280,6 +281,14 @@ void RenderWidgetHost::SetIsLoading(bool is_loading) {
view_->SetIsLoading(is_loading);
}
+void RenderWidgetHost::PaintAtSize(TransportDIB::Handle dib_handle,
+ const gfx::Size& size) {
+ // Ask the renderer to create a bitmap regardless of whether it's
+ // hidden, being resized, redrawn, etc., and to scale it by the
+ // scale factor given.
+ Send(new ViewMsg_PaintAtSize(routing_id_, dib_handle, size));
+}
+
BackingStore* RenderWidgetHost::GetBackingStore(bool force_create) {
// We should not be asked to paint while we are hidden. If we are hidden,
// then it means that our consumer failed to call WasRestored. If we're not
@@ -676,6 +685,13 @@ void RenderWidgetHost::OnMsgRequestMove(const gfx::Rect& pos) {
}
}
+void RenderWidgetHost::OnMsgPaintAtSizeAck(
+ const TransportDIB::Handle& dib_handle, const gfx::Size& size) {
+ if (painting_observer_) {
+ painting_observer_->WidgetDidReceivePaintAtSizeAck(this, dib_handle, size);
+ }
+}
+
void RenderWidgetHost::OnMsgUpdateRect(
const ViewHostMsg_UpdateRect_Params& params) {
TimeTicks paint_start = TimeTicks::Now();
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index d57d8bc..7b87d13 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -205,6 +205,16 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// Indicates if the page has finished loading.
void SetIsLoading(bool is_loading);
+ // This tells the renderer to paint into a bitmap and return it,
+ // regardless of whether the tab is hidden or not. It returns the
+ // bitmap scaled so it matches the requested size, so that the
+ // scaling happens on the rendering thread. When the bitmap is
+ // ready, the renderer sends a PaintAtSizeACK to this host, and the
+ // painting observer is notified. Note that this bypasses most of
+ // the update logic that is normally invoked, and doesn't put the
+ // results into the backing store.
+ void PaintAtSize(TransportDIB::Handle dib_handle, const gfx::Size& size);
+
// Get access to the widget's backing store. If a resize is in progress,
// then the current size of the backing store may be less than the size of
// the widget's view. If you pass |force_create| as true, then the backing
@@ -413,6 +423,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
FRIEND_TEST(RenderWidgetHostTest, Resize);
FRIEND_TEST(RenderWidgetHostTest, ResizeThenCrash);
FRIEND_TEST(RenderWidgetHostTest, HiddenPaint);
+ FRIEND_TEST(RenderWidgetHostTest, PaintAtSize);
// Tell this object to destroy itself.
void Destroy();
@@ -421,7 +432,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// if it is.
void CheckRendererIsUnresponsive();
- // Called if we know the renderer is responsive. When we currently thing the
+ // Called if we know the renderer is responsive. When we currently think the
// renderer is unresponsive, this will clear that state and call
// NotifyRendererResponsive.
void RendererIsResponsive();
@@ -431,6 +442,8 @@ class RenderWidgetHost : public IPC::Channel::Listener,
void OnMsgRenderViewGone();
void OnMsgClose();
void OnMsgRequestMove(const gfx::Rect& pos);
+ void OnMsgPaintAtSizeAck(const TransportDIB::Handle& dib_handle,
+ const gfx::Size& size);
void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
void OnMsgCreateVideo(const gfx::Size& size);
void OnMsgUpdateVideo(TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect);
diff --git a/chrome/browser/renderer_host/render_widget_host_painting_observer.h b/chrome/browser/renderer_host/render_widget_host_painting_observer.h
index d24b61a..332e446 100644
--- a/chrome/browser/renderer_host/render_widget_host_painting_observer.h
+++ b/chrome/browser/renderer_host/render_widget_host_painting_observer.h
@@ -5,8 +5,15 @@
#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_PAINTING_OBSERVER_H_
#define CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_PAINTING_OBSERVER_H_
+#include "app/surface/transport_dib.h"
+
class BackingStore;
class RenderWidgetHost;
+class SkBitmap;
+
+namespace gfx {
+class Size;
+}
// This class can be used to observe painting events for a RenderWidgetHost.
// Its primary goal in Chrome is to allow thumbnails to be generated.
@@ -19,6 +26,13 @@ class RenderWidgetHostPaintingObserver {
// Indicates that the RenderWidgetHost just updated the backing store.
virtual void WidgetDidUpdateBackingStore(RenderWidgetHost* widget) = 0;
+
+ // This notifies the painting observer that a PaintAtSizeACK was
+ // received.
+ virtual void WidgetDidReceivePaintAtSizeAck(
+ RenderWidgetHost* widget,
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Size& size) = 0;
};
#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_PAINTING_OBSERVER_H_
diff --git a/chrome/browser/renderer_host/render_widget_host_unittest.cc b/chrome/browser/renderer_host/render_widget_host_unittest.cc
index e0ffe1a..28be917 100644
--- a/chrome/browser/renderer_host/render_widget_host_unittest.cc
+++ b/chrome/browser/renderer_host/render_widget_host_unittest.cc
@@ -9,6 +9,7 @@
#include "base/timer.h"
#include "build/build_config.h"
#include "chrome/browser/renderer_host/backing_store.h"
+#include "chrome/browser/renderer_host/render_widget_host_painting_observer.h"
#include "chrome/browser/renderer_host/test/test_render_view_host.h"
#include "chrome/common/render_messages.h"
#include "gfx/canvas.h"
@@ -19,6 +20,10 @@ using base::TimeDelta;
using WebKit::WebInputEvent;
using WebKit::WebMouseWheelEvent;
+namespace gfx {
+class Size;
+}
+
// RenderWidgetHostProcess -----------------------------------------------------
class RenderWidgetHostProcess : public MockRenderProcessHost {
@@ -191,6 +196,32 @@ class MockRenderWidgetHost : public RenderWidgetHost {
bool unresponsive_timer_fired_;
};
+// MockPaintingObserver --------------------------------------------------------
+
+class MockPaintingObserver : public RenderWidgetHostPaintingObserver {
+ public:
+ void WidgetWillDestroyBackingStore(RenderWidgetHost* widget,
+ BackingStore* backing_store) {}
+ void WidgetDidUpdateBackingStore(RenderWidgetHost* widget) {}
+ void WidgetDidReceivePaintAtSizeAck(RenderWidgetHost* host,
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Size& size) {
+ host_ = reinterpret_cast<MockRenderWidgetHost*>(host);
+ dib_handle_ = dib_handle;
+ size_ = size;
+ }
+
+ MockRenderWidgetHost* host() const { return host_; }
+ TransportDIB::Handle dib_handle() const { return dib_handle_; }
+ gfx::Size size() const { return size_; }
+
+ private:
+ MockRenderWidgetHost* host_;
+ TransportDIB::Handle dib_handle_;
+ gfx::Size size_;
+};
+
+
// RenderWidgetHostTest --------------------------------------------------------
class RenderWidgetHostTest : public testing::Test {
@@ -504,6 +535,24 @@ TEST_F(RenderWidgetHostTest, HiddenPaint) {
EXPECT_TRUE(needs_repaint.a);
}
+TEST_F(RenderWidgetHostTest, PaintAtSize) {
+ host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), gfx::Size(20, 30));
+ EXPECT_TRUE(
+ process_->sink().GetUniqueMessageMatching(ViewMsg_PaintAtSize::ID));
+
+ MockPaintingObserver observer;
+ host_->set_painting_observer(&observer);
+
+ // Need to generate a fake handle value on all platforms.
+ TransportDIB::Handle handle = TransportDIB::GetFakeHandleForTest();
+ host_->OnMsgPaintAtSizeAck(handle, gfx::Size(20, 30));
+ EXPECT_TRUE(host_ == observer.host());
+ EXPECT_TRUE(handle == observer.dib_handle());
+ EXPECT_EQ(20, observer.size().width());
+ EXPECT_EQ(30, observer.size().height());
+ host_->set_painting_observer(NULL);
+}
+
TEST_F(RenderWidgetHostTest, HandleKeyEventsWeSent) {
// Simulate a keyboard event.
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
@@ -649,4 +698,3 @@ TEST_F(RenderWidgetHostTest, StopAndStartHangMonitorTimeout) {
MessageLoop::current()->Run();
EXPECT_TRUE(host_->unresponsive_timer_fired());
}
-
diff --git a/chrome/browser/tab_contents/thumbnail_generator.cc b/chrome/browser/tab_contents/thumbnail_generator.cc
index 212045e..a3c476c 100644
--- a/chrome/browser/tab_contents/thumbnail_generator.cc
+++ b/chrome/browser/tab_contents/thumbnail_generator.cc
@@ -5,12 +5,14 @@
#include "chrome/browser/tab_contents/thumbnail_generator.h"
#include <algorithm>
+#include <map>
#include "base/histogram.h"
#include "base/time.h"
#include "build/build_config.h"
#include "chrome/browser/renderer_host/backing_store.h"
#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/property_bag.h"
#include "gfx/rect.h"
@@ -105,17 +107,26 @@ SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {
&temp_canvas))
return result;
const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false);
- result = SkBitmapOperations::DownsampleByTwoUntilSize(bmp, kThumbnailWidth,
+
+#if defined(OS_CHROMEOS)
+ // On ChromeOS, the thumbnail is always half the dimensions of the
+ // original.
+ result = SkBitmapOperations::DownsampleByTwo(bmp);
+#else
+ result = SkBitmapOperations::DownsampleByTwoUntilSize(bmp,
+ kThumbnailWidth,
kThumbnailHeight);
- // This is a bit subtle. SkBitmaps are refcounted, but the magic ones in
- // PlatformCanvas can't be ssigned to SkBitmap with proper
- // refcounting. If the bitmap doesn't change, then the downsampler will
- // return the input bitmap, which will be the reference to the weird
- // PlatformCanvas one insetad of a regular one. To get a regular refcounted
- // bitmap, we need to copy it.
- if (bmp.width() == result.width() && bmp.height() == result.height())
+ // This is a bit subtle. SkBitmaps are refcounted, but the magic
+ // ones in PlatformCanvas can't be assigned to SkBitmap with proper
+ // refcounting. If the bitmap doesn't change, then the downsampler
+ // will return the input bitmap, which will be the reference to the
+ // weird PlatformCanvas one insetad of a regular one. To get a
+ // regular refcounted bitmap, we need to copy it.
+ if (bmp.width() == result.width() &&
+ bmp.height() == result.height())
bmp.copyTo(&result, SkBitmap::kARGB_8888_Config);
+#endif
HISTOGRAM_TIMES(kThumbnailHistogramName,
base::TimeTicks::Now() - begin_compute_thumbnail);
@@ -124,6 +135,12 @@ SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {
} // namespace
+struct ThumbnailGenerator::AsyncRequestInfo {
+ scoped_ptr<ThumbnailReadyCallback> callback;
+ scoped_ptr<TransportDIB> thumbnail_dib;
+ RenderWidgetHost* renderer; // Not owned.
+};
+
ThumbnailGenerator::ThumbnailGenerator()
: no_timeout_(false) {
// The BrowserProcessImpl creates this non-lazily. If you add nontrivial
@@ -144,14 +161,48 @@ void ThumbnailGenerator::StartThumbnailing() {
// aren't views like select popups.
registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
NotificationService::AllSources());
-
registrar_.Add(this, NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED,
NotificationService::AllSources());
registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED,
NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED,
+ NotificationService::AllSources());
}
}
+void ThumbnailGenerator::AskForThumbnail(RenderWidgetHost* renderer,
+ ThumbnailReadyCallback* callback,
+ gfx::Size size) {
+ SkBitmap first_try = GetThumbnailForRenderer(renderer);
+ if (!first_try.isNull()) {
+ // We were able to find a non-null thumbnail for this renderer, so
+ // we'll go with it.
+ callback->Run(first_try);
+ delete callback;
+ return;
+ }
+
+ // We are going to render the thumbnail asynchronously now, so keep
+ // this callback for later lookup when the rendering is done.
+ static int sequence_num = 0;
+ TransportDIB* thumbnail_dib = TransportDIB::Create(
+ size.width() * size.height() * 4, sequence_num++);
+ linked_ptr<AsyncRequestInfo> request_info(new AsyncRequestInfo);
+ request_info->callback.reset(callback);
+ request_info->thumbnail_dib.reset(thumbnail_dib);
+ request_info->renderer = renderer;
+ ThumbnailCallbackMap::value_type new_value(thumbnail_dib->handle(),
+ request_info);
+ std::pair<ThumbnailCallbackMap::iterator, bool> result =
+ callback_map_.insert(new_value);
+ if (!result.second) {
+ NOTREACHED() << "Callback already registered?";
+ return;
+ }
+
+ renderer->PaintAtSize(thumbnail_dib->handle(), size);
+}
+
SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
RenderWidgetHost* renderer) const {
WidgetThumbnail* wt = GetDataForHost(renderer);
@@ -221,6 +272,42 @@ void ThumbnailGenerator::WidgetDidUpdateBackingStore(
wt->thumbnail = SkBitmap();
}
+void ThumbnailGenerator::WidgetDidReceivePaintAtSizeAck(
+ RenderWidgetHost* widget,
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Size& size) {
+ // Lookup the callback, run it, and erase it.
+ ThumbnailCallbackMap::iterator item = callback_map_.find(dib_handle);
+ if (item != callback_map_.end()) {
+ TransportDIB* dib = item->second->thumbnail_dib.get();
+ DCHECK(dib);
+ if (!dib) {
+ return;
+ }
+
+ // Create an SkBitmap from the DIB.
+ SkBitmap non_owned_bitmap;
+ SkBitmap result;
+
+ // Fill out the non_owned_bitmap with the right config. Note that
+ // this code assumes that the transport dib is a 32-bit ARGB
+ // image.
+ non_owned_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ size.width(), size.height());
+ non_owned_bitmap.setPixels(dib->memory());
+
+ // Now alloc/copy the memory so we own it and can pass it around,
+ // and the memory won't go away when the DIB goes away.
+ // TODO: Figure out a way to avoid this copy?
+ non_owned_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
+
+ item->second->callback->Run(result);
+
+ // We're done with the callback, and with the DIB, so delete both.
+ callback_map_.erase(item);
+ }
+}
+
void ThumbnailGenerator::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -243,6 +330,10 @@ void ThumbnailGenerator::Observe(NotificationType type,
WidgetDestroyed(Source<RenderWidgetHost>(source).ptr());
break;
+ case NotificationType::TAB_CONTENTS_DISCONNECTED:
+ TabContentsDisconnected(Source<TabContents>(source).ptr());
+ break;
+
default:
NOTREACHED();
}
@@ -294,6 +385,20 @@ void ThumbnailGenerator::WidgetDestroyed(RenderWidgetHost* widget) {
EraseHostFromShownList(widget);
}
+void ThumbnailGenerator::TabContentsDisconnected(TabContents* contents) {
+ // Go through the existing callbacks, and find any that have the
+ // same renderer as this TabContents and remove them so they don't
+ // hang around.
+ ThumbnailCallbackMap::iterator iterator = callback_map_.begin();
+ RenderWidgetHost* renderer = contents->render_view_host();
+ while (iterator != callback_map_.end()) {
+ if (iterator->second->renderer == renderer) {
+ callback_map_.erase(iterator);
+ }
+ ++iterator;
+ }
+}
+
void ThumbnailGenerator::ShownDelayHandler() {
base::TimeTicks threshold = base::TimeTicks::Now() -
base::TimeDelta::FromMilliseconds(kVisibilitySlopMS);
diff --git a/chrome/browser/tab_contents/thumbnail_generator.h b/chrome/browser/tab_contents/thumbnail_generator.h
index 499c2d6..6398e17 100644
--- a/chrome/browser/tab_contents/thumbnail_generator.h
+++ b/chrome/browser/tab_contents/thumbnail_generator.h
@@ -5,22 +5,30 @@
#ifndef CHROME_BROWSER_TAB_CONTENTS_THUMBNAIL_GENERATOR_H_
#define CHROME_BROWSER_TAB_CONTENTS_THUMBNAIL_GENERATOR_H_
+#include <map>
+#include <utility>
#include <vector>
#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/linked_ptr.h"
+#include "base/lock.h"
#include "base/timer.h"
#include "chrome/browser/renderer_host/render_widget_host_painting_observer.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
+class RenderViewHost;
class RenderWidgetHost;
class SkBitmap;
+class TabContents;
// This class MUST be destroyed after the RenderWidgetHosts, since it installs
// a painting observer that is not removed.
class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,
public NotificationObserver {
public:
+ typedef Callback1<const SkBitmap&>::Type ThumbnailReadyCallback;
// This class will do nothing until you call StartThumbnailing.
ThumbnailGenerator();
~ThumbnailGenerator();
@@ -29,6 +37,17 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,
// be called repeatedly and with wild abandon to no ill effect.
void StartThumbnailing();
+ // This registers a callback that can receive the resulting SkBitmap
+ // from the renderer when it is done rendering it. This differs
+ // from GetThumbnailForRenderer in that it is asynchronous, and
+ // because it will also fetch the bitmap even if the tab is hidden.
+ // In addition, if the renderer has to be invoked, the scaling of
+ // the thumbnail happens on the rendering thread. Takes ownership
+ // of the callback object.
+ void AskForThumbnail(RenderWidgetHost* renderer,
+ ThumbnailReadyCallback* callback,
+ gfx::Size size);
+
SkBitmap GetThumbnailForRenderer(RenderWidgetHost* renderer) const;
#ifdef UNIT_TEST
@@ -45,6 +64,11 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,
BackingStore* backing_store);
virtual void WidgetDidUpdateBackingStore(RenderWidgetHost* widget);
+ virtual void WidgetDidReceivePaintAtSizeAck(
+ RenderWidgetHost* widget,
+ const TransportDIB::Handle& dib_handle,
+ const gfx::Size& size);
+
// NotificationObserver interface.
virtual void Observe(NotificationType type,
const NotificationSource& source,
@@ -57,6 +81,10 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,
// Called when the given widget is destroyed.
void WidgetDestroyed(RenderWidgetHost* widget);
+ // Called when the given tab contents are disconnected (either
+ // through being closed, or because the renderer is no longer there).
+ void TabContentsDisconnected(TabContents* contents);
+
// Timer function called on a delay after a tab has been shown. It will
// invalidate the thumbnail for hosts with expired thumbnails in shown_hosts_.
void ShownDelayHandler();
@@ -76,6 +104,12 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,
// See the setter above.
bool no_timeout_;
+ // Map of callback objects by TransportDIB::Handle.
+ struct AsyncRequestInfo;
+ typedef std::map<TransportDIB::Handle,
+ linked_ptr<AsyncRequestInfo> > ThumbnailCallbackMap;
+ ThumbnailCallbackMap callback_map_;
+
DISALLOW_COPY_AND_ASSIGN(ThumbnailGenerator);
};
diff --git a/chrome/browser/tab_contents/thumbnail_generator_unittest.cc b/chrome/browser/tab_contents/thumbnail_generator_unittest.cc
index 38a40f0..787873c 100644
--- a/chrome/browser/tab_contents/thumbnail_generator_unittest.cc
+++ b/chrome/browser/tab_contents/thumbnail_generator_unittest.cc
@@ -11,8 +11,8 @@
#include "chrome/common/notification_service.h"
#include "chrome/common/render_messages.h"
#include "chrome/test/testing_profile.h"
-#include "testing/gtest/include/gtest/gtest.h"
#include "skia/ext/platform_canvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColorPriv.h"
static const int kBitmapWidth = 100;
@@ -142,7 +142,7 @@ TEST_F(ThumbnailGeneratorTest, DiscardBackingStore) {
SendPaint(TRANSPORT_BLACK);
widget_.WasHidden();
ASSERT_TRUE(BackingStoreManager::ExpireBackingStoreForTest(&widget_));
- ASSERT_FALSE(widget_.GetBackingStore(false));
+ ASSERT_FALSE(widget_.GetBackingStore(false, false));
// The thumbnail generator should have stashed a thumbnail of the page.
SkBitmap result = generator_.GetThumbnailForRenderer(&widget_);
@@ -156,7 +156,7 @@ TEST_F(ThumbnailGeneratorTest, QuickShow) {
SendPaint(TRANSPORT_BLACK);
widget_.WasHidden();
ASSERT_TRUE(BackingStoreManager::ExpireBackingStoreForTest(&widget_));
- ASSERT_FALSE(widget_.GetBackingStore(false));
+ ASSERT_FALSE(widget_.GetBackingStore(false, false));
// Now show the widget and paint white.
widget_.WasRestored();