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/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