// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/render_widget_host.h"

#include "base/gfx/bitmap_header.h"
#include "base/message_loop.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/render_process_host.h"
#include "chrome/browser/render_widget_helper.h"
#include "chrome/browser/render_widget_host_view.h"
#include "chrome/common/mru_cache.h"
#include "chrome/common/win_util.h"
#include "chrome/views/view.h"
#include "webkit/glue/webcursor.h"
#include "webkit/glue/webinputevent.h"

// How long to (synchronously) wait for the renderer to respond with a
// PaintRect message, when our backing-store is invalid, before giving up and
// returning a null or incorrectly sized backing-store from GetBackingStore.
// This timeout impacts the "choppiness" of our window resize perf.
static const int kPaintMsgTimeoutMS = 40;

// How long to wait before we consider a renderer hung.
static const int kHungRendererDelayMs = 20000;

///////////////////////////////////////////////////////////////////////////////
// RenderWidget::BackingStore

RenderWidgetHost::BackingStore::BackingStore(const gfx::Size& size)
    : size_(size),
      backing_store_dib_(NULL),
      original_bitmap_(NULL) {
  HDC screen_dc = ::GetDC(NULL);
  hdc_ = CreateCompatibleDC(screen_dc);
  ReleaseDC(NULL, screen_dc);
}

RenderWidgetHost::BackingStore::~BackingStore() {
  DCHECK(hdc_);

  DeleteDC(hdc_);

  if (backing_store_dib_) {
    DeleteObject(backing_store_dib_);
    backing_store_dib_ = NULL;
  }
}

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 (!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);

  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;
}

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:
  // 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;
    }

    return backing_store;
  }

  // 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;

    BackingStoreCache::iterator it = cache_->Peek(host);
    if (it == cache_->end())
      return;

    cache_->Erase(it);

    if (cache_->empty()) {
      delete cache_;
      cache_ = NULL;
    }
  }

 private:
   // 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;
  }

  // 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);
};

RenderWidgetHost::BackingStoreManager::BackingStoreCache* 
    RenderWidgetHost::BackingStoreManager::cache_ = NULL;


///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHost

RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, int routing_id)
    : process_(process),
      routing_id_(routing_id),
      resize_ack_pending_(false),
      mouse_move_pending_(false),
      view_(NULL),
      is_loading_(false),
      is_hidden_(false),
      suppress_view_updating_(false),
      needs_repainting_on_restore_(false),
      hung_renderer_factory_(this),
      is_unresponsive_(false),
      view_being_painted_(false),
      repaint_ack_pending_(false) {
  if (routing_id_ == MSG_ROUTING_NONE)
    routing_id_ = process_->widget_helper()->GetNextRoutingID();

  process_->Attach(this, routing_id_);
  // Because the widget initializes as is_hidden_ == false,
  // tell the process host that we're alive.
  process_->WidgetRestored();
}

RenderWidgetHost::~RenderWidgetHost() {
  // Clear our current or cached backing store if either remains.
  BackingStoreManager::RemoveBackingStore(this);

  process_->Release(routing_id_);
}

void RenderWidgetHost::Init() {
  DCHECK(process_->channel());

  // Send the ack along with the information on placement.
  HWND plugin_hwnd = view_->GetPluginHWND();
  Send(new ViewMsg_CreatingNew_ACK(routing_id_, plugin_hwnd));

  WasResized();
}

///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHost, protected:

IPC_DEFINE_MESSAGE_MAP(RenderWidgetHost)
  IPC_MESSAGE_HANDLER(ViewHostMsg_RendererReady, OnMsgRendererReady)
  IPC_MESSAGE_HANDLER(ViewHostMsg_RendererGone, OnMsgRendererGone)
  IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose)
  IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove)
  IPC_MESSAGE_HANDLER(ViewHostMsg_PaintRect, OnMsgPaintRect)
  IPC_MESSAGE_HANDLER(ViewHostMsg_ScrollRect, OnMsgScrollRect)
  IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnMsgInputEventAck)
  IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnMsgFocus)
  IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnMsgBlur)
  IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnMsgSetCursor)
  IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateStatus, OnMsgImeUpdateStatus)
  IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP()

void RenderWidgetHost::OnMsgRendererReady() {
  WasResized();
}

void RenderWidgetHost::OnMsgRendererGone() {
  // TODO(evanm): This synchronously ends up calling "delete this".
  // Is that really what we want in response to this message?  I'm matching
  // previous behavior of the code here.
  Destroy();
}

