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