| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "AutoFillPopupMenuClient.h" |
| |
| #include "CSSStyleSelector.h" |
| #include "CSSValueKeywords.h" |
| #include "Chrome.h" |
| #include "FrameView.h" |
| #include "HTMLInputElement.h" |
| #include "RenderTheme.h" |
| #include "WebAutoFillClient.h" |
| #include "WebNode.h" |
| #include "WebString.h" |
| #include "WebVector.h" |
| #include "WebViewClient.h" |
| #include "WebViewImpl.h" |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| AutoFillPopupMenuClient::AutoFillPopupMenuClient() |
| : m_separatorIndex(-1) |
| , m_selectedIndex(-1) |
| , m_textField(0) |
| { |
| } |
| |
| AutoFillPopupMenuClient::~AutoFillPopupMenuClient() |
| { |
| } |
| |
| unsigned AutoFillPopupMenuClient::getSuggestionsCount() const |
| { |
| return m_names.size() + ((m_separatorIndex == -1) ? 0 : 1); |
| } |
| |
| WebString AutoFillPopupMenuClient::getSuggestion(unsigned listIndex) const |
| { |
| int index = convertListIndexToInternalIndex(listIndex); |
| if (index == -1) |
| return WebString(); |
| |
| ASSERT(index >= 0 && static_cast<size_t>(index) < m_names.size()); |
| return m_names[index]; |
| } |
| |
| WebString AutoFillPopupMenuClient::getLabel(unsigned listIndex) const |
| { |
| int index = convertListIndexToInternalIndex(listIndex); |
| if (index == -1) |
| return WebString(); |
| |
| ASSERT(index >= 0 && static_cast<size_t>(index) < m_labels.size()); |
| return m_labels[index]; |
| } |
| |
| WebString AutoFillPopupMenuClient::getIcon(unsigned listIndex) const |
| { |
| int index = convertListIndexToInternalIndex(listIndex); |
| if (index == -1) |
| return WebString(); |
| |
| ASSERT(index >= 0 && static_cast<size_t>(index) < m_icons.size()); |
| return m_icons[index]; |
| } |
| |
| void AutoFillPopupMenuClient::removeSuggestionAtIndex(unsigned listIndex) |
| { |
| if (!canRemoveSuggestionAtIndex(listIndex)) |
| return; |
| |
| int index = convertListIndexToInternalIndex(listIndex); |
| |
| ASSERT(static_cast<unsigned>(index) < m_names.size()); |
| |
| m_names.remove(index); |
| m_labels.remove(index); |
| m_icons.remove(index); |
| m_uniqueIDs.remove(index); |
| |
| // Shift the separator index if necessary. |
| if (m_separatorIndex != -1) |
| m_separatorIndex--; |
| } |
| |
| bool AutoFillPopupMenuClient::canRemoveSuggestionAtIndex(unsigned listIndex) |
| { |
| // Only allow deletion of items before the separator that have unique id 0 |
| // (i.e. are autocomplete rather than autofill items). |
| int index = convertListIndexToInternalIndex(listIndex); |
| return !m_uniqueIDs[index] && (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex)); |
| } |
| |
| void AutoFillPopupMenuClient::valueChanged(unsigned listIndex, bool fireEvents) |
| { |
| WebViewImpl* webView = getWebView(); |
| if (!webView) |
| return; |
| |
| if (m_separatorIndex != -1 && listIndex > static_cast<unsigned>(m_separatorIndex)) |
| --listIndex; |
| |
| ASSERT(listIndex < m_names.size()); |
| |
| webView->autoFillClient()->didAcceptAutoFillSuggestion(WebNode(getTextField()), |
| m_names[listIndex], |
| m_labels[listIndex], |
| m_uniqueIDs[listIndex], |
| listIndex); |
| } |
| |
| void AutoFillPopupMenuClient::selectionChanged(unsigned listIndex, bool fireEvents) |
| { |
| WebViewImpl* webView = getWebView(); |
| if (!webView) |
| return; |
| |
| if (m_separatorIndex != -1 && listIndex > static_cast<unsigned>(m_separatorIndex)) |
| --listIndex; |
| |
| ASSERT(listIndex < m_names.size()); |
| |
| webView->autoFillClient()->didSelectAutoFillSuggestion(WebNode(getTextField()), |
| m_names[listIndex], |
| m_labels[listIndex], |
| m_uniqueIDs[listIndex]); |
| } |
| |
| void AutoFillPopupMenuClient::selectionCleared() |
| { |
| WebViewImpl* webView = getWebView(); |
| if (webView) |
| webView->autoFillClient()->didClearAutoFillSelection(WebNode(getTextField())); |
| } |
| |
| String AutoFillPopupMenuClient::itemText(unsigned listIndex) const |
| { |
| return getSuggestion(listIndex); |
| } |
| |
| String AutoFillPopupMenuClient::itemLabel(unsigned listIndex) const |
| { |
| return getLabel(listIndex); |
| } |
| |
| String AutoFillPopupMenuClient::itemIcon(unsigned listIndex) const |
| { |
| return getIcon(listIndex); |
| } |
| |
| bool AutoFillPopupMenuClient::itemIsEnabled(unsigned listIndex) const |
| { |
| return !itemIsWarning(listIndex); |
| } |
| |
| PopupMenuStyle AutoFillPopupMenuClient::itemStyle(unsigned listIndex) const |
| { |
| return itemIsWarning(listIndex) ? *m_warningStyle : *m_regularStyle; |
| } |
| |
| PopupMenuStyle AutoFillPopupMenuClient::menuStyle() const |
| { |
| return *m_regularStyle; |
| } |
| |
| int AutoFillPopupMenuClient::clientPaddingLeft() const |
| { |
| // Bug http://crbug.com/7708 seems to indicate the style can be 0. |
| RenderStyle* style = textFieldStyle(); |
| if (!style) |
| return 0; |
| |
| return RenderTheme::defaultTheme()->popupInternalPaddingLeft(style); |
| } |
| |
| int AutoFillPopupMenuClient::clientPaddingRight() const |
| { |
| // Bug http://crbug.com/7708 seems to indicate the style can be 0. |
| RenderStyle* style = textFieldStyle(); |
| if (!style) |
| return 0; |
| |
| return RenderTheme::defaultTheme()->popupInternalPaddingRight(style); |
| } |
| |
| void AutoFillPopupMenuClient::popupDidHide() |
| { |
| WebViewImpl* webView = getWebView(); |
| if (!webView) |
| return; |
| |
| webView->autoFillPopupDidHide(); |
| webView->autoFillClient()->didClearAutoFillSelection(WebNode(getTextField())); |
| } |
| |
| bool AutoFillPopupMenuClient::itemIsSeparator(unsigned listIndex) const |
| { |
| return (m_separatorIndex != -1 && static_cast<unsigned>(m_separatorIndex) == listIndex); |
| } |
| |
| bool AutoFillPopupMenuClient::itemIsWarning(unsigned listIndex) const |
| { |
| int index = convertListIndexToInternalIndex(listIndex); |
| if (index == -1) |
| return false; |
| |
| ASSERT(index >= 0 && static_cast<size_t>(index) < m_uniqueIDs.size()); |
| return m_uniqueIDs[index] < 0; |
| } |
| |
| void AutoFillPopupMenuClient::setTextFromItem(unsigned listIndex) |
| { |
| m_textField->setValue(getSuggestion(listIndex)); |
| } |
| |
| FontSelector* AutoFillPopupMenuClient::fontSelector() const |
| { |
| return m_textField->document()->styleSelector()->fontSelector(); |
| } |
| |
| HostWindow* AutoFillPopupMenuClient::hostWindow() const |
| { |
| return m_textField->document()->view()->hostWindow(); |
| } |
| |
| PassRefPtr<Scrollbar> AutoFillPopupMenuClient::createScrollbar( |
| ScrollableArea* scrollableArea, |
| ScrollbarOrientation orientation, |
| ScrollbarControlSize size) |
| { |
| return Scrollbar::createNativeScrollbar(scrollableArea, orientation, size); |
| } |
| |
| void AutoFillPopupMenuClient::initialize( |
| HTMLInputElement* textField, |
| const WebVector<WebString>& names, |
| const WebVector<WebString>& labels, |
| const WebVector<WebString>& icons, |
| const WebVector<int>& uniqueIDs, |
| int separatorIndex) |
| { |
| ASSERT(names.size() == labels.size()); |
| ASSERT(names.size() == icons.size()); |
| ASSERT(names.size() == uniqueIDs.size()); |
| ASSERT(separatorIndex < static_cast<int>(names.size())); |
| |
| m_selectedIndex = -1; |
| m_textField = textField; |
| |
| // The suggestions must be set before initializing the |
| // AutoFillPopupMenuClient. |
| setSuggestions(names, labels, icons, uniqueIDs, separatorIndex); |
| |
| FontDescription regularFontDescription; |
| RenderTheme::defaultTheme()->systemFont(CSSValueWebkitControl, |
| regularFontDescription); |
| RenderStyle* style = m_textField->computedStyle(); |
| regularFontDescription.setComputedSize(style->fontDescription().computedSize()); |
| |
| Font regularFont(regularFontDescription, 0, 0); |
| regularFont.update(textField->document()->styleSelector()->fontSelector()); |
| // The direction of text in popup menu is set the same as the direction of |
| // the input element: textField. |
| m_regularStyle.set(new PopupMenuStyle(Color::black, Color::white, regularFont, |
| true, false, Length(WebCore::Fixed), |
| textField->renderer()->style()->direction(), textField->renderer()->style()->unicodeBidi() == Override)); |
| |
| FontDescription warningFontDescription = regularFont.fontDescription(); |
| warningFontDescription.setItalic(true); |
| Font warningFont(warningFontDescription, regularFont.letterSpacing(), regularFont.wordSpacing()); |
| warningFont.update(regularFont.fontSelector()); |
| m_warningStyle.set(new PopupMenuStyle(Color::darkGray, |
| m_regularStyle->backgroundColor(), |
| warningFont, |
| m_regularStyle->isVisible(), |
| m_regularStyle->isDisplayNone(), |
| m_regularStyle->textIndent(), |
| m_regularStyle->textDirection(), |
| m_regularStyle->hasTextDirectionOverride())); |
| } |
| |
| void AutoFillPopupMenuClient::setSuggestions(const WebVector<WebString>& names, |
| const WebVector<WebString>& labels, |
| const WebVector<WebString>& icons, |
| const WebVector<int>& uniqueIDs, |
| int separatorIndex) |
| { |
| ASSERT(names.size() == labels.size()); |
| ASSERT(names.size() == icons.size()); |
| ASSERT(names.size() == uniqueIDs.size()); |
| ASSERT(separatorIndex < static_cast<int>(names.size())); |
| |
| m_names.clear(); |
| m_labels.clear(); |
| m_icons.clear(); |
| m_uniqueIDs.clear(); |
| for (size_t i = 0; i < names.size(); ++i) { |
| m_names.append(names[i]); |
| m_labels.append(labels[i]); |
| m_icons.append(icons[i]); |
| m_uniqueIDs.append(uniqueIDs[i]); |
| } |
| |
| m_separatorIndex = separatorIndex; |
| |
| // Try to preserve selection if possible. |
| if (getSelectedIndex() >= static_cast<int>(names.size())) |
| setSelectedIndex(-1); |
| } |
| |
| int AutoFillPopupMenuClient::convertListIndexToInternalIndex(unsigned listIndex) const |
| { |
| if (listIndex == static_cast<unsigned>(m_separatorIndex)) |
| return -1; |
| |
| if (m_separatorIndex == -1 || listIndex < static_cast<unsigned>(m_separatorIndex)) |
| return listIndex; |
| return listIndex - 1; |
| } |
| |
| WebViewImpl* AutoFillPopupMenuClient::getWebView() const |
| { |
| Frame* frame = m_textField->document()->frame(); |
| if (!frame) |
| return 0; |
| |
| Page* page = frame->page(); |
| if (!page) |
| return 0; |
| |
| return static_cast<WebViewImpl*>(page->chrome()->client()->webView()); |
| } |
| |
| RenderStyle* AutoFillPopupMenuClient::textFieldStyle() const |
| { |
| RenderStyle* style = m_textField->computedStyle(); |
| if (!style) { |
| // It seems we can only have a 0 style in a TextField if the |
| // node is detached, in which case we the popup should not be |
| // showing. Please report this in http://crbug.com/7708 and |
| // include the page you were visiting. |
| ASSERT_NOT_REACHED(); |
| } |
| return style; |
| } |
| |
| } // namespace WebKit |