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 "CSSValueKeywords.h" 35 #include "WebAutofillClient.h" 36 #include "WebNode.h" 37 #include "WebViewClient.h" 38 #include "WebViewImpl.h" 39 #include "core/css/CSSFontSelector.h" 40 #include "core/css/resolver/StyleResolver.h" 41 #include "core/html/HTMLInputElement.h" 42 #include "core/page/Chrome.h" 43 #include "core/page/Frame.h" 44 #include "core/page/FrameView.h" 45 #include "core/page/Page.h" 46 #include "core/rendering/RenderTheme.h" 47 #include "public/platform/WebString.h" 48 #include "public/platform/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_WITH_SECURITY_IMPLICATION(listIndex < m_names.size()); 73 return m_names[listIndex]; 74 } 75 76 WebString AutofillPopupMenuClient::getLabel(unsigned listIndex) const 77 { 78 ASSERT_WITH_SECURITY_IMPLICATION(listIndex < m_labels.size()); 79 return m_labels[listIndex]; 80 } 81 82 WebString AutofillPopupMenuClient::getIcon(unsigned listIndex) const 83 { 84 ASSERT_WITH_SECURITY_IMPLICATION(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_WITH_SECURITY_IMPLICATION(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_WITH_SECURITY_IMPLICATION(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_WITH_SECURITY_IMPLICATION(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::CustomBackgroundColor, 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::CustomBackgroundColor, 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 359