Home | History | Annotate | Download | only in web
      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