66cb82bd59f2098912b16adb6eff1d0ce5db27a0
[WebKit-https.git] / Source / WebKit / chromium / src / AutofillPopupMenuClient.cpp
1 /*
2  * Copyright (C) 2011 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 "CSSFontSelector.h"
35 #include "CSSValueKeywords.h"
36 #include "Chrome.h"
37 #include "Frame.h"
38 #include "FrameView.h"
39 #include "HTMLInputElement.h"
40 #include "Page.h"
41 #include "RenderTheme.h"
42 #include "StyleResolver.h"
43 #include "WebAutofillClient.h"
44 #include "WebNode.h"
45 #include "WebViewClient.h"
46 #include "WebViewImpl.h"
47 #include <public/WebString.h>
48 #include <public/WebVector.h>
49
50 using namespace WebCore;
51
52 namespace WebKit {
53
54 AutofillPopupMenuClient::AutofillPopupMenuClient()
55     : m_selectedIndex(-1)
56     , m_textField(0)
57     , m_useLegacyBehavior(false)
58 {
59 }
60
61 AutofillPopupMenuClient::~AutofillPopupMenuClient()
62 {
63 }
64
65 unsigned AutofillPopupMenuClient::getSuggestionsCount() const
66 {
67     return m_names.size();
68 }
69
70 WebString AutofillPopupMenuClient::getSuggestion(unsigned listIndex) const
71 {
72     ASSERT(listIndex < m_names.size());
73     return m_names[listIndex];
74 }
75
76 WebString AutofillPopupMenuClient::getLabel(unsigned listIndex) const
77 {
78     ASSERT(listIndex < m_labels.size());
79     return m_labels[listIndex];
80 }
81
82 WebString AutofillPopupMenuClient::getIcon(unsigned listIndex) const
83 {
84     ASSERT(listIndex < m_icons.size());
85     return m_icons[listIndex];
86 }
87
88 void AutofillPopupMenuClient::removeSuggestionAtIndex(unsigned listIndex)
89 {
90     if (!canRemoveSuggestionAtIndex(listIndex))
91         return;
92
93     ASSERT(listIndex < m_names.size());
94
95     m_names.remove(listIndex);
96     m_labels.remove(listIndex);
97     m_icons.remove(listIndex);
98     m_itemIDs.remove(listIndex);
99 }
100
101 bool AutofillPopupMenuClient::canRemoveSuggestionAtIndex(unsigned listIndex)
102 {
103     return m_itemIDs[listIndex] == WebAutofillClient::MenuItemIDAutocompleteEntry || m_itemIDs[listIndex] == WebAutofillClient::MenuItemIDPasswordEntry;
104 }
105
106 void AutofillPopupMenuClient::valueChanged(unsigned listIndex, bool fireEvents)
107 {
108     WebViewImpl* webView = getWebView();
109     if (!webView)
110         return;
111
112     ASSERT(listIndex < m_names.size());
113
114     if (m_useLegacyBehavior) {
115         for (size_t i = 0; i < m_itemIDs.size(); ++i) {
116             if (m_itemIDs[i] == WebAutofillClient::MenuItemIDSeparator) {
117                 if (listIndex > i)
118                     listIndex--;
119                 break;
120             }
121         }
122     }
123
124     webView->autofillClient()->didAcceptAutofillSuggestion(WebNode(getTextField()),
125                                                            m_names[listIndex],
126                                                            m_labels[listIndex],
127                                                            m_itemIDs[listIndex],
128                                                            listIndex);
129 }
130
131 void AutofillPopupMenuClient::selectionChanged(unsigned listIndex, bool fireEvents)
132 {
133     WebViewImpl* webView = getWebView();
134     if (!webView)
135         return;
136
137     ASSERT(listIndex < m_names.size());
138
139     webView->autofillClient()->didSelectAutofillSuggestion(WebNode(getTextField()),
140                                                            m_names[listIndex],
141                                                            m_labels[listIndex],
142                                                            m_itemIDs[listIndex]);
143 }
144
145 void AutofillPopupMenuClient::selectionCleared()
146 {
147     WebViewImpl* webView = getWebView();
148     if (webView)
149         webView->autofillClient()->didClearAutofillSelection(WebNode(getTextField()));
150 }
151
152 String AutofillPopupMenuClient::itemText(unsigned listIndex) const
153 {
154     return getSuggestion(listIndex);
155 }
156
157 String AutofillPopupMenuClient::itemLabel(unsigned listIndex) const
158 {
159     return getLabel(listIndex);
160 }
161
162 String AutofillPopupMenuClient::itemIcon(unsigned listIndex) const
163 {
164     return getIcon(listIndex);
165 }
166
167 bool AutofillPopupMenuClient::itemIsEnabled(unsigned listIndex) const
168 {
169     return !itemIsWarning(listIndex);
170 }
171
172 PopupMenuStyle AutofillPopupMenuClient::itemStyle(unsigned listIndex) const
173 {
174     return itemIsWarning(listIndex) ? *m_warningStyle : *m_regularStyle;
175 }
176
177 PopupMenuStyle AutofillPopupMenuClient::menuStyle() const
178 {
179     return *m_regularStyle;
180 }
181
182 WebCore::LayoutUnit AutofillPopupMenuClient::clientPaddingLeft() const
183 {
184     // Bug http://crbug.com/7708 seems to indicate the style can be 0.
185     RenderStyle* style = textFieldStyle();
186     if (!style)
187        return 0;
188
189     return RenderTheme::defaultTheme()->popupInternalPaddingLeft(style);
190 }
191
192 WebCore::LayoutUnit AutofillPopupMenuClient::clientPaddingRight() const
193 {
194     // Bug http://crbug.com/7708 seems to indicate the style can be 0.
195     RenderStyle* style = textFieldStyle();
196     if (!style)
197         return 0;
198
199     return RenderTheme::defaultTheme()->popupInternalPaddingRight(style);
200 }
201
202 void AutofillPopupMenuClient::popupDidHide()
203 {
204     WebViewImpl* webView = getWebView();
205     if (!webView)
206         return;
207
208     webView->autofillPopupDidHide();
209     webView->autofillClient()->didClearAutofillSelection(WebNode(getTextField()));
210 }
211
212 bool AutofillPopupMenuClient::itemIsSeparator(unsigned listIndex) const
213 {
214     return m_itemIDs[listIndex] == WebAutofillClient::MenuItemIDSeparator;
215 }
216
217 bool AutofillPopupMenuClient::itemIsWarning(unsigned listIndex) const
218 {
219     return m_itemIDs[listIndex] == WebAutofillClient::MenuItemIDWarningMessage;
220 }
221
222 void AutofillPopupMenuClient::setTextFromItem(unsigned listIndex)
223 {
224     m_textField->setValue(getSuggestion(listIndex));
225 }
226
227 FontSelector* AutofillPopupMenuClient::fontSelector() const
228 {
229     return m_textField->document()->styleResolver()->fontSelector();
230 }
231
232 HostWindow* AutofillPopupMenuClient::hostWindow() const
233 {
234     return m_textField->document()->view()->hostWindow();
235 }
236
237 PassRefPtr<Scrollbar> AutofillPopupMenuClient::createScrollbar(
238     ScrollableArea* scrollableArea,
239     ScrollbarOrientation orientation,
240     ScrollbarControlSize size)
241 {
242     return Scrollbar::createNativeScrollbar(scrollableArea, orientation, size);
243 }
244
245 void AutofillPopupMenuClient::initialize(
246     HTMLInputElement* textField,
247     const WebVector<WebString>& names,
248     const WebVector<WebString>& labels,
249     const WebVector<WebString>& icons,
250     const WebVector<int>& itemIDs,
251     int separatorIndex)
252 {
253     ASSERT(names.size() == labels.size());
254     ASSERT(names.size() == icons.size());
255     ASSERT(names.size() == itemIDs.size());
256
257     m_selectedIndex = -1;
258     m_textField = textField;
259
260     if (separatorIndex == -1) {
261         // The suggestions must be set before initializing the
262         // AutofillPopupMenuClient.
263         setSuggestions(names, labels, icons, itemIDs);
264     } else {
265         m_useLegacyBehavior = true;
266         WebVector<WebString> namesWithSeparator(names.size() + 1);
267         WebVector<WebString> labelsWithSeparator(labels.size() + 1);
268         WebVector<WebString> iconsWithSeparator(icons.size() + 1);
269         WebVector<int> itemIDsWithSeparator(itemIDs.size() + 1);
270         for (size_t i = 0; i < names.size(); ++i) {
271             size_t j = i < static_cast<size_t>(separatorIndex) ? i : i + 1;
272             namesWithSeparator[j] = names[i];
273             labelsWithSeparator[j] = labels[i];
274             iconsWithSeparator[j] = icons[i];
275             itemIDsWithSeparator[j] = itemIDs[i];
276         }
277         itemIDsWithSeparator[separatorIndex] = WebAutofillClient::MenuItemIDSeparator;
278         setSuggestions(namesWithSeparator, labelsWithSeparator, iconsWithSeparator, itemIDsWithSeparator);
279     }
280
281     FontDescription regularFontDescription;
282     RenderTheme::defaultTheme()->systemFont(CSSValueWebkitControl,
283                                             regularFontDescription);
284     RenderStyle* style = m_textField->computedStyle();
285     regularFontDescription.setComputedSize(style->fontDescription().computedSize());
286
287     Font regularFont(regularFontDescription, 0, 0);
288     regularFont.update(textField->document()->styleResolver()->fontSelector());
289     // The direction of text in popup menu is set the same as the direction of
290     // the input element: textField.
291     m_regularStyle = adoptPtr(new PopupMenuStyle(Color::black, Color::white, regularFont, true, false,
292                                                  Length(WebCore::Fixed), textField->renderer()->style()->direction(),
293                                                  textField->renderer()->style()->unicodeBidi() == Override,
294                                                  PopupMenuStyle::AutofillPopup));
295
296     FontDescription warningFontDescription = regularFont.fontDescription();
297     warningFontDescription.setItalic(true);
298     Font warningFont(warningFontDescription, regularFont.letterSpacing(), regularFont.wordSpacing());
299     warningFont.update(regularFont.fontSelector());
300     m_warningStyle = adoptPtr(new PopupMenuStyle(Color::darkGray, m_regularStyle->backgroundColor(), warningFont,
301                                                  m_regularStyle->isVisible(), m_regularStyle->isDisplayNone(),
302                                                  m_regularStyle->textIndent(), m_regularStyle->textDirection(),
303                                                  m_regularStyle->hasTextDirectionOverride(),
304                                                  PopupMenuStyle::AutofillPopup));
305 }
306
307 void AutofillPopupMenuClient::setSuggestions(const WebVector<WebString>& names,
308                                              const WebVector<WebString>& labels,
309                                              const WebVector<WebString>& icons,
310                                              const WebVector<int>& itemIDs)
311 {
312     ASSERT(names.size() == labels.size());
313     ASSERT(names.size() == icons.size());
314     ASSERT(names.size() == itemIDs.size());
315
316     m_names.clear();
317     m_labels.clear();
318     m_icons.clear();
319     m_itemIDs.clear();
320     for (size_t i = 0; i < names.size(); ++i) {
321         m_names.append(names[i]);
322         m_labels.append(labels[i]);
323         m_icons.append(icons[i]);
324         m_itemIDs.append(itemIDs[i]);
325     }
326
327     // Try to preserve selection if possible.
328     if (getSelectedIndex() >= static_cast<int>(names.size()))
329         setSelectedIndex(-1);
330 }
331
332 WebViewImpl* AutofillPopupMenuClient::getWebView() const
333 {
334     Frame* frame = m_textField->document()->frame();
335     if (!frame)
336         return 0;
337
338     Page* page = frame->page();
339     if (!page)
340         return 0;
341
342     return static_cast<WebViewImpl*>(page->chrome()->client()->webView());
343 }
344
345 RenderStyle* AutofillPopupMenuClient::textFieldStyle() const
346 {
347     RenderStyle* style = m_textField->computedStyle();
348     if (!style) {
349         // It seems we can only have a 0 style in a TextField if the
350         // node is detached, in which case we the popup should not be
351         // showing.  Please report this in http://crbug.com/7708 and
352         // include the page you were visiting.
353         ASSERT_NOT_REACHED();
354     }
355     return style;
356 }
357
358 } // namespace WebKit