summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-28 23:48:08 +0000
committergspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-28 23:48:08 +0000
commit948f7ab76fa314b55380a5df35768ac10af49b05 (patch)
tree7b4f5addbb2f1950de917c6f417b2b50fb3b902f
parent074283adf1264cebfd1272afae558d2d7366a988 (diff)
downloadchromium_src-948f7ab76fa314b55380a5df35768ac10af49b05.zip
chromium_src-948f7ab76fa314b55380a5df35768ac10af49b05.tar.gz
chromium_src-948f7ab76fa314b55380a5df35768ac10af49b05.tar.bz2
This tunes up some of the snapshot creation code.
Includes: - Makes it so that the PaintAtSize message does what it should do: render the page at the size given and then scale (in the render process) the result to the desired size. - Changed some of the timeouts for updating snapshots to help with performance - Reduced the number of cases where we refresh snapshots in response to tabs changing. - Removed the need to force updates whenever we switch to overview mode, which makes it faster, and avoids the "shuffling" effect. - Started listening to TAB_CONTENTS_CONNECTED in order to detect new browsers, instead of BROWSER_WINDOW_READY, because there were still times when the tab contents didn't have any dimensions by the time BROWSER_WINDOW_READY was sent. This helps fix the bug referenced below. - Stopped updating snapshots in active mode. Now we just invalidate the snapshots and render them when we enter overview mode. - Added a parameter to indicate whether we prefer using the backing store or not when asking for snapshots from the thumbnail generator. - Added tracking of a timestamp for events from the window manager so we can avoid racing. TEST=ran in chromeos under WM and verified snapshot contents BUG=chromium-os:3142, 3136 Review URL: http://codereview.chromium.org/2098006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48544 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/frame/browser_view.cc1
-rw-r--r--chrome/browser/chromeos/wm_overview_controller.cc222
-rw-r--r--chrome/browser/chromeos/wm_overview_controller.h38
-rw-r--r--chrome/browser/chromeos/wm_overview_snapshot.cc6
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc10
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h19
-rw-r--r--chrome/browser/renderer_host/render_widget_host_unittest.cc3
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator.cc71
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator.h28
-rw-r--r--chrome/common/notification_type.h7
-rw-r--r--chrome/common/render_messages_internal.h15
-rw-r--r--chrome/renderer/render_widget.cc35
-rw-r--r--chrome/renderer/render_widget.h1
13 files changed, 304 insertions, 152 deletions
diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc
index b6fd133..7c954db 100644
--- a/chrome/browser/chromeos/frame/browser_view.cc
+++ b/chrome/browser/chromeos/frame/browser_view.cc
@@ -453,6 +453,7 @@ void BrowserView::Init() {
std::vector<int> params;
params.push_back(browser()->tab_count());
params.push_back(browser()->selected_index());
+ params.push_back(gtk_get_current_event_time());
WmIpc::instance()->SetWindowType(
GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()),
WM_IPC_WINDOW_CHROME_TOPLEVEL,
diff --git a/chrome/browser/chromeos/wm_overview_controller.cc b/chrome/browser/chromeos/wm_overview_controller.cc
index c62b932..943af95 100644
--- a/chrome/browser/chromeos/wm_overview_controller.cc
+++ b/chrome/browser/chromeos/wm_overview_controller.cc
@@ -13,6 +13,8 @@
#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/renderer_host/render_widget_host.h"
+#include "chrome/browser/renderer_host/render_widget_host_view.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"
@@ -31,6 +33,20 @@ using std::vector;
namespace chromeos {
+// The time that the delay timer waits before starting the
+// configuration pass.
+//
+// NOTE(gspencer): Yes, this is a pretty short delay, and I could
+// remove it and just use the configure timer. The reason I'm not
+// doing that is that I have a hunch that we'll be needing to tune
+// things further, and this will be one of the knobs.
+static const int kDelayTimeMs = 10;
+
+// The time between setting snapshots during the configuration pass,
+// so that the CPU has a chance to do something else (to keep overview
+// mode responsive).
+static const int kConfigureTimeMs = 10;
+
class BrowserListener : public TabStripModelObserver {
public:
BrowserListener(Browser* browser, WmOverviewController* parent);
@@ -64,6 +80,9 @@ class BrowserListener : public TabStripModelObserver {
// Removes all the snapshots and re-populates them from the browser.
void RecreateSnapshots();
+ // Mark the given snapshot as dirty, and start the delay timer.
+ void ReloadSnapshot(int index);
+
// Updates the selected index and tab count on the toplevel window.
void UpdateSelectedIndex(int index);
@@ -81,17 +100,16 @@ class BrowserListener : public TabStripModelObserver {
void RestoreOriginalSelectedTab();
// Selects the tab at the given index.
- void SelectTab(int index);
+ void SelectTab(int index, uint32 timestamp);
- // Shows any snapshots that are not visible, and updates their
- // bitmaps.
+ // Shows any snapshots that are not visible.
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);
}
+ private:
// Configures a cell from the tab contents.
void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents);
@@ -122,6 +140,11 @@ class BrowserListener : public TabStripModelObserver {
// True if the snapshots are showing.
bool snapshots_showing_;
+ // Non-zero if we are currently setting the tab from within SelectTab.
+ // This is used to make sure we use the right timestamp when sending
+ // property changes that originated from the window manager.
+ uint32 select_tab_timestamp_;
+
// The tab selected the last time SaveCurrentTab is called.
int original_selected_tab_;
@@ -133,10 +156,17 @@ BrowserListener::BrowserListener(Browser* browser,
: browser_(browser),
controller_(controller),
snapshots_showing_(false),
+ select_tab_timestamp_(0),
original_selected_tab_(-1) {
CHECK(browser_);
CHECK(controller_);
+
browser_->tabstrip_model()->AddObserver(this);
+
+ // This browser didn't already exist, and so we haven't been
+ // watching it for tab insertions, so we need to create the
+ // snapshots associated with it.
+ RecreateSnapshots();
}
BrowserListener::~BrowserListener() {
@@ -174,8 +204,10 @@ void BrowserListener::TabChangedAt(
TabContents* contents,
int index,
TabStripModelObserver::TabChangeType change_type) {
- snapshots_[index]->reload_snapshot();
- controller_->StartDelayTimer();
+ if (change_type != TabStripModelObserver::LOADING_ONLY &&
+ change_type != TabStripModelObserver::TITLE_NOT_LOADING) {
+ ReloadSnapshot(index);
+ }
}
void BrowserListener::TabStripEmpty() {
@@ -189,6 +221,11 @@ void BrowserListener::TabSelectedAt(TabContents* old_contents,
UpdateSelectedIndex(index);
}
+void BrowserListener::ReloadSnapshot(int index) {
+ snapshots_[index]->reload_snapshot();
+ controller_->StartDelayTimer();
+}
+
void BrowserListener::RecreateSnapshots() {
snapshots_.clear();
@@ -207,6 +244,8 @@ void BrowserListener::UpdateSelectedIndex(int index) {
std::vector<int> params;
params.push_back(browser_->tab_count());
params.push_back(index);
+ params.push_back(select_tab_timestamp_ ? select_tab_timestamp_ :
+ gtk_get_current_event_time());
WmIpc::instance()->SetWindowType(
GTK_WIDGET(browser_->window()->GetNativeHandle()),
WM_IPC_WINDOW_CHROME_TOPLEVEL,
@@ -218,7 +257,7 @@ 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));
+ ConfigureCell(cell, i);
return true;
}
}
@@ -234,43 +273,50 @@ void BrowserListener::RestoreOriginalSelectedTab() {
void BrowserListener::ShowSnapshots() {
for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
WmOverviewSnapshot* snapshot = snapshots_[i];
- snapshot->reload_snapshot();
- if (!snapshot->IsVisible()) {
+ if (!snapshot->IsVisible())
snapshot->Show();
- }
}
}
-void BrowserListener::SelectTab(int index) {
+void BrowserListener::SelectTab(int index, uint32 timestamp) {
+ uint32 old_value = select_tab_timestamp_;
+ select_tab_timestamp_ = timestamp;
browser_->SelectTabContentsAt(index, true);
+ select_tab_timestamp_ = old_value;
}
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());
- }
+ ThumbnailGenerator* generator =
+ g_browser_process->GetThumbnailGenerator();
+ // TODO: Make sure that if the cell gets deleted before the
+ // callback is called that it sticks around until it gets
+ // called. (some kind of "in flight" list that uses linked_ptr
+ // to make sure they don't actually get deleted?) Also, make
+ // sure that any request for a thumbnail eventually returns
+ // (even if it has bogus data), so we don't leak orphaned cells,
+ // which could happen if a tab is closed while it is being
+ // rendered.
+ ThumbnailGenerator::ThumbnailReadyCallback* callback =
+ NewCallback(cell, &WmOverviewSnapshot::SetImage);
+ gfx::Size cell_size = cell->size();
+
+ // Ask for the page size to be twice the requested size of the
+ // snapshot.
+ generator->AskForSnapshot(contents->render_view_host(),
+ false,
+ callback,
+ gfx::Size(cell_size.width() * 2,
+ cell_size.height() * 2),
+ 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());
- }
+ // configured_snapshot remains false and
+ // ConfigureNextUnconfiguredSnapshot would get stuck.
+ cell->SetImage(SkBitmap());
}
}
@@ -278,7 +324,7 @@ 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);
+ gfx::Size size(bounds.width() / 2, bounds.height() / 2);
snapshot->Init(size, browser_, index);
snapshots_.insert(snapshots_.begin() + index, snapshot);
snapshot->reload_snapshot();
@@ -292,12 +338,9 @@ void BrowserListener::ClearSnapshot(int 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)) {
+ if (snapshots_[i]->index() != static_cast<int>(i))
snapshots_[i]->UpdateIndex(browser_, i);
- changes++;
- }
}
}
@@ -314,8 +357,23 @@ WmOverviewController* WmOverviewController::instance() {
}
WmOverviewController::WmOverviewController()
- : allow_show_snapshots_(false) {
+ : layout_mode_(ACTIVE_MODE) {
AddAllBrowsers();
+
+ if (registrar_.IsEmpty()) {
+ // Make sure we get notifications for when the tab contents are
+ // connected, so we know when a new browser has been created.
+ registrar_.Add(this,
+ NotificationType::TAB_CONTENTS_CONNECTED,
+ NotificationService::AllSources());
+
+ // Ask for notification when the snapshot source image has changed
+ // and needs to be refreshed.
+ registrar_.Add(this,
+ NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED,
+ NotificationService::AllSources());
+ }
+
BrowserList::AddObserver(this);
WmMessageListener::instance()->AddObserver(this);
}
@@ -329,11 +387,49 @@ WmOverviewController::~WmOverviewController() {
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) {
- // This makes sure that the new listener is in the right order (to
- // match the order in the browser list).
- AddAllBrowsers();
+ switch (type.value) {
+ // Now that the tab contents are ready, we create the listeners
+ // and snapshots for any new browsers out there. This actually
+ // results in us traversing the list of browsers more often than
+ // necessary (whenever a tab is connected, as opposed to only when
+ // a new browser is created), but other notifications aren't
+ // sufficient to know when the first tab of a new browser has its
+ // dimensions set. The implementation of AddAllBrowsers avoids
+ // doing anything to already-existing browsers, so it's not a huge
+ // problem, but still, it would be nice if there were a more
+ // appropriate (browser-level) notification.
+ case NotificationType::TAB_CONTENTS_CONNECTED:
+ AddAllBrowsers();
+ break;
+
+ case NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED: {
+ // Don't do any dynamic updating if we're not in overview mode.
+ if (layout_mode_ == OVERVIEW_MODE) {
+ RenderWidgetHost* renderer = Details<RenderViewHost>(details).ptr();
+ SnapshotImageChanged(renderer);
+ }
+ break;
+ }
+ default:
+ // Do nothing.
+ break;
+ }
+}
+
+void WmOverviewController::SnapshotImageChanged(RenderWidgetHost* renderer) {
+ // Find out which TabContents this renderer is attached to, and then
+ // invalidate the associated snapshot so it'll update.
+ BrowserListenerVector::iterator iter = listeners_.begin();
+ while (iter != listeners_.end()) {
+ for (int i = 0; i < (*iter)->count(); i++) {
+ RenderWidgetHostView* view =
+ (*iter)->GetTabContentsAt(i)->GetRenderWidgetHostView();
+ if (view && view->GetRenderWidgetHost() == renderer) {
+ (*iter)->ReloadSnapshot(i);
+ return;
+ }
+ }
+ ++iter;
}
}
@@ -364,7 +460,8 @@ void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
GdkWindow* window) {
switch (message.type()) {
case WM_IPC_MESSAGE_CHROME_NOTIFY_LAYOUT_MODE: {
- if (message.param(0) == 0 || BrowserList::size() == 0) {
+ layout_mode_ = message.param(0) == 0 ? ACTIVE_MODE : OVERVIEW_MODE;
+ if (layout_mode_ == ACTIVE_MODE || BrowserList::size() == 0) {
Hide(message.param(1) != 0);
} else {
Show();
@@ -377,7 +474,9 @@ void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
for (BrowserListenerVector::iterator i = listeners_.begin();
i != listeners_.end(); ++i) {
if ((*i)->browser()->window() == browser_window) {
- (*i)->SelectTab(message.param(0));
+ // param(0): index of the tab to select.
+ // param(1): timestamp of the event.
+ (*i)->SelectTab(message.param(0), message.param(1));
break;
}
}
@@ -391,17 +490,13 @@ void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
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.
+ // snapshots. If we were to restart the delay timer, it could
+ // result in a very long delay until they get configured if tabs
+ // keep changing.
+ if (layout_mode_ == OVERVIEW_MODE &&
+ !delay_timer_.IsRunning() && !configure_timer_.IsRunning()) {
delay_timer_.Start(
- base::TimeDelta::FromMilliseconds(350), this,
+ base::TimeDelta::FromMilliseconds(kDelayTimeMs), this,
&WmOverviewController::StartConfiguring);
}
}
@@ -422,11 +517,15 @@ void WmOverviewController::SaveTabSelections() {
void WmOverviewController::Show() {
SaveTabSelections();
+
+ // Reset the timers.
+ configure_timer_.Stop();
+ delay_timer_.Stop();
+
for (BrowserListenerVector::iterator i = listeners_.begin();
i != listeners_.end(); ++i) {
(*i)->ShowSnapshots();
}
- allow_show_snapshots_ = false;
StartDelayTimer();
}
@@ -439,10 +538,9 @@ void WmOverviewController::Hide(bool cancelled) {
}
void WmOverviewController::StartConfiguring() {
- allow_show_snapshots_ = true;
configure_timer_.Stop();
configure_timer_.Start(
- base::TimeDelta::FromMilliseconds(10), this,
+ base::TimeDelta::FromMilliseconds(kConfigureTimeMs), this,
&WmOverviewController::ConfigureNextUnconfiguredSnapshot);
}
@@ -488,24 +586,14 @@ void WmOverviewController::AddAllBrowsers() {
}
}
- // This browser isn't owned by any listener, so create it.
+ // This browser isn't tracked 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
index b37f965..32d7538 100644
--- a/chrome/browser/chromeos/wm_overview_controller.h
+++ b/chrome/browser/chromeos/wm_overview_controller.h
@@ -21,6 +21,7 @@ class Widget;
class Animation;
class Browser;
+class RenderWidgetHost;
namespace chromeos {
@@ -47,6 +48,21 @@ class WmOverviewController : public BrowserList::Observer,
public WmMessageListener::Observer,
public NotificationObserver {
public:
+ // These are the possible layout modes that this controller can be
+ // in. The layout mode is controlled by the window manager.
+ enum LayoutMode {
+ // ACTIVE_MODE is the mode where chrome takes up the whole screen
+ // and the user interacts with it, and this controller hides the
+ // snapshots and stops refreshing them.
+ ACTIVE_MODE,
+
+ // OVERVIEW_MODE is the mode where the toplevel windows are hidden
+ // and the user interacts with the snapshots. This is when the
+ // snapshot windows are shown and actively updated by this
+ // controller.
+ OVERVIEW_MODE,
+ };
+
// This class is a singleton.
static WmOverviewController* instance();
@@ -73,15 +89,13 @@ class WmOverviewController : public BrowserList::Observer,
// 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();
+ LayoutMode layout_mode() const { return layout_mode_; }
+
private:
friend struct DefaultSingletonTraits<WmOverviewController>;
@@ -105,8 +119,7 @@ class WmOverviewController : public BrowserList::Observer,
// shown are restored.
void Hide(bool cancelled);
- // Invoked by delay_timer_. Sets allow_show_snapshots_ to true and starts
- // configure_timer_.
+ // Invoked by delay_timer_. Starts configure_timer_.
void StartConfiguring();
// Configure the next unconfigured snapshot window owned by any of
@@ -117,6 +130,11 @@ class WmOverviewController : public BrowserList::Observer,
// were already there.
void AddAllBrowsers();
+ // Called when the thumbnail generator notifies us that the snapshot
+ // image changed. This determines which TabContents the given
+ // renderer is attached to, and reloads that snapshot.
+ void SnapshotImageChanged(RenderWidgetHost* renderer);
+
// This is so we can register for notifications.
NotificationRegistrar registrar_;
@@ -128,17 +146,15 @@ class WmOverviewController : public BrowserList::Observer,
// 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_;
+ // The current layout mode.
+ LayoutMode layout_mode_;
+
DISALLOW_COPY_AND_ASSIGN(WmOverviewController);
};
diff --git a/chrome/browser/chromeos/wm_overview_snapshot.cc b/chrome/browser/chromeos/wm_overview_snapshot.cc
index 63c1e8a..6ee9cb7 100644
--- a/chrome/browser/chromeos/wm_overview_snapshot.cc
+++ b/chrome/browser/chromeos/wm_overview_snapshot.cc
@@ -36,12 +36,6 @@ 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(size));
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index 491aef7..45d1812 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -293,11 +293,13 @@ void RenderWidgetHost::SetIsLoading(bool is_loading) {
}
void RenderWidgetHost::PaintAtSize(TransportDIB::Handle dib_handle,
- const gfx::Size& size) {
+ const gfx::Size& page_size,
+ const gfx::Size& desired_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));
+ // hidden, being resized, redrawn, etc. It resizes the web widget
+ // to the page_size and then scales it to the desired_size.
+ Send(new ViewMsg_PaintAtSize(routing_id_, dib_handle,
+ page_size, desired_size));
}
BackingStore* RenderWidgetHost::GetBackingStore(bool force_create) {
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index 6498aa8..23ccbb8 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -209,14 +209,17 @@ class RenderWidgetHost : public IPC::Channel::Listener,
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);
+ // regardless of whether the tab is hidden or not. It resizes the
+ // web widget to match the |page_size| and then returns the bitmap
+ // scaled so it matches the |desired_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& page_size,
+ const gfx::Size& desired_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
diff --git a/chrome/browser/renderer_host/render_widget_host_unittest.cc b/chrome/browser/renderer_host/render_widget_host_unittest.cc
index b5fb071..b6e4326 100644
--- a/chrome/browser/renderer_host/render_widget_host_unittest.cc
+++ b/chrome/browser/renderer_host/render_widget_host_unittest.cc
@@ -536,7 +536,8 @@ TEST_F(RenderWidgetHostTest, HiddenPaint) {
}
TEST_F(RenderWidgetHostTest, PaintAtSize) {
- host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), gfx::Size(20, 30));
+ host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), gfx::Size(40, 60),
+ gfx::Size(20, 30));
EXPECT_TRUE(
process_->sink().GetUniqueMessageMatching(ViewMsg_PaintAtSize::ID));
diff --git a/chrome/browser/tab_contents/thumbnail_generator.cc b/chrome/browser/tab_contents/thumbnail_generator.cc
index 7650f88..a6f046c 100644
--- a/chrome/browser/tab_contents/thumbnail_generator.cc
+++ b/chrome/browser/tab_contents/thumbnail_generator.cc
@@ -68,7 +68,7 @@ static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
struct WidgetThumbnail {
SkBitmap thumbnail;
- // Indicates the last time the RWH was shown and hidden.
+ // Indicates the last time the RenderWidgetHost was shown and hidden.
base::TimeTicks last_shown;
base::TimeTicks last_hidden;
};
@@ -93,7 +93,9 @@ WidgetThumbnail* GetDataForHost(RenderWidgetHost* host) {
// Creates a downsampled thumbnail for the given backing store. The returned
// bitmap will be isNull if there was an error creating it.
-SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {
+SkBitmap GetBitmapForBackingStore(BackingStore* backing_store,
+ int desired_width,
+ int desired_height) {
base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
SkBitmap result;
@@ -107,14 +109,10 @@ SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {
return result;
const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false);
-#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);
+ // Need to resize it to the size we want, so downsample until it's
+ // close, and let the caller make it the exact size if desired.
+ result = SkBitmapOperations::DownsampleByTwoUntilSize(
+ bmp, desired_width, desired_height);
// This is a bit subtle. SkBitmaps are refcounted, but the magic
// ones in PlatformCanvas can't be assigned to SkBitmap with proper
@@ -125,7 +123,6 @@ SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {
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);
@@ -169,23 +166,33 @@ void ThumbnailGenerator::StartThumbnailing() {
}
}
-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;
+void ThumbnailGenerator::AskForSnapshot(RenderWidgetHost* renderer,
+ bool prefer_backing_store,
+ ThumbnailReadyCallback* callback,
+ gfx::Size page_size,
+ gfx::Size desired_size) {
+ if (prefer_backing_store) {
+ BackingStore* backing_store = renderer->GetBackingStore(false);
+ if (backing_store) {
+ // We were able to find a non-null backing store for this renderer, so
+ // we'll go with it.
+ SkBitmap first_try = GetBitmapForBackingStore(backing_store,
+ desired_size.width(),
+ desired_size.height());
+ callback->Run(first_try);
+
+ delete callback;
+ return;
+ }
+ // Now, if the backing store didn't exist, we will still try and
+ // render asynchronously.
}
// 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++);
+ desired_size.width() * desired_size.height() * 4, sequence_num++);
linked_ptr<AsyncRequestInfo> request_info(new AsyncRequestInfo);
request_info->callback.reset(callback);
request_info->thumbnail_dib.reset(thumbnail_dib);
@@ -199,7 +206,7 @@ void ThumbnailGenerator::AskForThumbnail(RenderWidgetHost* renderer,
return;
}
- renderer->PaintAtSize(thumbnail_dib->handle(), size);
+ renderer->PaintAtSize(thumbnail_dib->handle(), page_size, desired_size);
}
SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
@@ -226,7 +233,9 @@ SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
// Save this thumbnail in case we need to use it again soon. It will be
// invalidated on the next paint.
- wt->thumbnail = GetThumbnailForBackingStore(backing_store);
+ wt->thumbnail = GetBitmapForBackingStore(backing_store,
+ kThumbnailWidth,
+ kThumbnailHeight);
return wt->thumbnail;
}
@@ -245,13 +254,21 @@ void ThumbnailGenerator::WidgetWillDestroyBackingStore(
// Save a scaled-down image of the page in case we're asked for the thumbnail
// when there is no RenderViewHost. If this fails, we don't want to overwrite
// an existing thumbnail.
- SkBitmap new_thumbnail = GetThumbnailForBackingStore(backing_store);
+ SkBitmap new_thumbnail = GetBitmapForBackingStore(backing_store,
+ kThumbnailWidth,
+ kThumbnailHeight);
if (!new_thumbnail.isNull())
wt->thumbnail = new_thumbnail;
}
-void ThumbnailGenerator::WidgetDidUpdateBackingStore(
- RenderWidgetHost* widget) {
+void ThumbnailGenerator::WidgetDidUpdateBackingStore(RenderWidgetHost* widget) {
+ // Notify interested parties that they might want to update their
+ // snapshots.
+ NotificationService::current()->Notify(
+ NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED,
+ Source<ThumbnailGenerator>(this),
+ Details<RenderWidgetHost>(widget));
+
// Clear the current thumbnail since it's no longer valid.
WidgetThumbnail* wt = GetThumbnailAccessor()->GetProperty(
widget->property_bag());
diff --git a/chrome/browser/tab_contents/thumbnail_generator.h b/chrome/browser/tab_contents/thumbnail_generator.h
index 6398e17..f95e4f9 100644
--- a/chrome/browser/tab_contents/thumbnail_generator.h
+++ b/chrome/browser/tab_contents/thumbnail_generator.h
@@ -39,15 +39,29 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,
// 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
+ // from GetThumbnailForRenderer in that it may be 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);
-
+ // the thumbnail happens on the rendering thread.
+ //
+ // Takes ownership of the callback object.
+ //
+ // If |prefer_backing_store| is set, then the function will try and
+ // use the backing store for the page if it exists. |page_size| is
+ // the size to render the page, and |desired_size| is the size to
+ // scale the resulting rendered page to (which is done efficiently
+ // if done in the rendering thread). If |prefer_backing_store| is
+ // set, and the backing store is used, then the resulting image will
+ // be less then twice the size of the |desired_size| in both
+ // dimensions, but might not be the exact size requested.
+ void AskForSnapshot(RenderWidgetHost* renderer,
+ bool prefer_backing_store,
+ ThumbnailReadyCallback* callback,
+ gfx::Size page_size,
+ gfx::Size desired_size);
+
+ // This returns a thumbnail of a fixed, small size for the given
+ // renderer.
SkBitmap GetThumbnailForRenderer(RenderWidgetHost* renderer) const;
#ifdef UNIT_TEST
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 68a64b1..f5a52e7 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -602,6 +602,13 @@ class NotificationType {
// startup.
THUMBNAIL_STORE_READY,
+ // Sent by the ThumbnailGenerator whenever a render widget host
+ // updates its backing store. The source is the
+ // ThumbnailGenerator, and the details are the RenderWidgetHost
+ // that notified the ThumbnailGenerator that its backing store was
+ // updated.
+ THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED,
+
// Bookmarks ---------------------------------------------------------------
// Sent when the starred state of a URL changes. A URL is starred if there
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 5cf4697..d9a0a64 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -143,13 +143,16 @@ IPC_BEGIN_MESSAGES(View)
// JS garbage, not in purging irreplaceable objects.
IPC_MESSAGE_CONTROL0(ViewMsg_PurgeMemory)
- // Sent to render the view into the supplied transport DIB, scale it
- // by the appropriate scale to make it fit the given size, and
- // return it. In response to this message, the host generates a
- // ViewHostMsg_PaintAtSize_ACK message.
- IPC_MESSAGE_ROUTED2(ViewMsg_PaintAtSize,
+ // Sent to render the view into the supplied transport DIB, resize
+ // the web widget to match the |page_size|, scale it by the
+ // appropriate scale to make it fit the |desired_size|, and return
+ // it. In response to this message, the host generates a
+ // ViewHostMsg_PaintAtSize_ACK message. Note that the DIB *must* be
+ // the right size to receive an RGBA image at the |desired_size|.
+ IPC_MESSAGE_ROUTED3(ViewMsg_PaintAtSize,
TransportDIB::Handle /* dib_handle */,
- gfx::Size /* size */)
+ gfx::Size /* page_size */,
+ gfx::Size /* desired_size */)
// Tells the render view that a ViewHostMsg_UpdateRect message was processed.
// This signals the render view that it can send another UpdateRect message.
diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc
index e3d5390..c4656f7 100644
--- a/chrome/renderer/render_widget.cc
+++ b/chrome/renderer/render_widget.cc
@@ -752,16 +752,15 @@ void RenderWidget::OnImeSetComposition(WebCompositionCommand command,
ime_control_busy_ = false;
}
-// Forces a repaint even if we're hidden, so we can update backing
-// store even before a tab has been shown for the first time, and it
-// does it synchronously.
+// This message causes the renderer to render an image of the
+// desired_size, regardless of whether the tab is hidden or not.
void RenderWidget::OnMsgPaintAtSize(const TransportDIB::Handle& dib_handle,
+ const gfx::Size& page_size,
const gfx::Size& desired_size) {
if (!webwidget_ || dib_handle == TransportDIB::DefaultHandleValue())
return;
- if (webwidget_->size().isEmpty() ||
- desired_size.IsEmpty()) {
+ if (page_size.IsEmpty() || desired_size.IsEmpty()) {
// If one of these is empty, then we just return the dib we were
// given, to avoid leaking it.
Send(new ViewHostMsg_PaintAtSize_ACK(routing_id_,
@@ -772,16 +771,13 @@ void RenderWidget::OnMsgPaintAtSize(const TransportDIB::Handle& dib_handle,
// Map the given DIB ID into this process, and unmap it at the end
// of this function.
- scoped_ptr<TransportDIB> paint_at_scale_buffer(TransportDIB::Map(dib_handle));
+ scoped_ptr<TransportDIB> paint_at_size_buffer(TransportDIB::Map(dib_handle));
- DCHECK(paint_at_scale_buffer.get());
- if (!paint_at_scale_buffer.get())
+ DCHECK(paint_at_size_buffer.get());
+ if (!paint_at_size_buffer.get())
return;
- // Have to make sure we're laid out before rendering.
- webwidget_->layout();
-
- gfx::Size canvas_size = webwidget_->size();
+ gfx::Size canvas_size = page_size;
float x_scale = static_cast<float>(desired_size.width()) /
static_cast<float>(canvas_size.width());
float y_scale = static_cast<float>(desired_size.height()) /
@@ -793,8 +789,8 @@ void RenderWidget::OnMsgPaintAtSize(const TransportDIB::Handle& dib_handle,
gfx::Rect bounds(canvas_size);
scoped_ptr<skia::PlatformCanvas> canvas(
- paint_at_scale_buffer->GetPlatformCanvas(canvas_size.width(),
- canvas_size.height()));
+ paint_at_size_buffer->GetPlatformCanvas(canvas_size.width(),
+ canvas_size.height()));
if (!canvas.get()) {
NOTREACHED();
return;
@@ -808,13 +804,22 @@ void RenderWidget::OnMsgPaintAtSize(const TransportDIB::Handle& dib_handle,
bounds.set_height(canvas->getDevice()->height());
canvas->save();
- // Add the scale factor to the canvas, so that we'll get what we expect.
+ // Add the scale factor to the canvas, so that we'll get the desired size.
canvas->scale(SkFloatToScalar(x_scale), SkFloatToScalar(y_scale));
+ // Have to make sure we're laid out at the right size before
+ // rendering.
+ gfx::Size old_size = webwidget_->size();
+ webwidget_->resize(page_size);
+ webwidget_->layout();
+
// Paint the entire thing (using original bounds, not scaled bounds).
PaintRect(orig_bounds, orig_bounds.origin(), canvas.get());
canvas->restore();
+ // Return the widget to its previous size.
+ webwidget_->resize(old_size);
+
Send(new ViewHostMsg_PaintAtSize_ACK(routing_id_, dib_handle, bounds.size()));
}
diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h
index 4232d28..4086137 100644
--- a/chrome/renderer/render_widget.h
+++ b/chrome/renderer/render_widget.h
@@ -162,6 +162,7 @@ class RenderWidget : public IPC::Channel::Listener,
int target_start, int target_end,
const string16& ime_string);
void OnMsgPaintAtSize(const TransportDIB::Handle& dib_id,
+ const gfx::Size& page_size,
const gfx::Size& desired_size);
void OnMsgRepaint(const gfx::Size& size_to_paint);
void OnSetTextDirection(WebKit::WebTextDirection direction);