diff options
author | gspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-28 23:48:08 +0000 |
---|---|---|
committer | gspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-28 23:48:08 +0000 |
commit | 948f7ab76fa314b55380a5df35768ac10af49b05 (patch) | |
tree | 7b4f5addbb2f1950de917c6f417b2b50fb3b902f /chrome | |
parent | 074283adf1264cebfd1272afae558d2d7366a988 (diff) | |
download | chromium_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
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/chromeos/frame/browser_view.cc | 1 | ||||
-rw-r--r-- | chrome/browser/chromeos/wm_overview_controller.cc | 222 | ||||
-rw-r--r-- | chrome/browser/chromeos/wm_overview_controller.h | 38 | ||||
-rw-r--r-- | chrome/browser/chromeos/wm_overview_snapshot.cc | 6 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host.cc | 10 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host.h | 19 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_unittest.cc | 3 | ||||
-rw-r--r-- | chrome/browser/tab_contents/thumbnail_generator.cc | 71 | ||||
-rw-r--r-- | chrome/browser/tab_contents/thumbnail_generator.h | 28 | ||||
-rw-r--r-- | chrome/common/notification_type.h | 7 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 15 | ||||
-rw-r--r-- | chrome/renderer/render_widget.cc | 35 | ||||
-rw-r--r-- | chrome/renderer/render_widget.h | 1 |
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); |