AutoFillPopupMenuClient.cpp revision 65f03d4f644ce73618e5f4f50dd694b26f55ae12
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 47using namespace WebCore; 48 49namespace WebKit { 50 51AutoFillPopupMenuClient::AutoFillPopupMenuClient() 52 : m_separatorIndex(-1) 53 , m_selectedIndex(-1) 54 , m_textField(0) 55{ 56} 57 58AutoFillPopupMenuClient::~AutoFillPopupMenuClient() 59{ 60} 61 62unsigned AutoFillPopupMenuClient::getSuggestionsCount() const 63{ 64 return m_names.size() + ((m_separatorIndex == -1) ? 0 : 1); 65} 66 67WebString 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 77WebString 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 87WebString 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 97void 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 116bool 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 124void 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 142void 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 159void AutoFillPopupMenuClient::selectionCleared() 160{ 161 WebViewImpl* webView = getWebView(); 162 if (webView) 163 webView->autoFillClient()->didClearAutoFillSelection(WebNode(getTextField())); 164} 165 166String AutoFillPopupMenuClient::itemText(unsigned listIndex) const 167{ 168 return getSuggestion(listIndex); 169} 170 171String AutoFillPopupMenuClient::itemLabel(unsigned listIndex) const 172{ 173 return getLabel(listIndex); 174} 175 176String AutoFillPopupMenuClient::itemIcon(unsigned listIndex) const 177{ 178 return getIcon(listIndex); 179} 180 181bool AutoFillPopupMenuClient::itemIsEnabled(unsigned listIndex) const 182{ 183 return !itemIsWarning(listIndex); 184} 185 186PopupMenuStyle AutoFillPopupMenuClient::itemStyle(unsigned listIndex) const 187{ 188 return itemIsWarning(listIndex) ? *m_warningStyle : *m_regularStyle; 189} 190 191PopupMenuStyle AutoFillPopupMenuClient::menuStyle() const 192{ 193 return *m_regularStyle; 194} 195 196int 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 206int 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 216void AutoFillPopupMenuClient::popupDidHide() 217{ 218 WebViewImpl* webView = getWebView(); 219 if (!webView) 220 return; 221 222 webView->autoFillPopupDidHide(); 223 webView->autoFillClient()->didClearAutoFillSelection(WebNode(getTextField())); 224} 225 226bool AutoFillPopupMenuClient::itemIsSeparator(unsigned listIndex) const 227{ 228 return (m_separatorIndex != -1 && static_cast<unsigned>(m_separatorIndex) == listIndex); 229} 230 231bool 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 241void AutoFillPopupMenuClient::setTextFromItem(unsigned listIndex) 242{ 243 m_textField->setValue(getSuggestion(listIndex)); 244} 245 246FontSelector* AutoFillPopupMenuClient::fontSelector() const 247{ 248 return m_textField->document()->styleSelector()->fontSelector(); 249} 250 251HostWindow* AutoFillPopupMenuClient::hostWindow() const 252{ 253 return m_textField->document()->view()->hostWindow(); 254} 255 256PassRefPtr<Scrollbar> AutoFillPopupMenuClient::createScrollbar( 257 ScrollbarClient* client, 258 ScrollbarOrientation orientation, 259 ScrollbarControlSize size) 260{ 261 return Scrollbar::createNativeScrollbar(client, orientation, size); 262} 263 264void 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())); 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} 310 311void AutoFillPopupMenuClient::setSuggestions(const WebVector<WebString>& names, 312 const WebVector<WebString>& labels, 313 const WebVector<WebString>& icons, 314 const WebVector<int>& uniqueIDs, 315 int separatorIndex) 316{ 317 ASSERT(names.size() == labels.size()); 318 ASSERT(names.size() == icons.size()); 319 ASSERT(names.size() == uniqueIDs.size()); 320 ASSERT(separatorIndex < static_cast<int>(names.size())); 321 322 m_names.clear(); 323 m_labels.clear(); 324 m_icons.clear(); 325 m_uniqueIDs.clear(); 326 for (size_t i = 0; i < names.size(); ++i) { 327 m_names.append(names[i]); 328 m_labels.append(labels[i]); 329 m_icons.append(icons[i]); 330 m_uniqueIDs.append(uniqueIDs[i]); 331 } 332 333 m_separatorIndex = separatorIndex; 334 335 // Try to preserve selection if possible. 336 if (getSelectedIndex() >= static_cast<int>(names.size())) 337 setSelectedIndex(-1); 338} 339 340int AutoFillPopupMenuClient::convertListIndexToInternalIndex(unsigned listIndex) const 341{ 342 if (listIndex == static_cast<unsigned>(m_separatorIndex)) 343 return -1; 344 345 if (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex)) 346 return listIndex; 347 return listIndex - 1; 348} 349 350WebViewImpl* AutoFillPopupMenuClient::getWebView() const 351{ 352 Frame* frame = m_textField->document()->frame(); 353 if (!frame) 354 return 0; 355 356 Page* page = frame->page(); 357 if (!page) 358 return 0; 359 360 return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView(); 361} 362 363RenderStyle* AutoFillPopupMenuClient::textFieldStyle() const 364{ 365 RenderStyle* style = m_textField->computedStyle(); 366 if (!style) { 367 // It seems we can only have a 0 style in a TextField if the 368 // node is detached, in which case we the popup should not be 369 // showing. Please report this in http://crbug.com/7708 and 370 // include the page you were visiting. 371 ASSERT_NOT_REACHED(); 372 } 373 return style; 374} 375 376} // namespace WebKit 377