|
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 "WebNode.h" |
|
41 #include "WebString.h" |
|
42 #include "WebVector.h" |
|
43 #include "WebViewClient.h" |
|
44 #include "WebViewImpl.h" |
|
45 |
|
46 using namespace WebCore; |
|
47 |
|
48 namespace WebKit { |
|
49 |
|
50 AutoFillPopupMenuClient::AutoFillPopupMenuClient() |
|
51 : m_separatorIndex(-1) |
|
52 , m_selectedIndex(-1) |
|
53 , m_textField(0) |
|
54 , m_AutocompleteModeEnabled(false) |
|
55 { |
|
56 } |
|
57 |
|
58 AutoFillPopupMenuClient::~AutoFillPopupMenuClient() |
|
59 { |
|
60 } |
|
61 |
|
62 unsigned AutoFillPopupMenuClient::getSuggestionsCount() const |
|
63 { |
|
64 return m_names.size() + ((m_separatorIndex == -1) ? 0 : 1); |
|
65 } |
|
66 |
|
67 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 |
|
77 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 |
|
87 void AutoFillPopupMenuClient::removeSuggestionAtIndex(unsigned listIndex) |
|
88 { |
|
89 if (!canRemoveSuggestionAtIndex(listIndex)) |
|
90 return; |
|
91 |
|
92 int index = convertListIndexToInternalIndex(listIndex); |
|
93 |
|
94 ASSERT(static_cast<unsigned>(index) < m_names.size()); |
|
95 |
|
96 m_names.remove(index); |
|
97 m_labels.remove(index); |
|
98 |
|
99 // Shift the separator index if necessary. |
|
100 if (m_separatorIndex != -1) |
|
101 m_separatorIndex--; |
|
102 } |
|
103 |
|
104 bool AutoFillPopupMenuClient::canRemoveSuggestionAtIndex(unsigned listIndex) |
|
105 { |
|
106 // Only allow deletion of items before the separator and those that don't |
|
107 // have a label (autocomplete). |
|
108 int index = convertListIndexToInternalIndex(listIndex); |
|
109 return m_labels[index].isEmpty() && (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex)); |
|
110 } |
|
111 |
|
112 void AutoFillPopupMenuClient::valueChanged(unsigned listIndex, bool fireEvents) |
|
113 { |
|
114 // DEPRECATED: Will be removed once AutoFill and Autocomplete merge is |
|
115 // completed. |
|
116 if (m_AutocompleteModeEnabled) { |
|
117 m_textField->setValue(getSuggestion(listIndex)); |
|
118 |
|
119 WebViewImpl* webView = getWebView(); |
|
120 if (!webView) |
|
121 return; |
|
122 |
|
123 EditorClientImpl* editor = |
|
124 static_cast<EditorClientImpl*>(webView->page()->editorClient()); |
|
125 ASSERT(editor); |
|
126 editor->onAutocompleteSuggestionAccepted( |
|
127 static_cast<HTMLInputElement*>(m_textField.get())); |
|
128 } else { |
|
129 WebViewImpl* webView = getWebView(); |
|
130 if (!webView) |
|
131 return; |
|
132 |
|
133 if (m_separatorIndex != -1 && listIndex > static_cast<unsigned>(m_separatorIndex)) |
|
134 --listIndex; |
|
135 |
|
136 ASSERT(listIndex < m_names.size()); |
|
137 |
|
138 webView->client()->didAcceptAutoFillSuggestion(WebNode(getTextField()), |
|
139 m_names[listIndex], |
|
140 m_labels[listIndex], |
|
141 m_uniqueIDs[listIndex], |
|
142 listIndex); |
|
143 } |
|
144 } |
|
145 |
|
146 void AutoFillPopupMenuClient::selectionChanged(unsigned listIndex, bool fireEvents) |
|
147 { |
|
148 WebViewImpl* webView = getWebView(); |
|
149 if (!webView) |
|
150 return; |
|
151 |
|
152 if (m_separatorIndex != -1 && listIndex > static_cast<unsigned>(m_separatorIndex)) |
|
153 --listIndex; |
|
154 |
|
155 ASSERT(listIndex < m_names.size()); |
|
156 |
|
157 webView->client()->didSelectAutoFillSuggestion(WebNode(getTextField()), |
|
158 m_names[listIndex], |
|
159 m_labels[listIndex], |
|
160 m_uniqueIDs[listIndex]); |
|
161 } |
|
162 |
|
163 void AutoFillPopupMenuClient::selectionCleared() |
|
164 { |
|
165 WebViewImpl* webView = getWebView(); |
|
166 if (webView) |
|
167 webView->client()->didClearAutoFillSelection(WebNode(getTextField())); |
|
168 } |
|
169 |
|
170 String AutoFillPopupMenuClient::itemText(unsigned listIndex) const |
|
171 { |
|
172 return getSuggestion(listIndex); |
|
173 } |
|
174 |
|
175 String AutoFillPopupMenuClient::itemLabel(unsigned listIndex) const |
|
176 { |
|
177 return getLabel(listIndex); |
|
178 } |
|
179 |
|
180 PopupMenuStyle AutoFillPopupMenuClient::itemStyle(unsigned listIndex) const |
|
181 { |
|
182 return *m_style; |
|
183 } |
|
184 |
|
185 PopupMenuStyle AutoFillPopupMenuClient::menuStyle() const |
|
186 { |
|
187 return *m_style; |
|
188 } |
|
189 |
|
190 int AutoFillPopupMenuClient::clientPaddingLeft() const |
|
191 { |
|
192 // Bug http://crbug.com/7708 seems to indicate the style can be 0. |
|
193 RenderStyle* style = textFieldStyle(); |
|
194 if (!style) |
|
195 return 0; |
|
196 |
|
197 return RenderTheme::defaultTheme()->popupInternalPaddingLeft(style); |
|
198 } |
|
199 |
|
200 int AutoFillPopupMenuClient::clientPaddingRight() const |
|
201 { |
|
202 // Bug http://crbug.com/7708 seems to indicate the style can be 0. |
|
203 RenderStyle* style = textFieldStyle(); |
|
204 if (!style) |
|
205 return 0; |
|
206 |
|
207 return RenderTheme::defaultTheme()->popupInternalPaddingRight(style); |
|
208 } |
|
209 |
|
210 void AutoFillPopupMenuClient::popupDidHide() |
|
211 { |
|
212 WebViewImpl* webView = getWebView(); |
|
213 if (!webView) |
|
214 return; |
|
215 |
|
216 webView->autoFillPopupDidHide(); |
|
217 webView->client()->didClearAutoFillSelection(WebNode(getTextField())); |
|
218 } |
|
219 |
|
220 bool AutoFillPopupMenuClient::itemIsSeparator(unsigned listIndex) const |
|
221 { |
|
222 return (m_separatorIndex != -1 && static_cast<unsigned>(m_separatorIndex) == listIndex); |
|
223 } |
|
224 |
|
225 void AutoFillPopupMenuClient::setTextFromItem(unsigned listIndex) |
|
226 { |
|
227 m_textField->setValue(getSuggestion(listIndex)); |
|
228 } |
|
229 |
|
230 FontSelector* AutoFillPopupMenuClient::fontSelector() const |
|
231 { |
|
232 return m_textField->document()->styleSelector()->fontSelector(); |
|
233 } |
|
234 |
|
235 HostWindow* AutoFillPopupMenuClient::hostWindow() const |
|
236 { |
|
237 return m_textField->document()->view()->hostWindow(); |
|
238 } |
|
239 |
|
240 PassRefPtr<Scrollbar> AutoFillPopupMenuClient::createScrollbar( |
|
241 ScrollbarClient* client, |
|
242 ScrollbarOrientation orientation, |
|
243 ScrollbarControlSize size) |
|
244 { |
|
245 return Scrollbar::createNativeScrollbar(client, orientation, size); |
|
246 } |
|
247 |
|
248 void AutoFillPopupMenuClient::initialize( |
|
249 HTMLInputElement* textField, |
|
250 const WebVector<WebString>& names, |
|
251 const WebVector<WebString>& labels, |
|
252 const WebVector<int>& uniqueIDs, |
|
253 int separatorIndex) |
|
254 { |
|
255 ASSERT(names.size() == labels.size()); |
|
256 ASSERT(names.size() == uniqueIDs.size()); |
|
257 ASSERT(separatorIndex < static_cast<int>(names.size())); |
|
258 |
|
259 m_selectedIndex = -1; |
|
260 m_textField = textField; |
|
261 |
|
262 // The suggestions must be set before initializing the |
|
263 // AutoFillPopupMenuClient. |
|
264 setSuggestions(names, labels, uniqueIDs, separatorIndex); |
|
265 |
|
266 FontDescription fontDescription; |
|
267 RenderTheme::defaultTheme()->systemFont(CSSValueWebkitControl, |
|
268 fontDescription); |
|
269 RenderStyle* style = m_textField->computedStyle(); |
|
270 fontDescription.setComputedSize(style->fontDescription().computedSize()); |
|
271 |
|
272 Font font(fontDescription, 0, 0); |
|
273 font.update(textField->document()->styleSelector()->fontSelector()); |
|
274 // The direction of text in popup menu is set the same as the direction of |
|
275 // the input element: textField. |
|
276 m_style.set(new PopupMenuStyle(Color::black, Color::white, font, true, |
|
277 Length(WebCore::Fixed), |
|
278 textField->renderer()->style()->direction())); |
|
279 } |
|
280 |
|
281 void AutoFillPopupMenuClient::setSuggestions(const WebVector<WebString>& names, |
|
282 const WebVector<WebString>& labels, |
|
283 const WebVector<int>& uniqueIDs, |
|
284 int separatorIndex) |
|
285 { |
|
286 ASSERT(names.size() == labels.size()); |
|
287 ASSERT(names.size() == uniqueIDs.size()); |
|
288 ASSERT(separatorIndex < static_cast<int>(names.size())); |
|
289 |
|
290 m_names.clear(); |
|
291 m_labels.clear(); |
|
292 m_uniqueIDs.clear(); |
|
293 for (size_t i = 0; i < names.size(); ++i) { |
|
294 m_names.append(names[i]); |
|
295 m_labels.append(labels[i]); |
|
296 m_uniqueIDs.append(uniqueIDs[i]); |
|
297 } |
|
298 |
|
299 m_separatorIndex = separatorIndex; |
|
300 |
|
301 // Try to preserve selection if possible. |
|
302 if (getSelectedIndex() >= static_cast<int>(names.size())) |
|
303 setSelectedIndex(-1); |
|
304 } |
|
305 |
|
306 int AutoFillPopupMenuClient::convertListIndexToInternalIndex(unsigned listIndex) const |
|
307 { |
|
308 if (listIndex == static_cast<unsigned>(m_separatorIndex)) |
|
309 return -1; |
|
310 |
|
311 if (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex)) |
|
312 return listIndex; |
|
313 return listIndex - 1; |
|
314 } |
|
315 |
|
316 WebViewImpl* AutoFillPopupMenuClient::getWebView() const |
|
317 { |
|
318 Frame* frame = m_textField->document()->frame(); |
|
319 if (!frame) |
|
320 return 0; |
|
321 |
|
322 Page* page = frame->page(); |
|
323 if (!page) |
|
324 return 0; |
|
325 |
|
326 return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView(); |
|
327 } |
|
328 |
|
329 RenderStyle* AutoFillPopupMenuClient::textFieldStyle() const |
|
330 { |
|
331 RenderStyle* style = m_textField->computedStyle(); |
|
332 if (!style) { |
|
333 // It seems we can only have a 0 style in a TextField if the |
|
334 // node is detached, in which case we the popup should not be |
|
335 // showing. Please report this in http://crbug.com/7708 and |
|
336 // include the page you were visiting. |
|
337 ASSERT_NOT_REACHED(); |
|
338 } |
|
339 return style; |
|
340 } |
|
341 |
|
342 } // namespace WebKit |