1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "AutoFillPopupMenuClient.h"
33
34 #include "CSSStyleSelector.h"
35 #include "CSSValueKeywords.h"
36 #include "Chrome.h"
37 #include "FrameView.h"
38 #include "HTMLInputElement.h"
39 #include "RenderTheme.h"
40 #include "WebAutoFillClient.h"
41 #include "WebNode.h"
42 #include "WebString.h"
43 #include "WebVector.h"
44 #include "WebViewClient.h"
45 #include "WebViewImpl.h"
46
47 using namespace WebCore;
48
49 namespace WebKit {
50
AutoFillPopupMenuClient()51 AutoFillPopupMenuClient::AutoFillPopupMenuClient()
52 : m_separatorIndex(-1)
53 , m_selectedIndex(-1)
54 , m_textField(0)
55 {
56 }
57
~AutoFillPopupMenuClient()58 AutoFillPopupMenuClient::~AutoFillPopupMenuClient()
59 {
60 }
61
getSuggestionsCount() const62 unsigned AutoFillPopupMenuClient::getSuggestionsCount() const
63 {
64 return m_names.size() + ((m_separatorIndex == -1) ? 0 : 1);
65 }
66
getSuggestion(unsigned listIndex) const67 WebString AutoFillPopupMenuClient::getSuggestion(unsigned listIndex) const
68 {
69 int index = convertListIndexToInternalIndex(listIndex);
70 if (index == -1)
71 return WebString();
72
73 ASSERT(index >= 0 && static_cast<size_t>(index) < m_names.size());
74 return m_names[index];
75 }
76
getLabel(unsigned listIndex) const77 WebString AutoFillPopupMenuClient::getLabel(unsigned listIndex) const
78 {
79 int index = convertListIndexToInternalIndex(listIndex);
80 if (index == -1)
81 return WebString();
82
83 ASSERT(index >= 0 && static_cast<size_t>(index) < m_labels.size());
84 return m_labels[index];
85 }
86
getIcon(unsigned listIndex) const87 WebString AutoFillPopupMenuClient::getIcon(unsigned listIndex) const
88 {
89 int index = convertListIndexToInternalIndex(listIndex);
90 if (index == -1)
91 return WebString();
92
93 ASSERT(index >= 0 && static_cast<size_t>(index) < m_icons.size());
94 return m_icons[index];
95 }
96
removeSuggestionAtIndex(unsigned listIndex)97 void AutoFillPopupMenuClient::removeSuggestionAtIndex(unsigned listIndex)
98 {
99 if (!canRemoveSuggestionAtIndex(listIndex))
100 return;
101
102 int index = convertListIndexToInternalIndex(listIndex);
103
104 ASSERT(static_cast<unsigned>(index) < m_names.size());
105
106 m_names.remove(index);
107 m_labels.remove(index);
108 m_icons.remove(index);
109 m_uniqueIDs.remove(index);
110
111 // Shift the separator index if necessary.
112 if (m_separatorIndex != -1)
113 m_separatorIndex--;
114 }
115
canRemoveSuggestionAtIndex(unsigned listIndex)116 bool AutoFillPopupMenuClient::canRemoveSuggestionAtIndex(unsigned listIndex)
117 {
118 // Only allow deletion of items before the separator that have unique id 0
119 // (i.e. are autocomplete rather than autofill items).
120 int index = convertListIndexToInternalIndex(listIndex);
121 return !m_uniqueIDs[index] && (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex));
122 }
123
valueChanged(unsigned listIndex,bool fireEvents)124 void AutoFillPopupMenuClient::valueChanged(unsigned listIndex, bool fireEvents)
125 {
126 WebViewImpl* webView = getWebView();
127 if (!webView)
128 return;
129
130 if (m_separatorIndex != -1 && listIndex > static_cast<unsigned>(m_separatorIndex))
131 --listIndex;
132
133 ASSERT(listIndex < m_names.size());
134
135 webView->autoFillClient()->didAcceptAutoFillSuggestion(WebNode(getTextField()),
136 m_names[listIndex],
137 m_labels[listIndex],
138 m_uniqueIDs[listIndex],
139 listIndex);
140 }
141
selectionChanged(unsigned listIndex,bool fireEvents)142 void AutoFillPopupMenuClient::selectionChanged(unsigned listIndex, bool fireEvents)
143 {
144 WebViewImpl* webView = getWebView();
145 if (!webView)
146 return;
147
148 if (m_separatorIndex != -1 && listIndex > static_cast<unsigned>(m_separatorIndex))
149 --listIndex;
150
151 ASSERT(listIndex < m_names.size());
152
153 webView->autoFillClient()->didSelectAutoFillSuggestion(WebNode(getTextField()),
154 m_names[listIndex],
155 m_labels[listIndex],
156 m_uniqueIDs[listIndex]);
157 }
158
selectionCleared()159 void AutoFillPopupMenuClient::selectionCleared()
160 {
161 WebViewImpl* webView = getWebView();
162 if (webView)
163 webView->autoFillClient()->didClearAutoFillSelection(WebNode(getTextField()));
164 }
165
itemText(unsigned listIndex) const166 String AutoFillPopupMenuClient::itemText(unsigned listIndex) const
167 {
168 return getSuggestion(listIndex);
169 }
170
itemLabel(unsigned listIndex) const171 String AutoFillPopupMenuClient::itemLabel(unsigned listIndex) const
172 {
173 return getLabel(listIndex);
174 }
175
itemIcon(unsigned listIndex) const176 String AutoFillPopupMenuClient::itemIcon(unsigned listIndex) const
177 {
178 return getIcon(listIndex);
179 }
180
itemIsEnabled(unsigned listIndex) const181 bool AutoFillPopupMenuClient::itemIsEnabled(unsigned listIndex) const
182 {
183 return !itemIsWarning(listIndex);
184 }
185
itemStyle(unsigned listIndex) const186 PopupMenuStyle AutoFillPopupMenuClient::itemStyle(unsigned listIndex) const
187 {
188 return itemIsWarning(listIndex) ? *m_warningStyle : *m_regularStyle;
189 }
190
menuStyle() const191 PopupMenuStyle AutoFillPopupMenuClient::menuStyle() const
192 {
193 return *m_regularStyle;
194 }
195
clientPaddingLeft() const196 int AutoFillPopupMenuClient::clientPaddingLeft() const
197 {
198 // Bug http://crbug.com/7708 seems to indicate the style can be 0.
199 RenderStyle* style = textFieldStyle();
200 if (!style)
201 return 0;
202
203 return RenderTheme::defaultTheme()->popupInternalPaddingLeft(style);
204 }
205
clientPaddingRight() const206 int AutoFillPopupMenuClient::clientPaddingRight() const
207 {
208 // Bug http://crbug.com/7708 seems to indicate the style can be 0.
209 RenderStyle* style = textFieldStyle();
210 if (!style)
211 return 0;
212
213 return RenderTheme::defaultTheme()->popupInternalPaddingRight(style);
214 }
215
popupDidHide()216 void AutoFillPopupMenuClient::popupDidHide()
217 {
218 WebViewImpl* webView = getWebView();
219 if (!webView)
220 return;
221
222 webView->autoFillPopupDidHide();
223 webView->autoFillClient()->didClearAutoFillSelection(WebNode(getTextField()));
224 }
225
itemIsSeparator(unsigned listIndex) const226 bool AutoFillPopupMenuClient::itemIsSeparator(unsigned listIndex) const
227 {
228 return (m_separatorIndex != -1 && static_cast<unsigned>(m_separatorIndex) == listIndex);
229 }
230
itemIsWarning(unsigned listIndex) const231 bool AutoFillPopupMenuClient::itemIsWarning(unsigned listIndex) const
232 {
233 int index = convertListIndexToInternalIndex(listIndex);
234 if (index == -1)
235 return false;
236
237 ASSERT(index >= 0 && static_cast<size_t>(index) < m_uniqueIDs.size());
238 return m_uniqueIDs[index] < 0;
239 }
240
setTextFromItem(unsigned listIndex)241 void AutoFillPopupMenuClient::setTextFromItem(unsigned listIndex)
242 {
243 m_textField->setValue(getSuggestion(listIndex));
244 }
245
fontSelector() const246 FontSelector* AutoFillPopupMenuClient::fontSelector() const
247 {
248 return m_textField->document()->styleSelector()->fontSelector();
249 }
250
hostWindow() const251 HostWindow* AutoFillPopupMenuClient::hostWindow() const
252 {
253 return m_textField->document()->view()->hostWindow();
254 }
255
createScrollbar(ScrollableArea * scrollableArea,ScrollbarOrientation orientation,ScrollbarControlSize size)256 PassRefPtr<Scrollbar> AutoFillPopupMenuClient::createScrollbar(
257 ScrollableArea* scrollableArea,
258 ScrollbarOrientation orientation,
259 ScrollbarControlSize size)
260 {
261 return Scrollbar::createNativeScrollbar(scrollableArea, orientation, size);
262 }
263
initialize(HTMLInputElement * textField,const WebVector<WebString> & names,const WebVector<WebString> & labels,const WebVector<WebString> & icons,const WebVector<int> & uniqueIDs,int separatorIndex)264 void AutoFillPopupMenuClient::initialize(
265 HTMLInputElement* textField,
266 const WebVector<WebString>& names,
267 const WebVector<WebString>& labels,
268 const WebVector<WebString>& icons,
269 const WebVector<int>& uniqueIDs,
270 int separatorIndex)
271 {
272 ASSERT(names.size() == labels.size());
273 ASSERT(names.size() == icons.size());
274 ASSERT(names.size() == uniqueIDs.size());
275 ASSERT(separatorIndex < static_cast<int>(names.size()));
276
277 m_selectedIndex = -1;
278 m_textField = textField;
279
280 // The suggestions must be set before initializing the
281 // AutoFillPopupMenuClient.
282 setSuggestions(names, labels, icons, uniqueIDs, separatorIndex);
283
284 FontDescription regularFontDescription;
285 RenderTheme::defaultTheme()->systemFont(CSSValueWebkitControl,
286 regularFontDescription);
287 RenderStyle* style = m_textField->computedStyle();
288 regularFontDescription.setComputedSize(style->fontDescription().computedSize());
289
290 Font regularFont(regularFontDescription, 0, 0);
291 regularFont.update(textField->document()->styleSelector()->fontSelector());
292 // The direction of text in popup menu is set the same as the direction of
293 // the input element: textField.
294 m_regularStyle.set(new PopupMenuStyle(Color::black, Color::white, regularFont,
295 true, false, Length(WebCore::Fixed),
296 textField->renderer()->style()->direction(), textField->renderer()->style()->unicodeBidi() == Override));
297
298 FontDescription warningFontDescription = regularFont.fontDescription();
299 warningFontDescription.setItalic(true);
300 Font warningFont(warningFontDescription, regularFont.letterSpacing(), regularFont.wordSpacing());
301 warningFont.update(regularFont.fontSelector());
302 m_warningStyle.set(new PopupMenuStyle(Color::darkGray,
303 m_regularStyle->backgroundColor(),
304 warningFont,
305 m_regularStyle->isVisible(),
306 m_regularStyle->isDisplayNone(),
307 m_regularStyle->textIndent(),
308 m_regularStyle->textDirection(),
309 m_regularStyle->hasTextDirectionOverride()));
310 }
311
setSuggestions(const WebVector<WebString> & names,const WebVector<WebString> & labels,const WebVector<WebString> & icons,const WebVector<int> & uniqueIDs,int separatorIndex)312 void AutoFillPopupMenuClient::setSuggestions(const WebVector<WebString>& names,
313 const WebVector<WebString>& labels,
314 const WebVector<WebString>& icons,
315 const WebVector<int>& uniqueIDs,
316 int separatorIndex)
317 {
318 ASSERT(names.size() == labels.size());
319 ASSERT(names.size() == icons.size());
320 ASSERT(names.size() == uniqueIDs.size());
321 ASSERT(separatorIndex < static_cast<int>(names.size()));
322
323 m_names.clear();
324 m_labels.clear();
325 m_icons.clear();
326 m_uniqueIDs.clear();
327 for (size_t i = 0; i < names.size(); ++i) {
328 m_names.append(names[i]);
329 m_labels.append(labels[i]);
330 m_icons.append(icons[i]);
331 m_uniqueIDs.append(uniqueIDs[i]);
332 }
333
334 m_separatorIndex = separatorIndex;
335
336 // Try to preserve selection if possible.
337 if (getSelectedIndex() >= static_cast<int>(names.size()))
338 setSelectedIndex(-1);
339 }
340
convertListIndexToInternalIndex(unsigned listIndex) const341 int AutoFillPopupMenuClient::convertListIndexToInternalIndex(unsigned listIndex) const
342 {
343 if (listIndex == static_cast<unsigned>(m_separatorIndex))
344 return -1;
345
346 if (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex))
347 return listIndex;
348 return listIndex - 1;
349 }
350
getWebView() const351 WebViewImpl* AutoFillPopupMenuClient::getWebView() const
352 {
353 Frame* frame = m_textField->document()->frame();
354 if (!frame)
355 return 0;
356
357 Page* page = frame->page();
358 if (!page)
359 return 0;
360
361 return static_cast<WebViewImpl*>(page->chrome()->client()->webView());
362 }
363
textFieldStyle() const364 RenderStyle* AutoFillPopupMenuClient::textFieldStyle() const
365 {
366 RenderStyle* style = m_textField->computedStyle();
367 if (!style) {
368 // It seems we can only have a 0 style in a TextField if the
369 // node is detached, in which case we the popup should not be
370 // showing. Please report this in http://crbug.com/7708 and
371 // include the page you were visiting.
372 ASSERT_NOT_REACHED();
373 }
374 return style;
375 }
376
377 } // namespace WebKit
378