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
21 using blink::WebDOMEvent;
22 using blink::WebDOMMouseEvent;
23 using blink::WebElement;
24 using blink::WebFormControlElement;
25 using blink::WebFrame;
26 using blink::WebGestureEvent;
27 using blink::WebInputElement;
28 using blink::WebInputEvent;
29 using blink::WebMouseEvent;
30 using blink::WebNode;
31 using blink::WebString;
32 using blink::WebTextAreaElement;
33 using blink::WebView;
34
35 namespace {
36
37 // Casts |node| to a WebInputElement.
38 // Returns an empty (isNull()) WebInputElement if |node| is not a text field.
GetTextWebInputElement(const WebNode & node)39 const 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.
GetWebTextAreaElement(const WebNode & node)54 const 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
65 namespace autofill {
66
PageClickTracker(content::RenderView * render_view,PageClickListener * listener)67 PageClickTracker::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
~PageClickTracker()75 PageClickTracker::~PageClickTracker() {
76 }
77
DidHandleMouseEvent(const WebMouseEvent & event)78 void 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
DidHandleGestureEvent(const blink::WebGestureEvent & event)87 void 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
FocusedNodeChanged(const blink::WebNode & node)95 void 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
PotentialActivationAt(int x,int y)106 void 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
SetWasFocused()129 void PageClickTracker::SetWasFocused() {
130 was_focused_before_now_ = true;
131 }
132
133 } // namespace autofill
134