From 67a46b7f6da205b7a1e772b7383bb20bab8ca611 Mon Sep 17 00:00:00 2001 From: "brettw@chromium.org" Date: Tue, 16 Jun 2009 21:41:02 +0000 Subject: Adds kind-of-live thumbnail generation for a potential tab switcher. This listens to tab events and tries to keep thumbnails ready to go. See thumbnail_generator.cc for a more detailed design. This adds a painting observer to the RenderWidgetHost to enable this new behavior, as well as a notification to allow the thumbnail generator to hook its observer in. There is also a new notification that a backing store has been disabled, which required making the backing stores know about their owning widget hosts. This component is currently disabled. We just need to uncomment the member in Profile and it will start to work. Original review: http://codereview.chromium.org/118420 Review URL: http://codereview.chromium.org/126101 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18540 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/DEPS | 4 +- chrome/browser/DEPS | 1 - chrome/browser/browser_process_impl.h | 11 + chrome/browser/profile.cc | 2 +- chrome/browser/renderer_host/backing_store.h | 30 +- chrome/browser/renderer_host/backing_store_mac.cc | 5 +- .../browser/renderer_host/backing_store_manager.cc | 51 +++- .../browser/renderer_host/backing_store_manager.h | 5 + chrome/browser/renderer_host/backing_store_win.cc | 5 +- chrome/browser/renderer_host/backing_store_x.cc | 38 ++- chrome/browser/renderer_host/render_widget_host.cc | 35 ++- chrome/browser/renderer_host/render_widget_host.h | 33 ++- .../render_widget_host_painting_observer.h | 24 ++ .../renderer_host/render_widget_host_unittest.cc | 16 +- .../renderer_host/render_widget_host_view_gtk.cc | 22 +- .../renderer_host/render_widget_host_view_gtk.h | 62 ++-- .../renderer_host/render_widget_host_view_mac.mm | 4 +- .../renderer_host/render_widget_host_view_win.cc | 4 +- .../browser/renderer_host/test_render_view_host.cc | 9 +- .../browser/renderer_host/test_render_view_host.h | 3 +- .../tab_contents/render_view_host_manager.cc | 8 + chrome/browser/tab_contents/thumbnail_generator.cc | 321 +++++++++++++++++++++ chrome/browser/tab_contents/thumbnail_generator.h | 75 +++++ .../tab_contents/thumbnail_generator_unittest.cc | 179 ++++++++++++ chrome/chrome.gyp | 3 + chrome/common/notification_type.h | 11 + chrome/common/property_bag.h | 2 +- chrome/common/transport_dib.h | 8 + chrome/common/transport_dib_linux.cc | 6 + chrome/common/transport_dib_mac.cc | 6 + chrome/common/transport_dib_win.cc | 5 + chrome/renderer/DEPS | 1 - chrome/renderer/render_process.cc | 17 +- chrome/test/unit/unittests.vcproj | 4 + 34 files changed, 872 insertions(+), 138 deletions(-) mode change 100644 => 100755 chrome/DEPS mode change 100644 => 100755 chrome/browser/DEPS create mode 100755 chrome/browser/renderer_host/render_widget_host_painting_observer.h create mode 100755 chrome/browser/tab_contents/thumbnail_generator.cc create mode 100755 chrome/browser/tab_contents/thumbnail_generator.h create mode 100755 chrome/browser/tab_contents/thumbnail_generator_unittest.cc mode change 100644 => 100755 chrome/common/transport_dib.h mode change 100644 => 100755 chrome/common/transport_dib_linux.cc mode change 100644 => 100755 chrome/common/transport_dib_mac.cc mode change 100644 => 100755 chrome/common/transport_dib_win.cc mode change 100644 => 100755 chrome/renderer/DEPS diff --git a/chrome/DEPS b/chrome/DEPS old mode 100644 new mode 100755 index 3c413c6..bb61606 --- a/chrome/DEPS +++ b/chrome/DEPS @@ -24,8 +24,8 @@ include_rules = [ # Allow usage of Google Toolbox for Mac. "+third_party/GTM", - # Brett's test. Contact him for questions. - "+frame_window", + # Our Skia extensions. + "+skia/ext", # On Linux, we include some breakpad headers "+breakpad/linux", diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS old mode 100644 new mode 100755 index 90a473f..a18a7a56 --- a/chrome/browser/DEPS +++ b/chrome/browser/DEPS @@ -8,7 +8,6 @@ include_rules = [ "+grit", # For generated headers "+sandbox/src", "+skia/include", - "+skia/ext", "+webkit/default_plugin", "+webkit/glue", # Defines some types that are marshalled over IPC. diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h index f056476..27ce62acd 100644 --- a/chrome/browser/browser_process_impl.h +++ b/chrome/browser/browser_process_impl.h @@ -18,6 +18,7 @@ #include "base/scoped_ptr.h" #include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/tab_contents/thumbnail_generator.h" #if defined(OS_WIN) #include "sandbox/src/sandbox.h" @@ -270,6 +271,16 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe { bool checked_for_new_frames_; bool using_new_frames_; +#if defined(LINUX2) + // TODO(brettw) enable this for all builds when we have a need for it. This + // component has some overhead, so we don't want to have it running without + // any consumers. Since it integrates by listening to notifications, it's + // sufficient to just not instatiate it to make it disabled. + + // This service just sits around and makes thumanails for tabs. + ThumbnailGenerator thumbnail_generator_; +#endif + // An event that notifies when we are shutting-down. scoped_ptr shutdown_event_; diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index b19fb2d7..3e8b196 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -13,6 +13,7 @@ #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extensions_service.h" @@ -29,7 +30,6 @@ #include "chrome/browser/spellchecker.h" #include "chrome/browser/ssl/ssl_host_state.h" #include "chrome/browser/thumbnail_store.h" -#include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/visitedlink_master.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/chrome_constants.h" diff --git a/chrome/browser/renderer_host/backing_store.h b/chrome/browser/renderer_host/backing_store.h index 5d817d0..0fb47ab 100644 --- a/chrome/browser/renderer_host/backing_store.h +++ b/chrome/browser/renderer_host/backing_store.h @@ -21,6 +21,7 @@ #endif class RenderWidgetHost; +class SkBitmap; class TransportDIB; // BackingStore ---------------------------------------------------------------- @@ -29,31 +30,31 @@ class TransportDIB; class BackingStore { public: #if defined(OS_WIN) || defined(OS_MACOSX) - explicit BackingStore(const gfx::Size& size); + BackingStore(RenderWidgetHost* widget, const gfx::Size& size); #elif defined(OS_LINUX) - // Create a backing store on the X server. - // size: the size of the server-side pixmap - // x_connection: the display to target - // depth: the depth of the X window which will be drawn into - // visual: An Xlib Visual describing the format of the target window - // root_window: The X id of the root window - // use_render: if true, the X server supports Xrender - // use_shared_memory: if true, the X server is local - BackingStore(const gfx::Size& size, Display* x_connection, int depth, - void* visual, XID root_window, bool use_render, - bool use_shared_memory); + // Create a backing store on the X server. The visual is an Xlib Visual + // describing the format of the target window and the depth is the color + // depth of the X window which will be drawn into. + BackingStore(RenderWidgetHost* widget, + const gfx::Size& size, + void* visual, + int depth); + // This is for unittesting only. An object constructed using this constructor // will silently ignore all paints - explicit BackingStore(const gfx::Size& size); + BackingStore(RenderWidgetHost* widget, const gfx::Size& size); #endif ~BackingStore(); + RenderWidgetHost* render_widget_host() const { return render_widget_host_; } const gfx::Size& size() { return size_; } #if defined(OS_WIN) HDC hdc() { return hdc_; } + #elif defined(OS_MACOSX) skia::PlatformCanvas* canvas() { return &canvas_; } + #elif defined(OS_LINUX) // Copy from the server-side backing store to the target window // display: the display of the backing store and target window @@ -77,6 +78,9 @@ class BackingStore { const gfx::Size& view_size); private: + // The owner of this backing store. + RenderWidgetHost* render_widget_host_; + // The size of the backing store. gfx::Size size_; diff --git a/chrome/browser/renderer_host/backing_store_mac.cc b/chrome/browser/renderer_host/backing_store_mac.cc index 569e3f6..2395a6c 100644 --- a/chrome/browser/renderer_host/backing_store_mac.cc +++ b/chrome/browser/renderer_host/backing_store_mac.cc @@ -10,8 +10,9 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" -BackingStore::BackingStore(const gfx::Size& size) - : size_(size) { +BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size) + : render_widget_host_(widget), + size_(size) { if (!canvas_.initialize(size.width(), size.height(), true)) SK_CRASH(); } diff --git a/chrome/browser/renderer_host/backing_store_manager.cc b/chrome/browser/renderer_host/backing_store_manager.cc index 3ff2e76..7b16a43 100644 --- a/chrome/browser/renderer_host/backing_store_manager.cc +++ b/chrome/browser/renderer_host/backing_store_manager.cc @@ -7,6 +7,7 @@ #include "base/sys_info.h" #include "chrome/browser/renderer_host/backing_store.h" #include "chrome/browser/renderer_host/render_widget_host.h" +#include "chrome/browser/renderer_host/render_widget_host_painting_observer.h" #include "chrome/common/chrome_constants.h" namespace { @@ -15,35 +16,59 @@ typedef OwningMRUCache BackingStoreCache; static BackingStoreCache* cache = NULL; // Returns the size of the backing store cache. -static int GetBackingStoreCacheSize() { +static size_t GetBackingStoreCacheSize() { // This uses a similar approach to GetMaxRendererProcessCount. The goal // is to reduce memory pressure and swapping on low-resource machines. - static const int kMaxDibCountByRamTier[] = { + static const size_t kMaxDibCountByRamTier[] = { 2, // less than 256MB 3, // 256MB 4, // 512MB 5 // 768MB and above }; - static int max_size = kMaxDibCountByRamTier[ + static size_t max_size = kMaxDibCountByRamTier[ std::min(base::SysInfo::AmountOfPhysicalMemoryMB() / 256, static_cast(arraysize(kMaxDibCountByRamTier)) - 1)]; return max_size; } +// Expires the given backing store from the cache. +void ExpireBackingStoreAt(BackingStoreCache::iterator backing_store) { + RenderWidgetHost* rwh = backing_store->second->render_widget_host(); + if (rwh->painting_observer()) { + rwh->painting_observer()->WidgetWillDestroyBackingStore( + backing_store->first, + backing_store->second); + } + cache->Erase(backing_store); +} + // Creates the backing store for the host based on the dimensions passed in. // Removes the existing backing store if there is one. BackingStore* CreateBackingStore(RenderWidgetHost* host, const gfx::Size& backing_store_size) { + // Remove any existing backing store in case we're replacing it. BackingStoreManager::RemoveBackingStore(host); + size_t max_cache_size = GetBackingStoreCacheSize(); + if (max_cache_size > 0 && !cache) + cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); + + if (cache->size() >= max_cache_size) { + // Need to remove an old backing store to make room for the new one. We + // don't want to do this when the backing store is being replace by a new + // one for the same tab, but this case won't get called then: we'll have + // removed the onld one in the RemoveBackingStore above, and the cache + // won't be over-sized. + // + // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(), + // so we need to do -- to move one back to the actual last item. + ExpireBackingStoreAt(--cache->rbegin().base()); + } + BackingStore* backing_store = host->AllocBackingStore(backing_store_size); - int backing_store_cache_size = GetBackingStoreCacheSize(); - if (backing_store_cache_size > 0) { - if (!cache) - cache = new BackingStoreCache(backing_store_cache_size); + if (max_cache_size > 0) cache->Put(host, backing_store); - } return backing_store; } @@ -110,7 +135,6 @@ void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) { BackingStoreCache::iterator it = cache->Peek(host); if (it == cache->end()) return; - cache->Erase(it); if (cache->empty()) { @@ -118,3 +142,12 @@ void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) { cache = NULL; } } + +// static +bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) { + BackingStoreCache::iterator it = cache->Peek(host); + if (it == cache->end()) + return false; + ExpireBackingStoreAt(it); + return true; +} diff --git a/chrome/browser/renderer_host/backing_store_manager.h b/chrome/browser/renderer_host/backing_store_manager.h index 5b929f6..3078a93 100644 --- a/chrome/browser/renderer_host/backing_store_manager.h +++ b/chrome/browser/renderer_host/backing_store_manager.h @@ -58,6 +58,11 @@ class BackingStoreManager { // Removes the backing store for the host. static void RemoveBackingStore(RenderWidgetHost* host); + // Expires the given backing store. This emulates something getting evicted + // from the cache for the purpose of testing. Returns true if the host was + // removed, false if it wasn't found. + static bool ExpireBackingStoreForTest(RenderWidgetHost* host); + private: // Not intended for instantiation. BackingStoreManager() {} diff --git a/chrome/browser/renderer_host/backing_store_win.cc b/chrome/browser/renderer_host/backing_store_win.cc index 1241e42..b3a1839 100644 --- a/chrome/browser/renderer_host/backing_store_win.cc +++ b/chrome/browser/renderer_host/backing_store_win.cc @@ -25,8 +25,9 @@ HANDLE CreateDIB(HDC dc, int width, int height, int color_depth) { // BackingStore (Windows) ------------------------------------------------------ -BackingStore::BackingStore(const gfx::Size& size) - : size_(size), +BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size) + : render_widget_host_(widget), + size_(size), backing_store_dib_(NULL), original_bitmap_(NULL) { HDC screen_dc = ::GetDC(NULL); diff --git a/chrome/browser/renderer_host/backing_store_x.cc b/chrome/browser/renderer_host/backing_store_x.cc index 6cb7c91..52fd7dc 100644 --- a/chrome/browser/renderer_host/backing_store_x.cc +++ b/chrome/browser/renderer_host/backing_store_x.cc @@ -24,41 +24,39 @@ // shared memory or over the wire, and XRENDER is used to convert them to the // correct format for the backing store. -BackingStore::BackingStore(const gfx::Size& size, - Display* display, - int depth, +BackingStore::BackingStore(RenderWidgetHost* widget, + const gfx::Size& size, void* visual, - Drawable root_window, - bool use_render, - bool use_shared_memory) - : size_(size), - display_(display), - use_shared_memory_(use_shared_memory), - use_render_(use_render), + int depth) + : render_widget_host_(widget), + size_(size), + display_(x11_util::GetXDisplay()), + use_shared_memory_(x11_util::QuerySharedMemorySupport(display_)), + use_render_(x11_util::QueryRenderSupport(display_)), visual_depth_(depth), - root_window_(root_window) { - const int width = size.width(); - const int height = size.height(); - + root_window_(x11_util::GetX11RootWindow()) { COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); - pixmap_ = XCreatePixmap(display_, root_window, width, height, depth); + pixmap_ = XCreatePixmap(display_, root_window_, + size.width(), size.height(), depth); if (use_render_) { picture_ = XRenderCreatePicture( display_, pixmap_, - x11_util::GetRenderVisualFormat(display_, static_cast(visual)), - 0, NULL); + x11_util::GetRenderVisualFormat(display_, + static_cast(visual)), + 0, NULL); } else { picture_ = 0; - pixmap_bpp_ = x11_util::BitsPerPixelForPixmapDepth(display, depth); + pixmap_bpp_ = x11_util::BitsPerPixelForPixmapDepth(display_, depth); } pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL); } -BackingStore::BackingStore(const gfx::Size& size) - : size_(size), +BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size) + : render_widget_host_(widget), + size_(size), display_(NULL), use_shared_memory_(false), use_render_(false), diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc index fccd45d..51d2b3b 100644 --- a/chrome/browser/renderer_host/render_widget_host.cc +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -11,6 +11,7 @@ #include "chrome/browser/renderer_host/backing_store_manager.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_widget_helper.h" +#include "chrome/browser/renderer_host/render_widget_host_painting_observer.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/common/notification_service.h" #include "chrome/common/render_messages.h" @@ -49,6 +50,7 @@ RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, : renderer_initialized_(false), view_(NULL), process_(process), + painting_observer_(NULL), routing_id_(routing_id), is_loading_(false), is_hidden_(false), @@ -142,6 +144,12 @@ void RenderWidgetHost::WasHidden() { // Tell the RenderProcessHost we were hidden. process_->WidgetHidden(); + + bool is_visible = false; + NotificationService::current()->Notify( + NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED, + Source(this), + Details(&is_visible)); } void RenderWidgetHost::WasRestored() { @@ -163,6 +171,12 @@ void RenderWidgetHost::WasRestored() { Send(new ViewMsg_WasRestored(routing_id_, needs_repainting)); process_->WidgetRestored(); + + bool is_visible = true; + NotificationService::current()->Notify( + NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED, + Source(this), + Details(&is_visible)); } void RenderWidgetHost::WasResized() { @@ -217,10 +231,13 @@ void RenderWidgetHost::SetIsLoading(bool is_loading) { view_->SetIsLoading(is_loading); } -BackingStore* RenderWidgetHost::GetBackingStore() { +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. - DCHECK(!is_hidden_) << "GetBackingStore called while hidden!"; + // then it means that our consumer failed to call WasRestored. If we're not + // force creating the backing store, it's OK since we can feel free to give + // out our cached one if we have it. + DCHECK(!is_hidden_ || !force_create) << + "GetBackingStore called while hidden!"; // We should never be called recursively; this can theoretically lead to // infinite recursion and almost certainly leads to lower performance. @@ -230,6 +247,11 @@ BackingStore* RenderWidgetHost::GetBackingStore() { // We might have a cached backing store that we can reuse! BackingStore* backing_store = BackingStoreManager::GetBackingStore(this, current_size_); + if (!force_create) { + in_get_backing_store_ = false; + return backing_store; + } + // If we fail to find a backing store in the cache, send out a request // to the renderer to paint the view if required. if (!backing_store && !repaint_ack_pending_ && !resize_ack_pending_ && @@ -259,7 +281,6 @@ BackingStore* RenderWidgetHost::GetBackingStore() { BackingStore* RenderWidgetHost::AllocBackingStore(const gfx::Size& size) { if (!view_) return NULL; - return view_->AllocBackingStore(size); } @@ -549,6 +570,9 @@ void RenderWidgetHost::OnMsgPaintRect( } } + if (painting_observer_) + painting_observer_->WidgetDidUpdateBackingStore(this); + // Log the time delta for processing a paint message. TimeDelta delta = TimeTicks::Now() - paint_start; UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgPaintRect", delta); @@ -595,6 +619,9 @@ void RenderWidgetHost::OnMsgScrollRect( view_being_painted_ = false; } + if (painting_observer_) + painting_observer_->WidgetDidUpdateBackingStore(this); + // Log the time delta for processing a scroll message. TimeDelta delta = TimeTicks::Now() - scroll_start; UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgScrollRect", delta); diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h index 0645496..c5913ef 100644 --- a/chrome/browser/renderer_host/render_widget_host.h +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -14,6 +14,7 @@ #include "base/timer.h" #include "chrome/common/ipc_channel.h" #include "chrome/common/native_web_keyboard_event.h" +#include "chrome/common/property_bag.h" #include "testing/gtest/include/gtest/gtest_prod.h" #include "webkit/glue/webtextdirection.h" @@ -31,6 +32,7 @@ class BackingStore; class PaintObserver; class RenderProcessHost; class RenderWidgetHostView; +class RenderWidgetHostPaintingObserver; class TransportDIB; class WebCursor; struct ViewHostMsg_PaintRect_Params; @@ -140,6 +142,22 @@ class RenderWidgetHost : public IPC::Channel::Listener { paint_observer_.reset(paint_observer); } + // Returns the property bag for this widget, where callers can add extra data + // they may wish to associate with it. Returns a pointer rather than a + // reference since the PropertyAccessors expect this. + const PropertyBag* property_bag() const { return &property_bag_; } + PropertyBag* property_bag() { return &property_bag_; } + + // The painting observer that will be called for paint events. This + // pointer's ownership will remain with the caller and must remain valid + // until this class is destroyed or the observer is replaced. + RenderWidgetHostPaintingObserver* painting_observer() const { + return painting_observer_; + } + void set_painting_observer(RenderWidgetHostPaintingObserver* observer) { + painting_observer_ = observer; + } + // Called when a renderer object already been created for this host, and we // just need to be attached to it. Used for window.open,