void RenderWidgetHost::OnMsgClose() {
  Shutdown();
}

void RenderWidgetHost::OnMsgRequestMove(const gfx::Rect& pos) {
  // Don't allow renderer widgets to move themselves by default.  Maybe this
  // policy will change if we add more types of widgets.
}

void RenderWidgetHost::OnMsgPaintRect(
    const ViewHostMsg_PaintRect_Params& params) {
  TimeTicks paint_start = TimeTicks::Now();

  // Update our knowledge of the RenderWidget's size.
  current_size_ = params.view_size;

  bool is_resize_ack =
      ViewHostMsg_PaintRect_Flags::is_resize_ack(params.flags);

  // resize_ack_pending_ needs to be cleared before we call DidPaintRect, since
  // that will end up reaching GetBackingStore.
  if (is_resize_ack) {
    DCHECK(resize_ack_pending_);
    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);

  // ACK early so we can prefetch the next PaintRect if there is a next one.
  Send(new ViewMsg_PaintRect_ACK(routing_id_));

  // 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_) {
    view_being_painted_ = true;
    view_->DidPaintRect(params.bitmap_rect);
    view_being_painted_ = false;
  }

  if (paint_observer_.get())
    paint_observer_->RenderWidgetHostDidPaint(this);

  // If we got a resize ack, then perhaps we have another resize to send?
  if (is_resize_ack && view_) {
    gfx::Rect view_bounds = view_->GetViewBounds();
    if (current_size_.width() != view_bounds.width() ||
        current_size_.height() != view_bounds.height()) {
      WasResized();
    }
  }

  // Log the time delta for processing a paint message.
  TimeDelta delta = TimeTicks::Now() - paint_start;
  UMA_HISTOGRAM_TIMES(L"MPArch.RWH_OnMsgPaintRect", delta);
}

void RenderWidgetHost::OnMsgScrollRect(
    const ViewHostMsg_ScrollRect_Params& params) {
  TimeTicks scroll_start = TimeTicks::Now();

  DCHECK(!params.view_size.IsEmpty());

  ScrollRect(params.bitmap, params.bitmap_rect, params.dx, params.dy,
             params.clip_rect, params.view_size);

  // ACK early so we can prefetch the next ScrollRect if there is a next one.
  Send(new ViewMsg_ScrollRect_ACK(routing_id_));

  // 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_) {
    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;
  UMA_HISTOGRAM_TIMES(L"MPArch.RWH_OnMsgScrollRect", delta);
}

void RenderWidgetHost::MovePluginWindows(
    const std::vector<WebPluginGeometry>& plugin_window_moves) {
  HDWP defer_window_pos_info =
      ::BeginDeferWindowPos(static_cast<int>(plugin_window_moves.size()));

  if (!defer_window_pos_info) {
    NOTREACHED();
    return;
  }

  for (size_t i = 0; i < plugin_window_moves.size(); ++i) {
    unsigned long flags = 0;
    const WebPluginGeometry& move = plugin_window_moves[i];

    if (move.visible)
      flags |= SWP_SHOWWINDOW;
    else
      flags |= SWP_HIDEWINDOW;

    HRGN hrgn = ::CreateRectRgn(move.clip_rect.x(),
                                move.clip_rect.y(),
                                move.clip_rect.right(),
                                move.clip_rect.bottom());

    // Note: System will own the hrgn after we call SetWindowRgn,
    // so we don't need to call DeleteObject(hrgn)
    ::SetWindowRgn(move.window, hrgn, !move.clip_rect.IsEmpty());

    defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info,
                                             move.window, NULL,
                                             move.window_rect.x(),
                                             move.window_rect.y(),
                                             move.window_rect.width(),
                                             move.window_rect.height(), flags);
    if (!defer_window_pos_info) {
      return;
    }
  }

  ::EndDeferWindowPos(defer_window_pos_info);
}

