diff options
Diffstat (limited to 'chrome/browser/render_widget_host.cc')
-rw-r--r-- | chrome/browser/render_widget_host.cc | 429 |
1 files changed, 299 insertions, 130 deletions
diff --git a/chrome/browser/render_widget_host.cc b/chrome/browser/render_widget_host.cc index 23cf9e1..8f48a51 100644 --- a/chrome/browser/render_widget_host.cc +++ b/chrome/browser/render_widget_host.cc @@ -54,85 +54,269 @@ static const int kHungRendererDelayMs = 10000; // RenderWidget::BackingStore RenderWidgetHost::BackingStore::BackingStore(const gfx::Size& size) - : size_(size) { + : size_(size), + backing_store_dib_(NULL), + renderer_bitmap_section_(NULL), + original_bitmap_(NULL) { HDC screen_dc = ::GetDC(NULL); hdc_ = CreateCompatibleDC(screen_dc); - int color_depth = GetDeviceCaps(screen_dc, BITSPIXEL); - - // Color depths less than 16 bpp require a palette to be specified in the - // BITMAPINFO structure passed to CreateDIBSection. Instead of creating - // the palette, we specify the desired color depth as 16 which allows the - // OS to come up with an approximation. Tested this with 8bpp. - if (color_depth < 16) - color_depth = 16; - - BITMAPINFOHEADER hdr = {0}; - gfx::CreateBitmapHeaderWithColorDepth(size.width(), size.height(), - color_depth, &hdr); - void* data = NULL; - HANDLE bitmap = CreateDIBSection(hdc_, reinterpret_cast<BITMAPINFO*>(&hdr), - 0, &data, NULL, 0); - SelectObject(hdc_, bitmap); - ::ReleaseDC(NULL, screen_dc); + ReleaseDC(NULL, screen_dc); } RenderWidgetHost::BackingStore::~BackingStore() { DCHECK(hdc_); - HBITMAP bitmap = - reinterpret_cast<HBITMAP>(GetCurrentObject(hdc_, OBJ_BITMAP)); DeleteDC(hdc_); - DeleteObject(bitmap); + + if (backing_store_dib_) { + DeleteObject(backing_store_dib_); + backing_store_dib_ = NULL; + } + + if (renderer_bitmap_section_) { + CloseHandle(renderer_bitmap_section_); + renderer_bitmap_section_ = NULL; + } } -/////////////////////////////////////////////////////////////////////////////// -// RenderWidget::BackingStoreCache +bool RenderWidgetHost::BackingStore::Refresh(HANDLE process, + HANDLE bitmap_section, + const gfx::Rect& bitmap_rect) { + // The bitmap received is valid only in the renderer process. + HANDLE valid_bitmap = + win_util::GetSectionFromProcess(bitmap_section, process, false); + if (!valid_bitmap) + return false; + + if (bitmap_rect.size() == size()) { + CreateDIBSectionBackedByRendererBitmap(bitmap_rect, valid_bitmap); + return true; + } + + if (!backing_store_dib_) { + backing_store_dib_ = CreateDIB(hdc_, size_.width(), size_.height(), true, + NULL); + DCHECK(backing_store_dib_ != NULL); + original_bitmap_ = SelectObject(hdc_, backing_store_dib_); + } + + // TODO(darin): protect against integer overflow + DWORD size = 4 * bitmap_rect.width() * bitmap_rect.height(); + void* backing_store_data = MapViewOfFile(valid_bitmap, FILE_MAP_READ, 0, 0, + size); + // These values are shared with gfx::PlatformDevice + BITMAPINFOHEADER hdr; + gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr); + // Account for a bitmap_rect that exceeds the bounds of our view + gfx::Rect view_rect(0, 0, size_.width(), size_.height()); + gfx::Rect paint_rect = view_rect.Intersect(bitmap_rect); -class RenderWidgetHost::BackingStoreCache { + StretchDIBits(hdc_, + paint_rect.x(), + paint_rect.y(), + paint_rect.width(), + paint_rect.height(), + 0, 0, // source x,y + paint_rect.width(), + paint_rect.height(), + backing_store_data, + reinterpret_cast<BITMAPINFO*>(&hdr), + DIB_RGB_COLORS, + SRCCOPY); + + UnmapViewOfFile(backing_store_data); + CloseHandle(valid_bitmap); + return true; +} + +void RenderWidgetHost::BackingStore::CreateDIBSectionBackedByRendererBitmap( + const gfx::Rect& bitmap_rect, HANDLE bitmap_section_from_renderer) { + if (backing_store_dib_ != NULL) { + SelectObject(hdc_, original_bitmap_); + DeleteObject(backing_store_dib_); + backing_store_dib_ = NULL; + } + + if (renderer_bitmap_section_ != NULL) { + CloseHandle(renderer_bitmap_section_); + renderer_bitmap_section_ = NULL; + } + + backing_store_dib_ = CreateDIB(hdc_, bitmap_rect.width(), + bitmap_rect.height(), false, + bitmap_section_from_renderer); + DCHECK(backing_store_dib_ != NULL); + + renderer_bitmap_section_ = bitmap_section_from_renderer; + original_bitmap_ = SelectObject(hdc_, backing_store_dib_); +} + +HANDLE RenderWidgetHost::BackingStore::CreateDIB(HDC dc, int width, int height, + bool use_system_color_depth, + HANDLE section) { + BITMAPINFOHEADER hdr; + + if (use_system_color_depth) { + HDC screen_dc = ::GetDC(NULL); + int color_depth = GetDeviceCaps(screen_dc, BITSPIXEL); + ::ReleaseDC(NULL, screen_dc); + + // Color depths less than 16 bpp require a palette to be specified in the + // BITMAPINFO structure passed to CreateDIBSection. Instead of creating + // the palette, we specify the desired color depth as 16 which allows the + // OS to come up with an approximation. Tested this with 8bpp. + if (color_depth < 16) + color_depth = 16; + + gfx::CreateBitmapHeaderWithColorDepth(width, height, color_depth, &hdr); + } else { + gfx::CreateBitmapHeader(width, height, &hdr); + } + void* data = NULL; + HANDLE dib = + CreateDIBSection(hdc_, reinterpret_cast<BITMAPINFO*>(&hdr), + 0, &data, section, 0); + return dib; +} + + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHost::BackingStoreManager + +// This class manages backing stores in the browsr. Every RenderWidgetHost +// is associated with a backing store which it requests from this class. +// The hosts don't maintain any references to the backing stores. +// These backing stores are maintained in a cache which can be trimmed as +// needed. +class RenderWidgetHost::BackingStoreManager { public: - // The number of backing stores to cache. - enum { kMaxSize = 5 }; + // Returns a backing store which matches the desired dimensions. + // Parameters: + // host + // A pointer to the RenderWidgetHost. + // backing_store_rect + // The desired backing store dimensions. + // Returns a pointer to the backing store on success, NULL on failure. + static BackingStore* GetBackingStore(RenderWidgetHost* host, + const gfx::Size& desired_size) { + BackingStore* backing_store = Lookup(host); + if (backing_store) { + // If we already have a backing store, then make sure it is the correct + // size. + if (backing_store->size() == desired_size) + return backing_store; + backing_store = NULL; + } - static void Add(RenderWidgetHost* host, BackingStore* backing_store) { - // TODO(darin): Re-enable once we have a fix for bug 1143208. - if (!cache_) - cache_ = new Cache(kMaxSize); - cache_->Put(host, backing_store); + return backing_store; } - static BackingStore* Remove(RenderWidgetHost* host) { - // TODO(darin): Re-enable once we have a fix for bug 1143208. + // Returns a backing store which is fully ready for consumption, + // i.e. the bitmap from the renderer has been copied into the + // backing store dc, or the bitmap in the backing store dc references + // the renderer bitmap. + // Parameters: + // host + // A pointer to the RenderWidgetHost. + // backing_store_rect + // The desired backing store dimensions. + // process_handle + // The renderer process handle. + // bitmap_section + // The bitmap section from the renderer. + // bitmap_rect + // The rect to be painted into the backing store + // needs_full_paint + // Set if we need to send out a request to paint the view + // to the renderer. + static BackingStore* PrepareBackingStore(RenderWidgetHost* host, + const gfx::Rect& backing_store_rect, + HANDLE process_handle, + HANDLE bitmap_section, + const gfx::Rect& bitmap_rect, + bool* needs_full_paint) { + BackingStore* backing_store = GetBackingStore(host, + backing_store_rect.size()); + if (!backing_store) { + // We need to get Webkit to generate a new paint here, as we + // don't have a previous snapshot. + if (bitmap_rect != backing_store_rect) { + DCHECK(needs_full_paint != NULL); + *needs_full_paint = true; + } + backing_store = CreateBackingStore(host, backing_store_rect); + } + + DCHECK(backing_store != NULL); + backing_store->Refresh(process_handle, bitmap_section, bitmap_rect); + return backing_store; + } + + // Returns a matching backing store for the host. + // Returns NULL if we fail to find one. + static BackingStore* Lookup(RenderWidgetHost* host) { + if (cache_) { + BackingStoreCache::iterator it = cache_->Peek(host); + if (it != cache_->end()) + return it->second; + } + return NULL; + } + + // Removes the backing store for the host. + static void RemoveBackingStore(RenderWidgetHost* host) { if (!cache_) - return NULL; + return; - Cache::iterator it = cache_->Peek(host); + BackingStoreCache::iterator it = cache_->Peek(host); if (it == cache_->end()) - return NULL; - - BackingStore* result = it->second; + return; - // Remove from the cache without deleting the backing store object. - it->second = NULL; cache_->Erase(it); - if (cache_->size() == 0) { + if (cache_->empty()) { delete cache_; cache_ = NULL; } - - return result; } private: - ~BackingStoreCache(); // not intended to be instantiated + // Not intended for instantiation. + ~BackingStoreManager(); + + typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; + static BackingStoreCache* cache_; + + // Returns the size of the backing store cache. + // TODO(iyengar) Make this dynamic, i.e. based on the available resources + // on the machine. + static int GetBackingStoreCacheSize() { + const int kMaxSize = 5; + return kMaxSize; + } - typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> Cache; - static Cache* cache_; + // Creates the backing store for the host based on the dimensions passed in. + // Removes the existing backing store if there is one. + static BackingStore* CreateBackingStore( + RenderWidgetHost* host, const gfx::Rect& backing_store_rect) { + RemoveBackingStore(host); + + BackingStore* backing_store = new BackingStore(backing_store_rect.size()); + int backing_store_cache_size = GetBackingStoreCacheSize(); + if (backing_store_cache_size > 0) { + if (!cache_) + cache_ = new BackingStoreCache(backing_store_cache_size); + cache_->Put(host, backing_store); + } + return backing_store; + } + + DISALLOW_EVIL_CONSTRUCTORS(BackingStoreManager); }; -// static -RenderWidgetHost::BackingStoreCache::Cache* - RenderWidgetHost::BackingStoreCache::cache_ = NULL; +RenderWidgetHost::BackingStoreManager::BackingStoreCache* + RenderWidgetHost::BackingStoreManager::cache_ = NULL; + /////////////////////////////////////////////////////////////////////////////// // RenderWidgetHost @@ -148,7 +332,9 @@ RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, int routing_id) suppress_view_updating_(false), needs_repainting_on_restore_(false), hung_renderer_factory_(this), - is_unresponsive_(false) { + is_unresponsive_(false), + view_being_painted_(false), + repaint_ack_pending_(false) { if (routing_id_ == MSG_ROUTING_NONE) routing_id_ = process_->widget_helper()->GetNextRoutingID(); @@ -160,7 +346,7 @@ RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, int routing_id) RenderWidgetHost::~RenderWidgetHost() { // Clear our current or cached backing store if either remains. - backing_store_.reset(BackingStoreCache::Remove(this)); + BackingStoreManager::RemoveBackingStore(this); process_->Release(routing_id_); } @@ -230,21 +416,39 @@ void RenderWidgetHost::OnMsgPaintRect( resize_ack_pending_ = false; } + bool is_repaint_ack = + ViewHostMsg_PaintRect_Flags::is_repaint_ack(params.flags); + if (is_repaint_ack) { + repaint_ack_pending_ = false; + TimeDelta delta = TimeTicks::Now() - repaint_start_time_; + UMA_HISTOGRAM_TIMES(L"MPArch.RWH_RepaintDelta", delta); + } + DCHECK(params.bitmap); DCHECK(!params.bitmap_rect.IsEmpty()); DCHECK(!params.view_size.IsEmpty()); PaintRect(params.bitmap, params.bitmap_rect, params.view_size); + bool using_renderer_bitmap_section = false; + BackingStore* backing_store = BackingStoreManager::Lookup(this); + if (backing_store) { + using_renderer_bitmap_section = + backing_store->using_renderer_bitmap_section(); + } + // ACK early so we can prefetch the next PaintRect if there is a next one. - Send(new ViewMsg_PaintRect_ACK(routing_id_)); + Send(new ViewMsg_PaintRect_ACK(routing_id_, using_renderer_bitmap_section)); // TODO(darin): This should really be done by the view_! MovePluginWindows(params.plugin_window_moves); // The view might be destroyed already. Check for this case. - if (view_ && !suppress_view_updating_) + if (view_ && !suppress_view_updating_) { + view_being_painted_ = true; view_->DidPaintRect(params.bitmap_rect); + view_being_painted_ = false; + } if (paint_observer_.get()) paint_observer_->RenderWidgetHostDidPaint(this); @@ -279,8 +483,11 @@ void RenderWidgetHost::OnMsgScrollRect( MovePluginWindows(params.plugin_window_moves); // The view might be destroyed already. Check for this case - if (view_) + if (view_) { + view_being_painted_ = true; view_->DidScrollRect(params.clip_rect, params.dx, params.dy); + view_being_painted_ = false; + } // Log the time delta for processing a scroll message. TimeDelta delta = TimeTicks::Now() - scroll_start; @@ -398,10 +605,6 @@ void RenderWidgetHost::WasHidden() { // reduce its resource utilization. Send(new ViewMsg_WasHidden(routing_id_)); - // Yield the backing store (allows this memory to be freed). - if (backing_store_.get()) - BackingStoreCache::Add(this, backing_store_.release()); - // TODO(darin): what about constrained windows? it doesn't look like they // see a message when their parent is hidden. maybe there is something more // generic we can do at the TabContents API level instead of relying on @@ -417,13 +620,11 @@ void RenderWidgetHost::WasRestored() { return; is_hidden_ = false; - DCHECK(!backing_store_.get()); - backing_store_.reset(BackingStoreCache::Remove(this)); - + BackingStore* backing_store = BackingStoreManager::Lookup(this); // If we already have a backing store for this widget, then we don't need to // repaint on restore _unless_ we know that our backing store is invalid. bool needs_repainting; - if (needs_repainting_on_restore_ || !backing_store_.get()) { + if (needs_repainting_on_restore_ || !backing_store) { needs_repainting = true; needs_repainting_on_restore_ = false; } else { @@ -569,13 +770,21 @@ RenderWidgetHost::BackingStore* RenderWidgetHost::GetBackingStore() { DCHECK(!is_hidden_) << "GetBackingStore called while hidden!"; // We might have a cached backing store that we can reuse! - if (!backing_store_.get()) - backing_store_.reset(BackingStoreCache::Remove(this)); + BackingStore* backing_store = + BackingStoreManager::GetBackingStore(this, current_size_); + // 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_ && + !view_being_painted_) { + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + Send(new ViewMsg_Repaint(routing_id_, current_size_)); + } // When we have asked the RenderWidget to resize, and we are still waiting on // a response, block for a little while to see if we can't get a response // before returning the old (incorrectly sized) backing store. - if (resize_ack_pending_ || !backing_store_.get()) { + if (resize_ack_pending_ || !backing_store) { IPC::Message msg; TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS); if (process_->widget_helper()->WaitForPaintMsg(routing_id_, max_delay, @@ -584,53 +793,11 @@ RenderWidgetHost::BackingStore* RenderWidgetHost::GetBackingStore() { ViewHostMsg_PaintRect::Dispatch( &msg, this, &RenderWidgetHost::OnMsgPaintRect); suppress_view_updating_ = false; + backing_store = BackingStoreManager::GetBackingStore(this, current_size_); } } - return backing_store_.get(); -} - -void RenderWidgetHost::EnsureBackingStore(const gfx::Rect& view_rect) { - // We might have a cached backing store that we can reuse! - if (!backing_store_.get()) - backing_store_.reset(BackingStoreCache::Remove(this)); - - // If we already have a backing store, then make sure it is the correct size. - if (backing_store_.get() && backing_store_->size() == view_rect.size()) - return; - - backing_store_.reset(new BackingStore(view_rect.size())); -} - -void RenderWidgetHost::PaintBackingStore(HANDLE bitmap, - const gfx::Rect& bitmap_rect) { - // TODO(darin): protect against integer overflow - DWORD size = 4 * bitmap_rect.width() * bitmap_rect.height(); - void* data = MapViewOfFile(bitmap, FILE_MAP_READ, 0, 0, size); - - // These values are shared with gfx::PlatformDevice - BITMAPINFOHEADER hdr; - gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr); - - // Account for a bitmap_rect that exceeds the bounds of our view - gfx::Rect view_rect( - 0, 0, backing_store_->size().width(), backing_store_->size().height()); - gfx::Rect paint_rect = view_rect.Intersect(bitmap_rect); - - StretchDIBits(backing_store_->dc(), - paint_rect.x(), - paint_rect.y(), - paint_rect.width(), - paint_rect.height(), - 0, 0, // source x,y - paint_rect.width(), - paint_rect.height(), - data, - reinterpret_cast<BITMAPINFO*>(&hdr), - DIB_RGB_COLORS, - SRCCOPY); - - UnmapViewOfFile(data); + return backing_store; } void RenderWidgetHost::PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, @@ -640,18 +807,21 @@ void RenderWidgetHost::PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, return; } - // The bitmap received is valid only in the renderer process. - HANDLE valid_bitmap = - win_util::GetSectionFromProcess(bitmap, process_->process(), true); - if (valid_bitmap) { - // We use the view size according to the render view, which may not be - // quite the same as the size of our window. - gfx::Rect view_rect(0, 0, view_size.width(), view_size.height()); - - EnsureBackingStore(view_rect); - - PaintBackingStore(valid_bitmap, bitmap_rect); - CloseHandle(valid_bitmap); + // We use the view size according to the render view, which may not be + // quite the same as the size of our window. + gfx::Rect view_rect(0, 0, view_size.width(), view_size.height()); + + bool needs_full_paint = false; + BackingStore* backing_store = + BackingStoreManager::PrepareBackingStore(this, view_rect, + process_->process(), + bitmap, bitmap_rect, + &needs_full_paint); + DCHECK(backing_store != NULL); + if (needs_full_paint) { + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + Send(new ViewMsg_Repaint(routing_id_, view_size)); } } @@ -666,11 +836,12 @@ void RenderWidgetHost::ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, // TODO(darin): do we need to do something else if our backing store is not // the same size as the advertised view? maybe we just assume there is a // full paint on its way? - if (backing_store_->size() != view_size) + BackingStore* backing_store = BackingStoreManager::Lookup(this); + if (backing_store && backing_store->size() != view_size) return; RECT damaged_rect, r = clip_rect.ToRECT(); - ScrollDC(backing_store_->dc(), dx, dy, NULL, &r, NULL, &damaged_rect); + ScrollDC(backing_store->dc(), dx, dy, NULL, &r, NULL, &damaged_rect); // TODO(darin): this doesn't work if dx and dy are both non-zero! DCHECK(dx == 0 || dy == 0); @@ -678,13 +849,7 @@ void RenderWidgetHost::ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, // We expect that damaged_rect should equal bitmap_rect. DCHECK(gfx::Rect(damaged_rect) == bitmap_rect); - // The bitmap handle is valid only in the renderer process - HANDLE valid_bitmap = - win_util::GetSectionFromProcess(bitmap, process_->process(), true); - if (valid_bitmap) { - PaintBackingStore(valid_bitmap, bitmap_rect); - CloseHandle(valid_bitmap); - } + backing_store->Refresh(process_->process(), bitmap, bitmap_rect); } void RenderWidgetHost::RestartHangMonitorTimeout() { @@ -702,3 +867,7 @@ void RenderWidgetHost::StartHangMonitorTimeout(int delay) { hung_renderer_factory_.NewRunnableMethod( &RenderWidgetHost::RendererIsUnresponsive), delay); } + +void RenderWidgetHost::RendererExited() { + BackingStoreManager::RemoveBackingStore(this); +} |