Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: webkit/glue/autocomplete_input_listener.cc

Issue 11479: New take at implementing autofill using the editor client API (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « webkit/build/glue/glue.vcproj ('k') | webkit/glue/autocomplete_input_listener_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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 // This file provides an abstract implementation of the inline autocomplete
6 // infrastructure defined in autocomplete_input_listener.h.
7
8 #include "webkit/glue/autocomplete_input_listener.h"
9 #include <set>
10
11 MSVC_PUSH_WARNING_LEVEL(0);
12 #include "HTMLInputElement.h"
13 #include "HTMLFormElement.h"
14 #include "Document.h"
15 #include "Frame.h"
16 #include "Editor.h"
17 #include "EventNames.h"
18 #include "Event.h"
19 #include "HTMLNames.h"
20 MSVC_POP_WARNING();
21
22 #undef LOG
23
24 #include "base/logging.h"
25 #include "webkit/glue/editor_client_impl.h"
26 #include "webkit/glue/glue_util.h"
27
28 namespace webkit_glue {
29
30 // Hack (1 of 2) for http://bugs.webkit.org/show_bug.cgi?id=16976. This bug
31 // causes the caret position to be set after handling input events, which
32 // trumps our modifications, so for now we tell the EditorClient to preserve
33 // whatever selection set by our code.
34 // TODO(timsteele): Remove this function altogether once bug is fixed.
35 static void PreserveSelection(WebCore::HTMLInputElement* element) {
36 WebCore::EditorClient* ec =
37 element->form()->document()->frame()->editor()->client();
38 EditorClientImpl* client = static_cast<EditorClientImpl*>(ec);
39 client->PreserveSelection();
40 }
41
42 HTMLInputDelegate::HTMLInputDelegate(WebCore::HTMLInputElement* element)
43 : element_(element) {
44 // Reference the element for the lifetime of this delegate.
45 // e is NULL when testing.
46 if (element_)
47 element_->ref();
48 }
49
50 HTMLInputDelegate::~HTMLInputDelegate() {
51 if (element_)
52 element_->deref();
53 }
54
55 void HTMLInputDelegate::SetValue(const std::wstring& value) {
56 element_->setValue(StdWStringToString(value));
57 }
58
59 void HTMLInputDelegate::SetSelectionRange(size_t start, size_t end) {
60 element_->setSelectionRange(start, end);
61 // Hack, see comments for PreserveSelection().
62 PreserveSelection(element_);
63 }
64
65 void HTMLInputDelegate::OnFinishedAutocompleting() {
66 // This sets the input element to an autofilled state which will result in it
67 // having a yellow background.
68 element_->setAutofilled(true);
69 // Notify any changeEvent listeners.
70 element_->onChange();
71 }
72
73 AutocompleteBodyListener::AutocompleteBodyListener(WebCore::Frame* frame) {
74 WebCore::HTMLElement* body = frame->document()->body();
75 body->addEventListener(WebCore::eventNames().DOMFocusOutEvent, this, false);
76 body->addEventListener(WebCore::eventNames().inputEvent, this, false);
77 // Attaching to the WebCore body element effectively transfers ownership of
78 // the listener object. When WebCore is tearing down the document, any
79 // attached listeners are destroyed.
80 // See Document::removeAllEventListenersFromAllNodes which is called by
81 // FrameLoader::stopLoading. Also, there is no need for matching calls to
82 // removeEventListener because the simplest and most convienient thing to do
83 // for autocompletion is to stop listening once the element is destroyed.
84 }
85
86 AutocompleteBodyListener::~AutocompleteBodyListener() {
87 // Delete the listener. Pay special attention since we may have the same
88 // listener registered for several elements.
89 std::set<AutocompleteInputListener*> to_be_deleted_;
90 for (InputElementInfoMap::iterator iter = elements_info_.begin();
91 iter != elements_info_.end(); ++iter) {
92 to_be_deleted_.insert(iter->second.listener);
93 }
94
95 std::set<AutocompleteInputListener*>::iterator iter;
96 for (iter = to_be_deleted_.begin(); iter != to_be_deleted_.end(); ++iter)
97 delete *iter;
98 elements_info_.clear();
99 }
100
101 // The following method is based on Firefox2 code in
102 // toolkit/components/autocomplete/src/nsAutoCompleteController.cpp
103 // Its license block is
104 //
105 /* ***** BEGIN LICENSE BLOCK *****
106 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
107 *
108 * The contents of this file are subject to the Mozilla Public License Version
109 * 1.1 (the "License"); you may not use this file except in compliance with
110 * the License. You may obtain a copy of the License at
111 * http://www.mozilla.org/MPL/
112 *
113 * Software distributed under the License is distributed on an "AS IS" basis,
114 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
115 * for the specific language governing rights and limitations under the
116 * License.
117 *
118 * The Original Code is Mozilla Communicator client code.
119 *
120 * The Initial Developer of the Original Code is
121 * Netscape Communications Corporation.
122 * Portions created by the Initial Developer are Copyright (C) 1998
123 * the Initial Developer. All Rights Reserved.
124 *
125 * Contributor(s):
126 * Joe Hewitt <hewitt@netscape.com> (Original Author)
127 * Dean Tessman <dean_tessman@hotmail.com>
128 * Johnny Stenback <jst@mozilla.jstenback.com>
129 * Masayuki Nakano <masayuki@d-toybox.com>
130 *
131 * Alternatively, the contents of this file may be used under the terms of
132 * either the GNU General Public License Version 2 or later (the "GPL"), or
133 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
134 * in which case the provisions of the GPL or the LGPL are applicable instead
135 * of those above. If you wish to allow use of your version of this file only
136 * under the terms of either the GPL or the LGPL, and not to allow others to
137 * use your version of this file under the terms of the MPL, indicate your
138 * decision by deleting the provisions above and replace them with the notice
139 * and other provisions required by the GPL or the LGPL. If you do not delete
140 * the provisions above, a recipient may use your version of this file under
141 * the terms of any one of the MPL, the GPL or the LGPL.
142 *
143 * ***** END LICENSE BLOCK ***** */
144 bool AutocompleteBodyListener::ShouldInlineAutocomplete(
145 WebCore::HTMLInputElement* input,
146 const std::wstring& old_text,
147 const std::wstring& new_text) {
148 size_t prev_length = old_text.length();
149 // The following are a bunch of early returns in cases we don't want to
150 // go through with inline autocomplete.
151
152 // Don't bother doing AC if nothing changed.
153 if (new_text.length() > 0 && (new_text == old_text))
154 return false;
155
156 // Did user backspace?
157 if ((new_text.length() < old_text.length()) &&
158 old_text.substr(0, new_text.length()) == new_text) {
159 return false;
160 }
161
162 // Is search string empty?
163 if (new_text.empty())
164 return false;
165 return IsCaretAtEndOfText(input, new_text.length(), prev_length);
166 }
167
168 void AutocompleteBodyListener::handleEvent(WebCore::Event* event,
169 bool /*is_window_event*/) {
170 const WebCore::AtomicString& webcore_type = event->type();
171 DCHECK(event->target()->toNode());
172 if (!event->target()->toNode()->hasTagName(WebCore::HTMLNames::inputTag))
173 return; // Not a node of interest to us.
174
175 WebCore::HTMLInputElement* input =
176 static_cast<WebCore::HTMLInputElement*>(event->target()->toNode());
177 InputElementInfoMap::const_iterator iter = elements_info_.find(input);
178 if (iter == elements_info_.end())
179 return; // Not an input node we are listening to.
180
181 InputElementInfo input_info = iter->second;
182 const std::wstring& user_input = StringToStdWString(input->value());
183 if (webcore_type == WebCore::eventNames().DOMFocusOutEvent) {
184 input_info.listener->OnBlur(input, user_input);
185 } else if (webcore_type == WebCore::eventNames().inputEvent) {
186 // Perform inline autocomplete if it is safe to do so.
187 if (ShouldInlineAutocomplete(input,
188 input_info.previous_text, user_input)) {
189 input_info.listener->OnInlineAutocompleteNeeded(input, user_input);
190 }
191 // Update the info.
192 input_info.previous_text = user_input;
193 elements_info_[input] = input_info;
194 } else {
195 NOTREACHED() << "unexpected EventName for autocomplete listener";
196 }
197 }
198
199 void AutocompleteBodyListener::AddInputListener(
200 WebCore::HTMLInputElement* element,
201 AutocompleteInputListener* listener) {
202 DCHECK(elements_info_.find(element) == elements_info_.end());
203 InputElementInfo elem_info;
204 elem_info.listener = listener;
205 elements_info_[element] = elem_info;
206 }
207
208 bool AutocompleteBodyListener::IsCaretAtEndOfText(
209 WebCore::HTMLInputElement* element,
210 size_t input_length,
211 size_t previous_length) const {
212 // Hack 2 of 2 for http://bugs.webkit.org/show_bug.cgi?id=16976.
213 // TODO(timsteele): This check should only return early if
214 // !(selectionEnd == selectionStart == user_input.length()).
215 // However, because of webkit bug #16976 the caret is not properly moved
216 // until after the handlers have executed, so for now we do the following
217 // several checks. The first check handles the case webkit sets the End
218 // selection but not the Start selection correctly, and the second is for
219 // when webcore sets neither. This won't be perfect if the user moves the
220 // selection around during inline autocomplete, but for now its the
221 // friendliest behavior we can offer. Once the bug is fixed this method
222 // should no longer need the previous_length parameter.
223 if (((element->selectionEnd() != element->selectionStart() + 1) ||
224 (element->selectionEnd() != static_cast<int>(input_length))) &&
225 ((element->selectionEnd() != element->selectionStart()) ||
226 (element->selectionEnd() != static_cast<int>(previous_length)))) {
227 return false;
228 }
229 return true;
230 }
231 } // webkit_glue
OLDNEW
« no previous file with comments | « webkit/build/glue/glue.vcproj ('k') | webkit/glue/autocomplete_input_listener_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698