// Copyright (c) 2011 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 "chrome/common/render_messages.h"
#include "chrome/renderer/page_click_listener.h"
#include "chrome/renderer/page_click_tracker.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "content/public/renderer/render_view.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "ui/base/keycodes/keyboard_codes.h"
class TestPageClickListener : public PageClickListener {
 public:
  TestPageClickListener()
      : input_element_clicked_called_(false),
        input_element_lost_focus_called_(false),
        was_focused_(false),
        is_focused_(false),
        notification_response_(false) {
  }
  virtual bool InputElementClicked(const WebKit::WebInputElement& element,
                                   bool was_focused,
                                   bool is_focused) {
    input_element_clicked_called_ = true;
    element_clicked_ = element;
    was_focused_ = was_focused;
    is_focused_ = is_focused;
    return notification_response_;
  }
  virtual bool InputElementLostFocus() {
    input_element_lost_focus_called_ = true;
    return notification_response_;
  }
  void ClearResults() {
    input_element_clicked_called_ = false;
    input_element_lost_focus_called_ = false;
    element_clicked_.reset();
    was_focused_ = false;
    is_focused_ = false;
  }
  bool input_element_clicked_called_;
  bool input_element_lost_focus_called_;
  WebKit::WebInputElement element_clicked_;
  bool was_focused_;
  bool is_focused_;
  bool notification_response_;
};
class PageClickTrackerTest : public ChromeRenderViewTest {
 protected:
  virtual void SetUp() {
    ChromeRenderViewTest::SetUp();
    // RenderView creates PageClickTracker but it doesn't keep it around.
    // Rather than make it do so for the test, we create a new object.
    page_click_tracker_.reset(new PageClickTracker(view_));
    page_click_tracker_->AddListener(&test_listener1_);
    page_click_tracker_->AddListener(&test_listener2_);
    LoadHTML("
");
    GetWebWidget()->resize(WebKit::WebSize(500, 500));
    GetWebWidget()->setFocus(true);
    WebKit::WebDocument document = view_->GetWebView()->mainFrame()->document();
    text_ = document.getElementById("text_1");
    ASSERT_FALSE(text_.isNull());
  }
  // Send all the messages required for a complete key press.
  void SendKeyPress(int key_code) {
    WebKit::WebKeyboardEvent keyboard_event;
    keyboard_event.windowsKeyCode = key_code;
    keyboard_event.setKeyIdentifierFromWindowsKeyCode();
    keyboard_event.type = WebKit::WebInputEvent::RawKeyDown;
    SendWebKeyboardEvent(keyboard_event);
    keyboard_event.type = WebKit::WebInputEvent::Char;
    SendWebKeyboardEvent(keyboard_event);
    keyboard_event.type = WebKit::WebInputEvent::KeyUp;
    SendWebKeyboardEvent(keyboard_event);
  }
  scoped_ptr  page_click_tracker_;
  TestPageClickListener test_listener1_;
  TestPageClickListener test_listener2_;
  WebKit::WebElement text_;
};
// Tests that PageClickTracker does notify correctly when a node is clicked.
TEST_F(PageClickTrackerTest, PageClickTrackerInputClicked) {
  // Click the text field once.
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_TRUE(test_listener1_.input_element_clicked_called_);
  EXPECT_TRUE(test_listener2_.input_element_clicked_called_);
  EXPECT_FALSE(test_listener1_.was_focused_);
  EXPECT_FALSE(test_listener2_.was_focused_);
  EXPECT_TRUE(test_listener1_.is_focused_);
  EXPECT_TRUE(test_listener2_.is_focused_);
  EXPECT_TRUE(text_ == test_listener1_.element_clicked_);
  EXPECT_TRUE(text_ == test_listener2_.element_clicked_);
  test_listener1_.ClearResults();
  test_listener2_.ClearResults();
  // Click the text field again to test that was_focused_ is set correctly.
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_TRUE(test_listener1_.input_element_clicked_called_);
  EXPECT_TRUE(test_listener2_.input_element_clicked_called_);
  EXPECT_TRUE(test_listener1_.was_focused_);
  EXPECT_TRUE(test_listener2_.was_focused_);
  EXPECT_TRUE(test_listener1_.is_focused_);
  EXPECT_TRUE(test_listener2_.is_focused_);
  EXPECT_TRUE(text_ == test_listener1_.element_clicked_);
  EXPECT_TRUE(text_ == test_listener2_.element_clicked_);
  test_listener1_.ClearResults();
  test_listener2_.ClearResults();
  // Click the button, no notification should happen (this is not a text-input).
  EXPECT_TRUE(SimulateElementClick("button"));
  EXPECT_FALSE(test_listener1_.input_element_clicked_called_);
  EXPECT_FALSE(test_listener2_.input_element_clicked_called_);
  // Make the first listener stop the event propagation, click the text field
  // and make sure only the first listener is notified.
  test_listener1_.notification_response_ = true;
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_TRUE(test_listener1_.input_element_clicked_called_);
  EXPECT_FALSE(test_listener2_.input_element_clicked_called_);
  test_listener1_.ClearResults();
  // Make sure removing a listener work.
  page_click_tracker_->RemoveListener(&test_listener1_);
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_FALSE(test_listener1_.input_element_clicked_called_);
  EXPECT_TRUE(test_listener2_.input_element_clicked_called_);
  test_listener2_.ClearResults();
  // Make sure we don't choke when no listeners are registered.
  page_click_tracker_->RemoveListener(&test_listener2_);
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_FALSE(test_listener1_.input_element_clicked_called_);
  EXPECT_FALSE(test_listener2_.input_element_clicked_called_);
}
TEST_F(PageClickTrackerTest, PageClickTrackerInputFocusLost) {
  // Gain focus on the text field by using tab.
  EXPECT_NE(text_, text_.document().focusedNode());
  SendKeyPress(ui::VKEY_TAB);
  EXPECT_EQ(text_, text_.document().focusedNode());
  EXPECT_FALSE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_FALSE(test_listener2_.input_element_lost_focus_called_);
  // Click a button and ensure that the lost focus notification was sent,
  // even though focus was gained without the mouse.
  EXPECT_TRUE(SimulateElementClick("button"));
  EXPECT_TRUE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_TRUE(test_listener2_.input_element_lost_focus_called_);
  test_listener1_.ClearResults();
  test_listener2_.ClearResults();
  // Click a text field and test that no lost focus notifications are sent.
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_FALSE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_FALSE(test_listener2_.input_element_lost_focus_called_);
  test_listener1_.ClearResults();
  test_listener2_.ClearResults();
  // Select another text field to test that the notifcation for the
  // first text field losing focus is sent.
  EXPECT_TRUE(SimulateElementClick("text_2"));
  EXPECT_TRUE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_TRUE(test_listener2_.input_element_lost_focus_called_);
  test_listener1_.ClearResults();
  test_listener2_.ClearResults();
  // Click the button, a notification should happen since a text field has
  // lost focus.
  EXPECT_TRUE(SimulateElementClick("button"));
  EXPECT_TRUE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_TRUE(test_listener2_.input_element_lost_focus_called_);
  test_listener1_.ClearResults();
  test_listener2_.ClearResults();
  // Click on a text field while the button has focus and ensure no lost focus
  // notification is sent.
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_FALSE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_FALSE(test_listener2_.input_element_lost_focus_called_);
  // Make the first listener stop the event propagation, then click a button
  // and make sure only the first listener is notified.
  test_listener1_.notification_response_ = true;
  EXPECT_TRUE(SimulateElementClick("button"));
  EXPECT_TRUE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_FALSE(test_listener2_.input_element_lost_focus_called_);
  test_listener1_.ClearResults();
  // Make sure removing a listener work.
  page_click_tracker_->RemoveListener(&test_listener1_);
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_TRUE(SimulateElementClick("button"));
  EXPECT_FALSE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_TRUE(test_listener2_.input_element_lost_focus_called_);
  test_listener2_.ClearResults();
  // Make sure we don't choke when no listeners are registered.
  page_click_tracker_->RemoveListener(&test_listener2_);
  EXPECT_TRUE(SimulateElementClick("text_1"));
  EXPECT_TRUE(SimulateElementClick("button"));
  EXPECT_FALSE(test_listener1_.input_element_lost_focus_called_);
  EXPECT_FALSE(test_listener2_.input_element_lost_focus_called_);
}