Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / forms / TypeAhead.cpp
1 /*
2  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
3  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
4  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
7  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
8  * Copyright (C) 2010 Google Inc. All rights reserved.
9  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  *
26  */
27
28 #include "config.h"
29 #include "core/html/forms/TypeAhead.h"
30
31 #include "core/events/KeyboardEvent.h"
32 #include "wtf/unicode/CharacterNames.h"
33
34 using namespace WTF::Unicode;
35
36 namespace blink {
37
38 TypeAhead::TypeAhead(TypeAheadDataSource* dataSource)
39     : m_dataSource(dataSource)
40     , m_lastTypeTime(0)
41     , m_repeatingChar(0)
42 {
43 }
44
45 static const DOMTimeStamp typeAheadTimeout = 1000;
46
47 static String stripLeadingWhiteSpace(const String& string)
48 {
49     unsigned length = string.length();
50
51     unsigned i;
52     for (i = 0; i < length; ++i) {
53         if (string[i] != noBreakSpace && !isSpaceOrNewline(string[i]))
54             break;
55     }
56
57     return string.substring(i, length - i);
58 }
59
60 int TypeAhead::handleEvent(KeyboardEvent* event, MatchModeFlags matchMode)
61 {
62     if (event->timeStamp() < m_lastTypeTime)
63         return -1;
64
65     int optionCount = m_dataSource->optionCount();
66     DOMTimeStamp delta = event->timeStamp() - m_lastTypeTime;
67     m_lastTypeTime = event->timeStamp();
68
69     UChar c = event->charCode();
70
71     if (delta > typeAheadTimeout)
72         m_buffer.clear();
73     m_buffer.append(c);
74
75     if (optionCount < 1)
76         return -1;
77
78     int searchStartOffset = 1;
79     String prefix;
80     if (matchMode & CycleFirstChar && c == m_repeatingChar) {
81         // The user is likely trying to cycle through all the items starting
82         // with this character, so just search on the character.
83         prefix = String(&c, 1);
84         m_repeatingChar = c;
85     } else if (matchMode & MatchPrefix) {
86         prefix = m_buffer.toString();
87         if (m_buffer.length() > 1) {
88             m_repeatingChar = 0;
89             searchStartOffset = 0;
90         } else {
91             m_repeatingChar = c;
92         }
93     }
94
95     if (!prefix.isEmpty()) {
96         int selected = m_dataSource->indexOfSelectedOption();
97         int index = (selected < 0 ? 0 : selected) + searchStartOffset;
98         index %= optionCount;
99
100         // Compute a case-folded copy of the prefix string before beginning the search for
101         // a matching element. This code uses foldCase to work around the fact that
102         // String::startWith does not fold non-ASCII characters. This code can be changed
103         // to use startWith once that is fixed.
104         String prefixWithCaseFolded(prefix.foldCase());
105         for (int i = 0; i < optionCount; ++i, index = (index + 1) % optionCount) {
106             // Fold the option string and check if its prefix is equal to the folded prefix.
107             String text = m_dataSource->optionAtIndex(index);
108             if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded))
109                 return index;
110         }
111     }
112
113     if (matchMode & MatchIndex) {
114         bool ok = false;
115         int index = m_buffer.toString().toInt(&ok);
116         if (index > 0 && index <= optionCount)
117             return index - 1;
118     }
119     return -1;
120 }
121
122 } // namespace blink