void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) {
  // Log the time delta for processing an input event.
  TimeDelta delta = TimeTicks::Now() - input_event_start_time_;
  UMA_HISTOGRAM_TIMES(L"MPArch.RWH_InputEventDelta", delta);

  // Cancel pending hung renderer checks since the renderer is responsive.
  StopHangMonitorTimeout();

  void* iter = NULL;
  int type = 0;
  bool r = message.ReadInt(&iter, &type);
  DCHECK(r);

  if (type == WebInputEvent::MOUSE_MOVE) {
    mouse_move_pending_ = false;

    // now, we can send the next mouse move event
    if (next_mouse_move_.get()) {
      DCHECK(next_mouse_move_->type == WebInputEvent::MOUSE_MOVE);
      ForwardMouseEvent(*next_mouse_move_);
    }
  }

  const char* data = NULL;
  int length = 0;
  if (message.ReadData(&iter, &data, &length)) {
    const WebInputEvent* input_event =
        reinterpret_cast<const WebInputEvent*>(data);
    UnhandledInputEvent(*input_event);
  }
}

void RenderWidgetHost::OnMsgFocus() {
  // Only the user can focus a RenderWidgetHost.
  NOTREACHED();
}

void RenderWidgetHost::OnMsgBlur() {
  if (view_) {
    view_->Blur();
  }
}

void RenderWidgetHost::OnMsgSetCursor(const WebCursor& cursor) {
  if (!view_) {
    return;
  }
  view_->UpdateCursor(cursor);
}

void RenderWidgetHost::OnMsgImeUpdateStatus(ViewHostMsg_ImeControl control,
                                            int x, int y) {
  if (view_) {
    view_->IMEUpdateStatus(control, x, y);
  }
}

///////////////////////////////////////////////////////////////////////////////

void RenderWidgetHost::WasHidden() {
  is_hidden_ = true;

  // Don't bother reporting hung state when we aren't the active tab.
  StopHangMonitorTimeout();

  // If we have a renderer, then inform it that we are being hidden so it can
  // reduce its resource utilization.
  Send(new ViewMsg_WasHidden(routing_id_));

  // 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
  // Windows messages.

  // Tell the RenderProcessHost we were hidden.
  process_->WidgetHidden();
}

void RenderWidgetHost::WasRestored() {
  // When we create the widget, it is created as *not* hidden.
  if (!is_hidden_)
    return;
  is_hidden_ = false;

  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) {
    needs_repainting = true;
    needs_repainting_on_restore_ = false;
  } else {
    needs_repainting = false;
  }
  Send(new ViewMsg_WasRestored(routing_id_, needs_repainting));

  process_->WidgetRestored();
}

void RenderWidgetHost::WasResized() {
  if (resize_ack_pending_ || !process_->channel())
    return;

  gfx::Rect view_bounds = view_->GetViewBounds();
  gfx::Size new_size(view_bounds.width(), view_bounds.height());

  // Avoid asking the RenderWidget to resize to its current size, since it
  // won't send us a PaintRect message in that case.
  if (new_size == current_size_)
    return;

  // We don't expect to receive an ACK when the requested size is empty.
  if (!new_size.IsEmpty())
    resize_ack_pending_ = true;

  if (!Send(new ViewMsg_Resize(routing_id_, new_size)))
    resize_ack_pending_ = false;
}

void RenderWidgetHost::ForwardMouseEvent(const WebMouseEvent& mouse_event) {
  // Avoid spamming the renderer with mouse move events.  It is important
  // to note that WM_MOUSEMOVE events are anyways synthetic, but since our
  // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way
  // more WM_MOUSEMOVE events than we wish to send to the renderer.
  if (mouse_event.type == WebInputEvent::MOUSE_MOVE) {
    if (mouse_move_pending_) {
      next_mouse_move_.reset(new WebMouseEvent(mouse_event));
      return;
    }
    mouse_move_pending_ = true;
  }

  ForwardInputEvent(mouse_event, sizeof(WebMouseEvent));
}

void RenderWidgetHost::ForwardKeyboardEvent(
    const WebKeyboardEvent& key_event) {
  ForwardInputEvent(key_event, sizeof(WebKeyboardEvent));
}

void RenderWidgetHost::ForwardWheelEvent(
    const WebMouseWheelEvent& wheel_event) {
  ForwardInputEvent(wheel_event, sizeof(WebMouseWheelEvent));
}

void RenderWidgetHost::ForwardInputEvent(const WebInputEvent& input_event,
                                         int event_size) {
  if (!process_->channel())
    return;

  IPC::Message* message = new ViewMsg_HandleInputEvent(routing_id_);
  message->WriteData(
      reinterpret_cast<const char*>(&input_event), event_size);
  input_event_start_time_ = TimeTicks::Now();
  Send(message);

  // any input event cancels a pending mouse move event
  next_mouse_move_.reset();

  StartHangMonitorTimeout(kHungRendererDelayMs);
}

