1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/autofill/content/renderer/page_click_tracker.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "components/autofill/content/renderer/form_autofill_util.h"
10#include "components/autofill/content/renderer/page_click_listener.h"
11#include "content/public/renderer/render_view.h"
12#include "third_party/WebKit/public/platform/WebString.h"
13#include "third_party/WebKit/public/web/WebDOMMouseEvent.h"
14#include "third_party/WebKit/public/web/WebDocument.h"
15#include "third_party/WebKit/public/web/WebInputElement.h"
16#include "third_party/WebKit/public/web/WebInputEvent.h"
17#include "third_party/WebKit/public/web/WebLocalFrame.h"
18#include "third_party/WebKit/public/web/WebTextAreaElement.h"
19#include "third_party/WebKit/public/web/WebView.h"
20
21using blink::WebDOMEvent;
22using blink::WebDOMMouseEvent;
23using blink::WebElement;
24using blink::WebFormControlElement;
25using blink::WebFrame;
26using blink::WebGestureEvent;
27using blink::WebInputElement;
28using blink::WebInputEvent;
29using blink::WebMouseEvent;
30using blink::WebNode;
31using blink::WebString;
32using blink::WebTextAreaElement;
33using blink::WebView;
34
35namespace {
36
37// Casts |node| to a WebInputElement.
38// Returns an empty (isNull()) WebInputElement if |node| is not a text field.
39const WebInputElement GetTextWebInputElement(const WebNode& node) {
40  if (!node.isElementNode())
41    return WebInputElement();
42  const WebElement element = node.toConst<WebElement>();
43  if (!element.hasHTMLTagName("input"))
44    return WebInputElement();
45  const WebInputElement* input = blink::toWebInputElement(&element);
46  if (!autofill::IsTextInput(input))
47    return WebInputElement();
48  return *input;
49}
50
51// Casts |node| to a WebTextAreaElement.
52// Returns an empty (isNull()) WebTextAreaElement if |node| is not a
53// textarea field.
54const WebTextAreaElement GetWebTextAreaElement(const WebNode& node) {
55  if (!node.isElementNode())
56    return WebTextAreaElement();
57  const WebElement element = node.toConst<WebElement>();
58  if (!element.hasHTMLTagName("textarea"))
59    return WebTextAreaElement();
60  return element.toConst<WebTextAreaElement>();
61}
62
63}  // namespace
64
65namespace autofill {
66
67PageClickTracker::PageClickTracker(content::RenderView* render_view,
68                                   PageClickListener* listener)
69    : content::RenderViewObserver(render_view),
70      was_focused_before_now_(false),
71      listener_(listener),
72      weak_ptr_factory_(this) {
73}
74
75PageClickTracker::~PageClickTracker() {
76}
77
78void PageClickTracker::DidHandleMouseEvent(const WebMouseEvent& event) {
79  if (event.type != WebInputEvent::MouseDown ||
80      event.button != WebMouseEvent::ButtonLeft) {
81    return;
82  }
83
84  PotentialActivationAt(event.x, event.y);
85}
86
87void PageClickTracker::DidHandleGestureEvent(
88    const blink::WebGestureEvent& event) {
89  if (event.type != blink::WebGestureEvent::GestureTap)
90    return;
91
92  PotentialActivationAt(event.x, event.y);
93}
94
95void PageClickTracker::FocusedNodeChanged(const blink::WebNode& node) {
96  was_focused_before_now_ = false;
97  // If the focus change was a result of handling a click or tap, we'll soon get
98  // an associated event. Reset |was_focused_before_now_| to true only after the
99  // message loop unwinds.
100  base::MessageLoop::current()->PostTask(
101      FROM_HERE,
102      base::Bind(&PageClickTracker::SetWasFocused,
103                 weak_ptr_factory_.GetWeakPtr()));
104}
105
106void PageClickTracker::PotentialActivationAt(int x, int y) {
107  blink::WebNode focused_node = render_view()->GetFocusedElement();
108  if (focused_node.isNull())
109    return;
110
111  if (!render_view()->NodeContainsPoint(focused_node, gfx::Point(x, y)))
112    return;
113
114  const WebInputElement input_element = GetTextWebInputElement(focused_node);
115  if (!input_element.isNull()) {
116    listener_->FormControlElementClicked(input_element,
117                                         was_focused_before_now_);
118    return;
119  }
120
121  const WebTextAreaElement textarea_element =
122      GetWebTextAreaElement(focused_node);
123  if (!textarea_element.isNull()) {
124    listener_->FormControlElementClicked(textarea_element,
125                                         was_focused_before_now_);
126  }
127}
128
129void PageClickTracker::SetWasFocused() {
130  was_focused_before_now_ = true;
131}
132
133}  // namespace autofill
134