text: // Copyright (c) 2012 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 "base/basictypes.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/shared_memory.h"
  #include "base/timer.h"
  #include "content/browser/browser_thread_impl.h"
  #include "content/browser/renderer_host/backing_store.h"
  #include "content/browser/renderer_host/test_render_view_host.h"
  #include "content/common/view_messages.h"
  #include "content/port/browser/render_widget_host_view_port.h"
  #include "content/public/browser/notification_details.h"
  #include "content/public/browser/notification_observer.h"
  #include "content/public/browser/notification_registrar.h"
  #include "content/public/browser/notification_source.h"
  #include "content/public/browser/notification_types.h"
  #include "content/test/mock_render_process_host.h"
  #include "content/test/test_browser_context.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "ui/base/keycodes/keyboard_codes.h"
  #include "ui/gfx/canvas.h"
  
  #if defined(USE_AURA)
  #include "content/browser/renderer_host/render_widget_host_view_aura.h"
  #endif
  
  using base::TimeDelta;
  using content::BrowserThread;
  using content::BrowserThreadImpl;
  using content::MockRenderProcessHost;
  using content::RenderWidgetHost;
  using content::RenderWidgetHostImpl;
  using WebKit::WebInputEvent;
  using WebKit::WebMouseWheelEvent;
  
  namespace gfx {
  class Size;
  }
  
  // RenderWidgetHostProcess -----------------------------------------------------
  
  class RenderWidgetHostProcess : public MockRenderProcessHost {
   public:
    explicit RenderWidgetHostProcess(content::BrowserContext* browser_context)
        : MockRenderProcessHost(browser_context),
          current_update_buf_(NULL),
          update_msg_should_reply_(false),
          update_msg_reply_flags_(0) {
    }
    ~RenderWidgetHostProcess() {
      delete current_update_buf_;
    }
  
    void set_update_msg_should_reply(bool reply) {
      update_msg_should_reply_ = reply;
    }
    void set_update_msg_reply_flags(int flags) {
      update_msg_reply_flags_ = flags;
    }
  
    // Fills the given update parameters with resonable default values.
    void InitUpdateRectParams(ViewHostMsg_UpdateRect_Params* params);
  
    virtual bool HasConnection() const { return true; }
  
   protected:
    virtual bool WaitForUpdateMsg(int render_widget_id,
                                  const base::TimeDelta& max_delay,
                                  IPC::Message* msg);
  
    TransportDIB* current_update_buf_;
  
    // Set to true when WaitForUpdateMsg should return a successful update message
    // reply. False implies timeout.
    bool update_msg_should_reply_;
  
    // Indicates the flags that should be sent with a the repaint request. This
    // only has an effect when update_msg_should_reply_ is true.
    int update_msg_reply_flags_;
  
    DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostProcess);
  };
  
  void RenderWidgetHostProcess::InitUpdateRectParams(
      ViewHostMsg_UpdateRect_Params* params) {
    // Create the shared backing store.
    const int w = 100, h = 100;
    const size_t pixel_size = w * h * 4;
  
    if (!current_update_buf_)
      current_update_buf_ = TransportDIB::Create(pixel_size, 0);
    params->bitmap = current_update_buf_->id();
    params->bitmap_rect = gfx::Rect(0, 0, w, h);
    params->dx = 0;
    params->dy = 0;
    params->copy_rects.push_back(params->bitmap_rect);
    params->view_size = gfx::Size(w, h);
    params->flags = update_msg_reply_flags_;
    params->needs_ack = true;
  }
  
  bool RenderWidgetHostProcess::WaitForUpdateMsg(int render_widget_id,
                                                 const base::TimeDelta& max_delay,
                                                 IPC::Message* msg) {
    if (!update_msg_should_reply_)
      return false;
  
    // Construct a fake update reply.
    ViewHostMsg_UpdateRect_Params params;
    InitUpdateRectParams(&params);
  
    ViewHostMsg_UpdateRect message(render_widget_id, params);
    *msg = message;
    return true;
  }
  
  // TestView --------------------------------------------------------------------
  
  // This test view allows us to specify the size.
  class TestView : public content::TestRenderWidgetHostView {
   public:
    explicit TestView(RenderWidgetHostImpl* rwh)
        : content::TestRenderWidgetHostView(rwh) {
    }
  
    // Sets the bounds returned by GetViewBounds.
    void set_bounds(const gfx::Rect& bounds) {
      bounds_ = bounds;
    }
  
    // RenderWidgetHostView override.
    virtual gfx::Rect GetViewBounds() const {
      return bounds_;
    }
  
  #if defined(OS_MACOSX)
    virtual gfx::Rect GetViewCocoaBounds() const {
      return bounds_;
    }
  #endif
  
   protected:
    gfx::Rect bounds_;
    DISALLOW_COPY_AND_ASSIGN(TestView);
  };
  
  // MockRenderWidgetHost ----------------------------------------------------
  
  class MockRenderWidgetHost : public RenderWidgetHostImpl {
   public:
    MockRenderWidgetHost(content::RenderProcessHost* process, int routing_id)
        : RenderWidgetHostImpl(process, routing_id),
          prehandle_keyboard_event_(false),
          prehandle_keyboard_event_called_(false),
          prehandle_keyboard_event_type_(WebInputEvent::Undefined),
          unhandled_keyboard_event_called_(false),
          unhandled_keyboard_event_type_(WebInputEvent::Undefined),
          unresponsive_timer_fired_(false) {
    }
  
    // Allow poking at a few private members.
    using RenderWidgetHostImpl::OnMsgPaintAtSizeAck;
    using RenderWidgetHostImpl::OnMsgUpdateRect;
    using RenderWidgetHostImpl::RendererExited;
    using RenderWidgetHostImpl::in_flight_size_;
    using RenderWidgetHostImpl::is_hidden_;
    using RenderWidgetHostImpl::resize_ack_pending_;
  
    // Tests that make sure we ignore keyboard event acknowledgments to events we
    // didn't send work by making sure we didn't call UnhandledKeyboardEvent().
    bool unhandled_keyboard_event_called() const {
      return unhandled_keyboard_event_called_;
    }
  
    WebInputEvent::Type unhandled_keyboard_event_type() const {
      return unhandled_keyboard_event_type_;
    }
  
    bool prehandle_keyboard_event_called() const {
      return prehandle_keyboard_event_called_;
    }
  
    WebInputEvent::Type prehandle_keyboard_event_type() const {
      return prehandle_keyboard_event_type_;
    }
  
    void set_prehandle_keyboard_event(bool handle) {
      prehandle_keyboard_event_ = handle;
    }
  
    bool unresponsive_timer_fired() const {
      return unresponsive_timer_fired_;
    }
  
    void set_hung_renderer_delay_ms(int delay_ms) {
      hung_renderer_delay_ms_ = delay_ms;
    }
  
   protected:
    virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
                                        bool* is_keyboard_shortcut) {
      prehandle_keyboard_event_type_ = event.type;
      prehandle_keyboard_event_called_ = true;
      return prehandle_keyboard_event_;
    }
  
    virtual void UnhandledKeyboardEvent(const NativeWebKeyboardEvent& event) {
      unhandled_keyboard_event_type_ = event.type;
      unhandled_keyboard_event_called_ = true;
    }
  
    virtual void NotifyRendererUnresponsive() {
      unresponsive_timer_fired_ = true;
    }
  
   private:
    bool prehandle_keyboard_event_;
    bool prehandle_keyboard_event_called_;
    WebInputEvent::Type prehandle_keyboard_event_type_;
  
    bool unhandled_keyboard_event_called_;
    WebInputEvent::Type unhandled_keyboard_event_type_;
  
    bool unresponsive_timer_fired_;
  };
  
  // MockPaintingObserver --------------------------------------------------------
  
  class MockPaintingObserver : public content::NotificationObserver {
   public:
    void WidgetDidReceivePaintAtSizeAck(RenderWidgetHostImpl* host,
                                        int tag,
                                        const gfx::Size& size) {
      host_ = reinterpret_cast<MockRenderWidgetHost*>(host);
      tag_ = tag;
      size_ = size;
    }
  
    void Observe(int type,
                 const content::NotificationSource& source,
                 const content::NotificationDetails& details) {
      if (type ==
          content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK) {
        std::pair<int, gfx::Size>* size_ack_details =
            content::Details<std::pair<int, gfx::Size> >(details).ptr();
        WidgetDidReceivePaintAtSizeAck(
            RenderWidgetHostImpl::From(
                content::Source<RenderWidgetHost>(source).ptr()),
            size_ack_details->first,
            size_ack_details->second);
      }
    }
  
    MockRenderWidgetHost* host() const { return host_; }
    int tag() const { return tag_; }
    gfx::Size size() const { return size_; }
  
   private:
    MockRenderWidgetHost* host_;
    int tag_;
    gfx::Size size_;
  };
  
  
  // RenderWidgetHostTest --------------------------------------------------------
  
  class RenderWidgetHostTest : public testing::Test {
   public:
    RenderWidgetHostTest() : process_(NULL) {
    }
    ~RenderWidgetHostTest() {
    }
  
   protected:
    // testing::Test
    void SetUp() {
      browser_context_.reset(new TestBrowserContext());
      process_ = new RenderWidgetHostProcess(browser_context_.get());
      host_.reset(new MockRenderWidgetHost(process_, MSG_ROUTING_NONE));
      view_.reset(new TestView(host_.get()));
      host_->SetView(view_.get());
      host_->Init();
    }
    void TearDown() {
      view_.reset();
      host_.reset();
      process_ = NULL;
      browser_context_.reset();
  
      // Process all pending tasks to avoid leaks.
      MessageLoop::current()->RunAllPending();
    }
  
    void SendInputEventACK(WebInputEvent::Type type, bool processed) {
      scoped_ptr<IPC::Message> response(
          new ViewHostMsg_HandleInputEvent_ACK(0, type, processed));
      host_->OnMessageReceived(*response);
    }
  
    void SimulateKeyboardEvent(WebInputEvent::Type type) {
      NativeWebKeyboardEvent key_event;
      key_event.type = type;
      key_event.windowsKeyCode = ui::VKEY_L;  // non-null made up value.
      host_->ForwardKeyboardEvent(key_event);
    }
  
    void SimulateWheelEvent(float dX, float dY, int modifiers) {
      WebMouseWheelEvent wheel_event;
      wheel_event.type = WebInputEvent::MouseWheel;
      wheel_event.deltaX = dX;
      wheel_event.deltaY = dY;
      wheel_event.modifiers = modifiers;
      host_->ForwardWheelEvent(wheel_event);
    }
  
    MessageLoopForUI message_loop_;
  
    scoped_ptr<TestBrowserContext> browser_context_;
    RenderWidgetHostProcess* process_;  // Deleted automatically by the widget.
    scoped_ptr<MockRenderWidgetHost> host_;
    scoped_ptr<TestView> view_;
  
    DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostTest);
  };
  
  // -----------------------------------------------------------------------------
  
  TEST_F(RenderWidgetHostTest, Resize) {
    // The initial bounds is the empty rect, so setting it to the same thing
    // should do nothing.
    view_->set_bounds(gfx::Rect());
    host_->WasResized();
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
    EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Setting the bounds to a "real" rect should send out the notification.
    gfx::Rect original_size(0, 0, 100, 100);
    process_->sink().ClearMessages();
    view_->set_bounds(original_size);
    host_->WasResized();
    EXPECT_TRUE(host_->resize_ack_pending_);
    EXPECT_EQ(original_size.size(), host_->in_flight_size_);
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Send out a update that's not a resize ack. This should not clean the
    // resize ack pending flag.
    ViewHostMsg_UpdateRect_Params params;
    process_->InitUpdateRectParams(&params);
    host_->OnMsgUpdateRect(params);
    EXPECT_TRUE(host_->resize_ack_pending_);
    EXPECT_EQ(original_size.size(), host_->in_flight_size_);
  
    // Sending out a new notification should NOT send out a new IPC message since
    // a resize ACK is pending.
    gfx::Rect second_size(0, 0, 90, 90);
    process_->sink().ClearMessages();
    view_->set_bounds(second_size);
    host_->WasResized();
    EXPECT_TRUE(host_->resize_ack_pending_);
    EXPECT_EQ(original_size.size(), host_->in_flight_size_);
    EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Send a update that's a resize ack, but for the original_size we sent. Since
    // this isn't the second_size, the message handler should immediately send
    // a new resize message for the new size to the renderer.
    process_->sink().ClearMessages();
    params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK;
    params.view_size = original_size.size();
    host_->OnMsgUpdateRect(params);
    EXPECT_TRUE(host_->resize_ack_pending_);
    EXPECT_EQ(second_size.size(), host_->in_flight_size_);
    ASSERT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Send the resize ack for the latest size.
    process_->sink().ClearMessages();
    params.view_size = second_size.size();
    host_->OnMsgUpdateRect(params);
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
    ASSERT_FALSE(process_->sink().GetFirstMessageMatching(ViewMsg_Resize::ID));
  
    // Now clearing the bounds should send out a notification but we shouldn't
    // expect a resize ack (since the renderer won't ack empty sizes). The message
    // should contain the new size (0x0) and not the previous one that we skipped
    process_->sink().ClearMessages();
    view_->set_bounds(gfx::Rect());
    host_->WasResized();
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Send a rect that has no area but has either width or height set.
    process_->sink().ClearMessages();
    view_->set_bounds(gfx::Rect(0, 0, 0, 30));
    host_->WasResized();
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(0, 30), host_->in_flight_size_);
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Set the same size again. It should not be sent again.
    process_->sink().ClearMessages();
    host_->WasResized();
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(0, 30), host_->in_flight_size_);
    EXPECT_FALSE(process_->sink().GetFirstMessageMatching(ViewMsg_Resize::ID));
  
    // A different size should be sent again, however.
    view_->set_bounds(gfx::Rect(0, 0, 0, 31));
    host_->WasResized();
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(0, 31), host_->in_flight_size_);
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  }
  
  // Test for crbug.com/25097.  If a renderer crashes between a resize and the
  // corresponding update message, we must be sure to clear the resize ack logic.
  TEST_F(RenderWidgetHostTest, ResizeThenCrash) {
    // Setting the bounds to a "real" rect should send out the notification.
    gfx::Rect original_size(0, 0, 100, 100);
    view_->set_bounds(original_size);
    host_->WasResized();
    EXPECT_TRUE(host_->resize_ack_pending_);
    EXPECT_EQ(original_size.size(), host_->in_flight_size_);
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
  
    // Simulate a renderer crash before the update message.  Ensure all the
    // resize ack logic is cleared.  Must clear the view first so it doesn't get
    // deleted.
    host_->SetView(NULL);
    host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
    EXPECT_FALSE(host_->resize_ack_pending_);
    EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
  
    // Reset the view so we can exit the test cleanly.
    host_->SetView(view_.get());
  }
  
  // Tests setting custom background
  TEST_F(RenderWidgetHostTest, Background) {
  #if !defined(OS_MACOSX)
    scoped_ptr<content::RenderWidgetHostView> view(
        content::RenderWidgetHostView::CreateViewForWidget(host_.get()));
  #if defined(OS_LINUX) || defined(USE_AURA)
    // TODO(derat): Call this on all platforms: http://crbug.com/102450.
    // InitAsChild doesn't seem to work if NULL parent is passed on Windows,
    // which leads to DCHECK failure in RenderWidgetHostView::Destroy.
    // When you enable this for OS_WIN, enable |view.release()->Destroy()|
    // below.
    view->InitAsChild(NULL);
  #endif
    host_->SetView(view.get());
  
    // Create a checkerboard background to test with.
    gfx::Canvas canvas(gfx::Size(4, 4), true);
    canvas.FillRect(gfx::Rect(0, 0, 2, 2), SK_ColorBLACK);
    canvas.FillRect(gfx::Rect(2, 0, 2, 2), SK_ColorWHITE);
    canvas.FillRect(gfx::Rect(0, 2, 2, 2), SK_ColorWHITE);
    canvas.FillRect(gfx::Rect(2, 2, 2, 2), SK_ColorBLACK);
    const SkBitmap& background =
        canvas.sk_canvas()->getDevice()->accessBitmap(false);
  
    // Set the background and make sure we get back a copy.
    view->SetBackground(background);
    EXPECT_EQ(4, view->GetBackground().width());
    EXPECT_EQ(4, view->GetBackground().height());
    EXPECT_EQ(background.getSize(), view->GetBackground().getSize());
    background.lockPixels();
    view->GetBackground().lockPixels();
    EXPECT_TRUE(0 == memcmp(background.getPixels(),
                            view->GetBackground().getPixels(),
                            background.getSize()));
    view->GetBackground().unlockPixels();
    background.unlockPixels();
  
    const IPC::Message* set_background =
        process_->sink().GetUniqueMessageMatching(ViewMsg_SetBackground::ID);
    ASSERT_TRUE(set_background);
    Tuple1<SkBitmap> sent_background;
    ViewMsg_SetBackground::Read(set_background, &sent_background);
    EXPECT_EQ(background.getSize(), sent_background.a.getSize());
    background.lockPixels();
    sent_background.a.lockPixels();
    EXPECT_TRUE(0 == memcmp(background.getPixels(),
                            sent_background.a.getPixels(),
                            background.getSize()));
    sent_background.a.unlockPixels();
    background.unlockPixels();
  
  #if defined(OS_LINUX) || defined(USE_AURA)
    // See the comment above |InitAsChild(NULL)|.
    host_->SetView(NULL);
    static_cast<content::RenderWidgetHostViewPort*>(view.release())->Destroy();
  #endif
  
  #else
    // TODO(port): Mac does not have gfx::Canvas. Maybe we can just change this
    // test to use SkCanvas directly?
  #endif
  
    // TODO(aa): It would be nice to factor out the painting logic so that we
    // could test that, but it appears that would mean painting everything twice
    // since windows HDC structures are opaque.
  }
  
  // Tests getting the backing store with the renderer not setting repaint ack
  // flags.
  TEST_F(RenderWidgetHostTest, GetBackingStore_NoRepaintAck) {
    // We don't currently have a backing store, and if the renderer doesn't send
    // one in time, we should get nothing.
    process_->set_update_msg_should_reply(false);
    BackingStore* backing = host_->GetBackingStore(true);
    EXPECT_FALSE(backing);
    // The widget host should have sent a request for a repaint, and there should
    // be no paint ACK.
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
    EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
        ViewMsg_UpdateRect_ACK::ID));
  
    // Allowing the renderer to reply in time should give is a backing store.
    process_->sink().ClearMessages();
    process_->set_update_msg_should_reply(true);
    process_->set_update_msg_reply_flags(0);
    backing = host_->GetBackingStore(true);
    EXPECT_TRUE(backing);
    // The widget host should NOT have sent a request for a repaint, since there
    // was an ACK already pending.
    EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
        ViewMsg_UpdateRect_ACK::ID));
  }
  
  // Tests getting the backing store with the renderer sending a repaint ack.
  TEST_F(RenderWidgetHostTest, GetBackingStore_RepaintAck) {
    // Doing a request request with the update message allowed should work and
    // the repaint ack should work.
    process_->set_update_msg_should_reply(true);
    process_->set_update_msg_reply_flags(
        ViewHostMsg_UpdateRect_Flags::IS_REPAINT_ACK);
    BackingStore* backing = host_->GetBackingStore(true);
    EXPECT_TRUE(backing);
    // We still should not have sent out a repaint request since the last flags
    // didn't have the repaint ack set, and the pending flag will still be set.
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
        ViewMsg_UpdateRect_ACK::ID));
  
    // Asking again for the backing store should just re-use the existing one
    // and not send any messagse.
    process_->sink().ClearMessages();
    backing = host_->GetBackingStore(true);
    EXPECT_TRUE(backing);
    EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
    EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
        ViewMsg_UpdateRect_ACK::ID));
  }
  
  // Test that we don't paint when we're hidden, but we still send the ACK. Most
  // of the rest of the painting is tested in the GetBackingStore* ones.
  TEST_F(RenderWidgetHostTest, HiddenPaint) {
    BrowserThreadImpl ui_thread(BrowserThread::UI, MessageLoop::current());
    // Hide the widget, it should have sent out a message to the renderer.
    EXPECT_FALSE(host_->is_hidden_);
    host_->WasHidden();
    EXPECT_TRUE(host_->is_hidden_);
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_WasHidden::ID));
  
    // Send it an update as from the renderer.
    process_->sink().ClearMessages();
    ViewHostMsg_UpdateRect_Params params;
    process_->InitUpdateRectParams(&params);
    host_->OnMsgUpdateRect(params);
  
    // It should have sent out the ACK.
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
        ViewMsg_UpdateRect_ACK::ID));
  
    // Now unhide.
    process_->sink().ClearMessages();
    host_->WasRestored();
    EXPECT_FALSE(host_->is_hidden_);
  
    // It should have sent out a restored message with a request to paint.
    const IPC::Message* restored = process_->sink().GetUniqueMessageMatching(
        ViewMsg_WasRestored::ID);
    ASSERT_TRUE(restored);
    Tuple1<bool> needs_repaint;
    ViewMsg_WasRestored::Read(restored, &needs_repaint);
    EXPECT_TRUE(needs_repaint.a);
  }
  
  TEST_F(RenderWidgetHostTest, PaintAtSize) {
    const int kPaintAtSizeTag = 42;
    host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), kPaintAtSizeTag,
                       gfx::Size(40, 60), gfx::Size(20, 30));
    EXPECT_TRUE(
        process_->sink().GetUniqueMessageMatching(ViewMsg_PaintAtSize::ID));
  
    content::NotificationRegistrar registrar;
    MockPaintingObserver observer;
    registrar.Add(
        &observer,
        content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
        content::Source<RenderWidgetHost>(host_.get()));
  
    host_->OnMsgPaintAtSizeAck(kPaintAtSizeTag, gfx::Size(20, 30));
    EXPECT_EQ(host_.get(), observer.host());
    EXPECT_EQ(kPaintAtSizeTag, observer.tag());
    EXPECT_EQ(20, observer.size().width());
    EXPECT_EQ(30, observer.size().height());
  }
  
  // Fails on Linux Aura, see http://crbug.com/100344
  #if defined(USE_AURA) && !defined(OS_WIN)
  #define MAYBE_HandleKeyEventsWeSent FAILS_HandleKeyEventsWeSent
  #else
  #define MAYBE_HandleKeyEventsWeSent HandleKeyEventsWeSent
  #endif
  TEST_F(RenderWidgetHostTest, MAYBE_HandleKeyEventsWeSent) {
    // Simulate a keyboard event.
    SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
  
    // Make sure we sent the input event to the renderer.
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
                    ViewMsg_HandleInputEvent::ID));
    process_->sink().ClearMessages();
  
    // Send the simulated response from the renderer back.
    SendInputEventACK(WebInputEvent::RawKeyDown, false);
  
    EXPECT_TRUE(host_->unhandled_keyboard_event_called());
    EXPECT_EQ(WebInputEvent::RawKeyDown, host_->unhandled_keyboard_event_type());
  }
  
  TEST_F(RenderWidgetHostTest, IgnoreKeyEventsWeDidntSend) {
    // Send a simulated, unrequested key response. We should ignore this.
    SendInputEventACK(WebInputEvent::RawKeyDown, false);
  
    EXPECT_FALSE(host_->unhandled_keyboard_event_called());
  }
  
  TEST_F(RenderWidgetHostTest, IgnoreKeyEventsHandledByRenderer) {
    // Simulate a keyboard event.
    SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
  
    // Make sure we sent the input event to the renderer.
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
                    ViewMsg_HandleInputEvent::ID));
    process_->sink().ClearMessages();
  
    // Send the simulated response from the renderer back.
    SendInputEventACK(WebInputEvent::RawKeyDown, true);
    EXPECT_FALSE(host_->unhandled_keyboard_event_called());
  }
  
  TEST_F(RenderWidgetHostTest, PreHandleRawKeyDownEvent) {
    // Simluate the situation that the browser handled the key down event during
    // pre-handle phrase.
    host_->set_prehandle_keyboard_event(true);
    process_->sink().ClearMessages();
  
    // Simulate a keyboard event.
    SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
  
    EXPECT_TRUE(host_->prehandle_keyboard_event_called());
    EXPECT_EQ(WebInputEvent::RawKeyDown, host_->prehandle_keyboard_event_type());
  
    // Make sure the RawKeyDown event is not sent to the renderer.
    EXPECT_EQ(0U, process_->sink().message_count());
  
    // The browser won't pre-handle a Char event.
    host_->set_prehandle_keyboard_event(false);
  
    // Forward the Char event.
    SimulateKeyboardEvent(WebInputEvent::Char);
  
    // Make sure the Char event is suppressed.
    EXPECT_EQ(0U, process_->sink().message_count());
  
    // Forward the KeyUp event.
    SimulateKeyboardEvent(WebInputEvent::KeyUp);
  
    // Make sure only KeyUp was sent to the renderer.
    EXPECT_EQ(1U, process_->sink().message_count());
    EXPECT_EQ(ViewMsg_HandleInputEvent::ID,
              process_->sink().GetMessageAt(0)->type());
    process_->sink().ClearMessages();
  
    // Send the simulated response from the renderer back.
    SendInputEventACK(WebInputEvent::KeyUp, false);
  
    EXPECT_TRUE(host_->unhandled_keyboard_event_called());
    EXPECT_EQ(WebInputEvent::KeyUp, host_->unhandled_keyboard_event_type());
  }
  
  TEST_F(RenderWidgetHostTest, CoalescesWheelEvents) {
    process_->sink().ClearMessages();
  
    // Simulate wheel events.
    SimulateWheelEvent(0, -5, 0);  // sent directly
    SimulateWheelEvent(0, -10, 0);  // enqueued
    SimulateWheelEvent(8, -6, 0);  // coalesced into previous event
    SimulateWheelEvent(9, -7, 1);  // enqueued, different modifiers
  
    // Check that only the first event was sent.
    EXPECT_EQ(1U, process_->sink().message_count());
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
                    ViewMsg_HandleInputEvent::ID));
    process_->sink().ClearMessages();
  
    // Check that the ACK sends the second message.
    SendInputEventACK(WebInputEvent::MouseWheel, true);
    // The coalesced events can queue up a delayed ack
    // so that additional input events can be processed before
    // we turn off coalescing.
    MessageLoop::current()->RunAllPending();
    EXPECT_EQ(1U, process_->sink().message_count());
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
                    ViewMsg_HandleInputEvent::ID));
    process_->sink().ClearMessages();
  
    // One more time.
    SendInputEventACK(WebInputEvent::MouseWheel, true);
    MessageLoop::current()->RunAllPending();
    EXPECT_EQ(1U, process_->sink().message_count());
    EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
                    ViewMsg_HandleInputEvent::ID));
    process_->sink().ClearMessages();
  
    // After the final ack, the queue should be empty.
    SendInputEventACK(WebInputEvent::MouseWheel, true);
    MessageLoop::current()->RunAllPending();
    EXPECT_EQ(0U, process_->sink().message_count());
  }
  
  // Test that the hang monitor timer expires properly if a new timer is started
  // while one is in progress (see crbug.com/11007).
  TEST_F(RenderWidgetHostTest, DontPostponeHangMonitorTimeout) {
    // Start with a short timeout.
    host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
  
    // Immediately try to add a long 30 second timeout.
    EXPECT_FALSE(host_->unresponsive_timer_fired());
    host_->StartHangMonitorTimeout(TimeDelta::FromSeconds(30));
  
    // Wait long enough for first timeout and see if it fired.
    MessageLoop::current()->PostDelayedTask(
        FROM_HERE, MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds(10));
    MessageLoop::current()->Run();
    EXPECT_TRUE(host_->unresponsive_timer_fired());
  }
  
  // Test that the hang monitor timer expires properly if it is started, stopped,
  // and then started again.
  TEST_F(RenderWidgetHostTest, StopAndStartHangMonitorTimeout) {
    // Start with a short timeout, then stop it.
    host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
    host_->StopHangMonitorTimeout();
  
    // Start it again to ensure it still works.
    EXPECT_FALSE(host_->unresponsive_timer_fired());
    host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
  
    // Wait long enough for first timeout and see if it fired.
    MessageLoop::current()->PostDelayedTask(
        FROM_HERE, MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds(40));
    MessageLoop::current()->Run();
    EXPECT_TRUE(host_->unresponsive_timer_fired());
  }
  
  // Test that the hang monitor catches two input events but only one ack.
  // This can happen if the second input event causes the renderer to hang.
  // This test will catch a regression of crbug.com/111185.
  TEST_F(RenderWidgetHostTest, MultipleInputEvents) {
    // Configure the host to wait 10ms before considering
    // the renderer hung.
    host_->set_hung_renderer_delay_ms(10);
  
    // Send two events but only one ack.
    SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
    SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
    SendInputEventACK(WebInputEvent::RawKeyDown, true);
  
    // Wait long enough for first timeout and see if it fired.
    MessageLoop::current()->PostDelayedTask(
        FROM_HERE, MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds(40));
    MessageLoop::current()->Run();
    EXPECT_TRUE(host_->unresponsive_timer_fired());
  }
  