void RenderWidgetHost::Shutdown() {
  if (process_->channel()) {
    // Tell the renderer object to close.
    process_->ReportExpectingClose(routing_id_);
    bool rv = Send(new ViewMsg_Close(routing_id_));
    DCHECK(rv);
  }

  Destroy();
}

void RenderWidgetHost::Focus() {
  Send(new ViewMsg_SetFocus(routing_id_, true));
}

void RenderWidgetHost::Blur() {
  Send(new ViewMsg_SetFocus(routing_id_, false));
}

void RenderWidgetHost::LostCapture() {
  Send(new ViewMsg_MouseCaptureLost(routing_id_));
}

void RenderWidgetHost::ViewDestroyed() {
  // TODO(evanm): tracking this may no longer be necessary;
  // eliminate this function if so.
  view_ = NULL;
}

void RenderWidgetHost::Destroy() {
  // Tell the view to die.
  // Note that in the process of the view shutting down, it can call a ton
  // of other messages on us.  So if you do any other deinitialization here,
  // do it after this call to view_->Destroy().
  if (view_)
    view_->Destroy();

  delete this;
}

void RenderWidgetHost::RendererIsUnresponsive() {
  NotificationService::current()->Notify(NOTIFY_RENDERER_PROCESS_HANG,
                                         Source<RenderWidgetHost>(this),
                                         NotificationService::NoDetails());
  is_unresponsive_ = true;
  NotifyRendererUnresponsive();
}

void RenderWidgetHost::RendererIsResponsive() {
  if (is_unresponsive_) {
    is_unresponsive_ = false;
    NotifyRendererResponsive();
  }
}

bool RenderWidgetHost::Send(IPC::Message* msg) {
  return process_->Send(msg);
}

void RenderWidgetHost::SetIsLoading(bool is_loading) {
  is_loading_ = is_loading;
  if (!view_)
    return;
  view_->SetIsLoading(is_loading);
}

RenderWidgetHost::BackingStore* RenderWidgetHost::GetBackingStore() {
  // 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!";

  // We might have a cached backing store that we can reuse!
  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) {
    IPC::Message msg;
    TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS);
    if (process_->widget_helper()->WaitForPaintMsg(routing_id_, max_delay,
                                                   &msg)) {
      suppress_view_updating_ = true;
      ViewHostMsg_PaintRect::Dispatch(
          &msg, this, &RenderWidgetHost::OnMsgPaintRect);
      suppress_view_updating_ = false;
      backing_store = BackingStoreManager::GetBackingStore(this, current_size_);
    }
  }

  return backing_store;
}

void RenderWidgetHost::PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect,
                                 const gfx::Size& view_size) {
  if (is_hidden_) {
    needs_repainting_on_restore_ = true;
    return;
  }

  // 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));
  }
}

void RenderWidgetHost::ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect,
                                  int dx, int dy, const gfx::Rect& clip_rect,
                                  const gfx::Size& view_size) {
  if (is_hidden_) {
    needs_repainting_on_restore_ = true;
    return;
  }

  // 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?
  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);

  // TODO(darin): this doesn't work if dx and dy are both non-zero!
  DCHECK(dx == 0 || dy == 0);

  // We expect that damaged_rect should equal bitmap_rect.
  DCHECK(gfx::Rect(damaged_rect) == bitmap_rect);

  backing_store->Refresh(process_->process(), bitmap, bitmap_rect);
}

void RenderWidgetHost::RestartHangMonitorTimeout() {
  hung_renderer_factory_.RevokeAll();
  StartHangMonitorTimeout(kHungRendererDelayMs);
}

void RenderWidgetHost::StopHangMonitorTimeout() {
  hung_renderer_factory_.RevokeAll();
  RendererIsResponsive();
}

void RenderWidgetHost::StartHangMonitorTimeout(int delay) {
  MessageLoop::current()->PostDelayedTask(FROM_HERE,
        hung_renderer_factory_.NewRunnableMethod(
            &RenderWidgetHost::RendererIsUnresponsive), delay);
}

void RenderWidgetHost::RendererExited() {
  BackingStoreManager::RemoveBackingStore(this);
}

void RenderWidgetHost::SystemThemeChanged() {
  Send(new ViewMsg_ThemeChanged(routing_id_));
}