Diff
Modified: trunk/LayoutTests/ChangeLog (136110 => 136111)
--- trunk/LayoutTests/ChangeLog 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/LayoutTests/ChangeLog 2012-11-29 09:15:57 UTC (rev 136111)
@@ -1,3 +1,13 @@
+2012-11-29 Keishi Hattori <[email protected]>
+
+ Better type ahead for DateTimeSymbolicFieldElement
+ https://bugs.webkit.org/show_bug.cgi?id=103031
+
+ Reviewed by Kent Tamura.
+
+ * fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt:
+ * fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html: Added tests for typeahead.
+
2012-11-29 Kent Tamura <[email protected]>
Convert *-appearance-pseudo-classes.html to dumpAsText tests
Modified: trunk/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt (136110 => 136111)
--- trunk/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt 2012-11-29 09:15:57 UTC (rev 136111)
@@ -15,7 +15,7 @@
== Digit keys ==
FAIL input.value should be 0012-09. Was 0112-09.
== Left/Right keys ==
-FAIL input.value should be 0005-06. Was 0005-09.
+PASS input.value is "0005-06"
PASS document.activeElement.id is "input"
== Up/Down keys ==
PASS input.value is "2012-10"
@@ -49,6 +49,23 @@
== Typeahead ==
PASS input.value is "0001-12"
PASS input.value is "0002-12"
+== Typeahead cycle first character ==
+PASS input.value is "2012-01"
+PASS input.value is "2012-06"
+PASS input.value is "2012-07"
+PASS input.value is "2012-01"
+PASS input.value is "2012-01"
+== Typeahead prefix match ==
+PASS input.value is "2012-03"
+PASS input.value is "2012-03"
+PASS input.value is "2012-05"
+PASS input.value is "2012-05"
+== Typeahead index match ==
+PASS input.value is "2012-01"
+PASS input.value is "2012-12"
+PASS input.value is "2012-12"
+== Typeahead should search from current selection ==
+PASS input.value is "2012-06"
== RTL Left/Right keys ==
PASS input.value is "2012-10"
PASS input.value is "0002-10"
Modified: trunk/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html (136110 => 136111)
--- trunk/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html 2012-11-29 09:15:57 UTC (rev 136111)
@@ -171,6 +171,40 @@
keyDown('2');
shouldBeEqualToString('input.value', '0002-12');
+beginTest('Typeahead cycle first character', '2012-09');
+keyDown('j'); // -> [Jan] 2012
+shouldBeEqualToString('input.value', '2012-01');
+keyDown('j'); // -> [Jun] 2012
+shouldBeEqualToString('input.value', '2012-06');
+keyDown('j'); // -> [Jul] 2012
+shouldBeEqualToString('input.value', '2012-07');
+keyDown('j'); // -> [Jan] 2012
+shouldBeEqualToString('input.value', '2012-01');
+keyDown('x'); // -> [Jan] 2012
+shouldBeEqualToString('input.value', '2012-01');
+
+beginTest('Typeahead prefix match', '2012-09');
+keyDown('m'); // -> [Mar] 2012
+shouldBeEqualToString('input.value', '2012-03');
+keyDown('a'); // -> [Mar] 2012
+shouldBeEqualToString('input.value', '2012-03');
+keyDown('y'); // -> [May] 2012
+shouldBeEqualToString('input.value', '2012-05');
+keyDown('x'); // -> [May] 2012
+shouldBeEqualToString('input.value', '2012-05');
+
+beginTest('Typeahead index match', '2012-09');
+keyDown('1'); // -> [Jan] 2012
+shouldBeEqualToString('input.value', '2012-01');
+keyDown('2'); // -> [Dec] 2012
+shouldBeEqualToString('input.value', '2012-12');
+keyDown('x'); // -> [Dec] 2012
+shouldBeEqualToString('input.value', '2012-12');
+
+beginTest('Typeahead should search from current selection', '2012-01');
+keyDown('j'); // -> [Jun] 2012
+shouldBeEqualToString('input.value', '2012-06');
+
// The tests in the following block fail on platforms without the
// lang-attribute-aware-form-control-UI feature.
input.setAttribute("lang", "he-il");
Modified: trunk/Source/WebCore/CMakeLists.txt (136110 => 136111)
--- trunk/Source/WebCore/CMakeLists.txt 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/CMakeLists.txt 2012-11-29 09:15:57 UTC (rev 136111)
@@ -1467,6 +1467,7 @@
html/TextInputType.cpp
html/TimeInputType.cpp
html/TimeRanges.cpp
+ html/TypeAhead.cpp
html/URLInputType.cpp
html/ValidationMessage.cpp
html/ValidityState.cpp
Modified: trunk/Source/WebCore/ChangeLog (136110 => 136111)
--- trunk/Source/WebCore/ChangeLog 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/ChangeLog 2012-11-29 09:15:57 UTC (rev 136111)
@@ -1,3 +1,52 @@
+2012-11-29 Keishi Hattori <[email protected]>
+
+ Better type ahead for DateTimeSymbolicFieldElement
+ https://bugs.webkit.org/show_bug.cgi?id=103031
+
+ Reviewed by Kent Tamura.
+
+ This cuts out the type ahead code that will be used by HTMLSelectElement
+ and DateTimeSymbolicFieldElement into a TypeAhead class. This will
+ improve DateTimeSymbolicFieldElement type ahead which was first
+ character match only, by adding cycling, prefix match and index number
+ match.
+
+ Added tests to month-multiple-fields-keyboard-events.html.
+
+ * GNUmakefile.list.am: Added TypeAhead.{h,cpp}
+ * Target.pri: Ditto.
+ * WebCore.gypi: Ditto.
+ * WebCore.vcproj/WebCore.vcproj: Ditto.
+ * WebCore.xcodeproj/project.pbxproj: Ditto.
+ * CMakeLists.txt: Ditto.
+ * html/HTMLSelectElement.cpp:
+ (WebCore):
+ (WebCore::HTMLSelectElement::HTMLSelectElement):
+ (WebCore::HTMLSelectElement::indexOfSelectedOption): Returns index of current selection.
+ (WebCore::HTMLSelectElement::optionCount): Returns total number of options.
+ (WebCore::HTMLSelectElement::optionAtIndex): Returns option at index.
+ (WebCore::HTMLSelectElement::typeAheadFind): Use TypeAhead.
+ * html/HTMLSelectElement.h:
+ (HTMLSelectElement):
+ * html/TypeAhead.cpp: Added.
+ (WebCore):
+ (WebCore::TypeAhead::TypeAhead):
+ (WebCore::stripLeadingWhiteSpace): Moved from HTMLSelectElement.cpp.
+ (WebCore::TypeAhead::handleEvent): Returns index for match.
+ * html/TypeAhead.h: Added.
+ (WebCore):
+ (TypeAheadDataSource): Provide the data about the options that TypeAhead should match against.
+ (TypeAhead):
+ * html/shadow/DateTimeSymbolicFieldElement.cpp:
+ (WebCore::DateTimeSymbolicFieldElement::DateTimeSymbolicFieldElement):
+ (WebCore::DateTimeSymbolicFieldElement::handleKeyboardEvent):
+ (WebCore::DateTimeSymbolicFieldElement::indexOfSelectedOption):
+ (WebCore):
+ (WebCore::DateTimeSymbolicFieldElement::optionCount):
+ (WebCore::DateTimeSymbolicFieldElement::optionAtIndex):
+ * html/shadow/DateTimeSymbolicFieldElement.h:
+ (DateTimeSymbolicFieldElement):
+
2012-11-29 Andrei Bucur <[email protected]>
[CSS Regions] Fix content node renderers ordering inside the named flow thread
Modified: trunk/Source/WebCore/GNUmakefile.list.am (136110 => 136111)
--- trunk/Source/WebCore/GNUmakefile.list.am 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/GNUmakefile.list.am 2012-11-29 09:15:57 UTC (rev 136111)
@@ -3625,6 +3625,8 @@
Source/WebCore/html/track/WebVTTToken.h \
Source/WebCore/html/track/WebVTTTokenizer.h \
Source/WebCore/html/track/WebVTTTokenizer.cpp \
+ Source/WebCore/html/TypeAhead.cpp \
+ Source/WebCore/html/TypeAhead.h \
Source/WebCore/html/URLInputType.cpp \
Source/WebCore/html/URLInputType.h \
Source/WebCore/html/ValidationMessage.cpp \
Modified: trunk/Source/WebCore/Target.pri (136110 => 136111)
--- trunk/Source/WebCore/Target.pri 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/Target.pri 2012-11-29 09:15:57 UTC (rev 136111)
@@ -690,6 +690,7 @@
html/TextFieldInputType.cpp \
html/TextInputType.cpp \
html/TimeInputType.cpp \
+ html/TypeAhead.cpp \
html/URLInputType.cpp \
html/ValidationMessage.cpp \
html/ValidityState.cpp \
@@ -1844,6 +1845,7 @@
html/StepRange.h \
html/TextDocument.h \
html/TimeRanges.h \
+ html/TypeAhead.h \
html/ValidityState.h \
html/parser/CSSPreloadScanner.h \
html/parser/HTMLConstructionSite.h \
Modified: trunk/Source/WebCore/WebCore.gypi (136110 => 136111)
--- trunk/Source/WebCore/WebCore.gypi 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/WebCore.gypi 2012-11-29 09:15:57 UTC (rev 136111)
@@ -4265,6 +4265,8 @@
'html/ValidityState.h',
'html/WeekInputType.cpp',
'html/WeekInputType.h',
+ 'html/TypeAhead.cpp',
+ 'html/TypeAhead.h',
'html/canvas/CanvasContextAttributes.cpp',
'html/canvas/CanvasContextAttributes.h',
'html/canvas/CanvasGradient.cpp',
Modified: trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj (136110 => 136111)
--- trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj 2012-11-29 09:15:57 UTC (rev 136111)
@@ -63989,6 +63989,14 @@
RelativePath="..\html\TimeRanges.h"
>
</File>
+ <File
+ RelativePath="..\html\TypeAhead.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\html\TypeAhead.h"
+ >
+ </File>
<File
RelativePath="..\html\URLInputType.cpp"
>
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (136110 => 136111)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2012-11-29 09:15:57 UTC (rev 136111)
@@ -5831,6 +5831,8 @@
C33EE5C514FB49610002095A /* BaseClickableWithKeyInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = C33EE5C314FB49610002095A /* BaseClickableWithKeyInputType.h */; };
C348612315FDE21E007A1CC9 /* InputTypeNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C348612115FDE21E007A1CC9 /* InputTypeNames.cpp */; };
C348612415FDE21E007A1CC9 /* InputTypeNames.h in Headers */ = {isa = PBXBuildFile; fileRef = C348612215FDE21E007A1CC9 /* InputTypeNames.h */; };
+ C375D7FD16639519006184AB /* TypeAhead.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C375D7FB16639519006184AB /* TypeAhead.cpp */; };
+ C375D7FE16639519006184AB /* TypeAhead.h in Headers */ = {isa = PBXBuildFile; fileRef = C375D7FC16639519006184AB /* TypeAhead.h */; };
C37CDEBD149EF2030042090D /* ColorChooserClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C37CDEBC149EF2030042090D /* ColorChooserClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
C3CF17A415B0063F00276D39 /* IdTargetObserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C3CF17A015B0063F00276D39 /* IdTargetObserver.cpp */; };
C3CF17A515B0063F00276D39 /* IdTargetObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C3CF17A115B0063F00276D39 /* IdTargetObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -13239,6 +13241,8 @@
C33EE5C314FB49610002095A /* BaseClickableWithKeyInputType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseClickableWithKeyInputType.h; sourceTree = "<group>"; };
C348612115FDE21E007A1CC9 /* InputTypeNames.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputTypeNames.cpp; sourceTree = "<group>"; };
C348612215FDE21E007A1CC9 /* InputTypeNames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputTypeNames.h; sourceTree = "<group>"; };
+ C375D7FB16639519006184AB /* TypeAhead.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TypeAhead.cpp; sourceTree = "<group>"; };
+ C375D7FC16639519006184AB /* TypeAhead.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TypeAhead.h; sourceTree = "<group>"; };
C37CDEBC149EF2030042090D /* ColorChooserClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorChooserClient.h; sourceTree = "<group>"; };
C3CF17A015B0063F00276D39 /* IdTargetObserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IdTargetObserver.cpp; sourceTree = "<group>"; };
C3CF17A115B0063F00276D39 /* IdTargetObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IdTargetObserver.h; sourceTree = "<group>"; };
@@ -17892,6 +17896,8 @@
E446139D0CD6331000FADA75 /* TimeRanges.cpp */,
E446139E0CD6331000FADA75 /* TimeRanges.h */,
E446139F0CD6331000FADA75 /* TimeRanges.idl */,
+ C375D7FB16639519006184AB /* TypeAhead.cpp */,
+ C375D7FC16639519006184AB /* TypeAhead.h */,
F55B3DA91251F12D003EF269 /* URLInputType.cpp */,
F55B3DAA1251F12D003EF269 /* URLInputType.h */,
F5A154251279534D00D0B0C0 /* ValidationMessage.cpp */,
@@ -25836,6 +25842,7 @@
FB2C15C3165D649D0039C9F8 /* CachedSVGDocumentReference.h in Headers */,
31741AAD16636609008A5B7E /* SimulatedClickOptions.h in Headers */,
15B8B7C91652C5220036EF55 /* JSWebKitCSSMixFunctionValue.h in Headers */,
+ C375D7FE16639519006184AB /* TypeAhead.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -28939,6 +28946,7 @@
447958051643B4B2001E0A7F /* ParsedContentType.cpp in Sources */,
15B8B7C81652C5220036EF55 /* JSWebKitCSSMixFunctionValue.cpp in Sources */,
86BA766E166427A8005BE5D1 /* FrameLoadRequest.cpp in Sources */,
+ C375D7FD16639519006184AB /* TypeAhead.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Modified: trunk/Source/WebCore/html/HTMLSelectElement.cpp (136110 => 136111)
--- trunk/Source/WebCore/html/HTMLSelectElement.cpp 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/html/HTMLSelectElement.cpp 2012-11-29 09:15:57 UTC (rev 136111)
@@ -64,16 +64,13 @@
// Upper limit agreed upon with representatives of Opera and Mozilla.
static const unsigned maxSelectItems = 10000;
-static const DOMTimeStamp typeAheadTimeout = 1000;
-
HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
: HTMLFormControlElementWithState(tagName, document, form)
- , m_lastCharTime(0)
+ , m_typeAhead(this)
, m_size(0)
, m_lastOnChangeIndex(-1)
, m_activeSelectionAnchorIndex(-1)
, m_activeSelectionEndIndex(-1)
- , m_repeatingChar(0)
, m_isProcessingUserDrivenChange(false)
, m_multiple(false)
, m_activeSelectionState(false)
@@ -1474,82 +1471,34 @@
return -1;
}
-static String stripLeadingWhiteSpace(const String& string)
+int HTMLSelectElement::indexOfSelectedOption() const
{
- int length = string.length();
-
- int i;
- for (i = 0; i < length; ++i) {
- if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral)))
- break;
- }
-
- return string.substring(i, length - i);
+ return optionToListIndex(selectedIndex());
}
-void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
+int HTMLSelectElement::optionCount() const
{
- if (event->timeStamp() < m_lastCharTime)
- return;
+ return listItems().size();
+}
- DOMTimeStamp delta = event->timeStamp() - m_lastCharTime;
- m_lastCharTime = event->timeStamp();
-
- UChar c = event->charCode();
-
- String prefix;
- int searchStartOffset = 1;
- if (delta > typeAheadTimeout) {
- prefix = String(&c, 1);
- m_typedString = prefix;
- m_repeatingChar = c;
- } else {
- m_typedString.append(c);
-
- if (c == m_repeatingChar) {
- // The user is likely trying to cycle through all the items starting
- // with this character, so just search on the character.
- prefix = String(&c, 1);
- } else {
- m_repeatingChar = 0;
- prefix = m_typedString;
- searchStartOffset = 0;
- }
- }
-
+String HTMLSelectElement::optionAtIndex(int index) const
+{
const Vector<HTMLElement*>& items = listItems();
- int itemCount = items.size();
- if (itemCount < 1)
- return;
+
+ HTMLElement* element = items[index];
+ if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->disabled())
+ return String();
+ return toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
+}
- int selected = selectedIndex();
- int index = optionToListIndex(selected >= 0 ? selected : 0) + searchStartOffset;
+void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
+{
+ int index = m_typeAhead.handleEvent(event, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar);
if (index < 0)
return;
- index %= itemCount;
-
- // Compute a case-folded copy of the prefix string before beginning the search for
- // a matching element. This code uses foldCase to work around the fact that
- // String::startWith does not fold non-ASCII characters. This code can be changed
- // to use startWith once that is fixed.
- String prefixWithCaseFolded(prefix.foldCase());
- for (int i = 0; i < itemCount; ++i, index = (index + 1) % itemCount) {
- HTMLElement* element = items[index];
- if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->disabled())
- continue;
-
- // Fold the option string and check if its prefix is equal to the folded prefix.
- String text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
- if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded)) {
- selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
- if (!usesMenuList())
- listBoxOnChange();
-
- setOptionsChangedOnRenderer();
- setNeedsStyleRecalc();
- return;
- }
- }
+ selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
+ if (!usesMenuList())
+ listBoxOnChange();
}
Node::InsertionNotificationRequest HTMLSelectElement::insertedInto(ContainerNode* insertionPoint)
Modified: trunk/Source/WebCore/html/HTMLSelectElement.h (136110 => 136111)
--- trunk/Source/WebCore/html/HTMLSelectElement.h 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/html/HTMLSelectElement.h 2012-11-29 09:15:57 UTC (rev 136111)
@@ -29,13 +29,14 @@
#include "Event.h"
#include "HTMLFormControlElementWithState.h"
#include "HTMLOptionsCollection.h"
+#include "TypeAhead.h"
#include <wtf/Vector.h>
namespace WebCore {
class HTMLOptionElement;
-class HTMLSelectElement : public HTMLFormControlElementWithState {
+class HTMLSelectElement : public HTMLFormControlElementWithState, public TypeAheadDataSource {
public:
static PassRefPtr<HTMLSelectElement> create(const QualifiedName&, Document*, HTMLFormElement*);
@@ -103,7 +104,7 @@
// For use in the implementation of HTMLOptionElement.
void optionSelectionStateChanged(HTMLOptionElement*, bool optionIsSelected);
-
+
protected:
HTMLSelectElement(const QualifiedName&, Document*, HTMLFormElement*);
@@ -181,17 +182,20 @@
virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0);
virtual bool areAuthorShadowsAllowed() const OVERRIDE { return false; }
+ // TypeAheadDataSource functions.
+ virtual int indexOfSelectedOption() const OVERRIDE;
+ virtual int optionCount() const OVERRIDE;
+ virtual String optionAtIndex(int index) const OVERRIDE;
+
// m_listItems contains HTMLOptionElement, HTMLOptGroupElement, and HTMLHRElement objects.
mutable Vector<HTMLElement*> m_listItems;
Vector<bool> m_lastOnChangeSelection;
Vector<bool> m_cachedStateForActiveSelection;
- DOMTimeStamp m_lastCharTime;
- String m_typedString;
+ TypeAhead m_typeAhead;
int m_size;
int m_lastOnChangeIndex;
int m_activeSelectionAnchorIndex;
int m_activeSelectionEndIndex;
- UChar m_repeatingChar;
bool m_isProcessingUserDrivenChange;
bool m_multiple;
bool m_activeSelectionState;
Added: trunk/Source/WebCore/html/TypeAhead.cpp (0 => 136111)
--- trunk/Source/WebCore/html/TypeAhead.cpp (rev 0)
+++ trunk/Source/WebCore/html/TypeAhead.cpp 2012-11-29 09:15:57 UTC (rev 136111)
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 1999 Lars Knoll ([email protected])
+ * (C) 1999 Antti Koivisto ([email protected])
+ * (C) 2001 Dirk Mueller ([email protected])
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * (C) 2006 Alexey Proskuryakov ([email protected])
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "TypeAhead.h"
+
+#include "KeyboardEvent.h"
+#include <wtf/unicode/CharacterNames.h>
+#include <wtf/unicode/Unicode.h>
+
+using namespace WTF::Unicode;
+
+namespace WebCore {
+
+TypeAhead::TypeAhead(TypeAheadDataSource* dataSource)
+ : m_dataSource(dataSource)
+ , m_lastTypeTime(0)
+ , m_repeatingChar(0)
+{
+}
+
+static const DOMTimeStamp typeAheadTimeout = 1000;
+
+static String stripLeadingWhiteSpace(const String& string)
+{
+ unsigned length = string.length();
+
+ unsigned i;
+ for (i = 0; i < length; ++i) {
+ if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral)))
+ break;
+ }
+
+ return string.substring(i, length - i);
+}
+
+int TypeAhead::handleEvent(KeyboardEvent* event, MatchModeFlags matchMode)
+{
+ if (event->timeStamp() < m_lastTypeTime)
+ return -1;
+
+ int optionCount = m_dataSource->optionCount();
+ DOMTimeStamp delta = event->timeStamp() - m_lastTypeTime;
+ m_lastTypeTime = event->timeStamp();
+
+ UChar c = event->charCode();
+
+ if (delta > typeAheadTimeout)
+ m_buffer.clear();
+ m_buffer.append(c);
+
+ if (optionCount < 1)
+ return -1;
+
+ int searchStartOffset = 1;
+ String prefix;
+ if (matchMode & CycleFirstChar && c == m_repeatingChar) {
+ // The user is likely trying to cycle through all the items starting
+ // with this character, so just search on the character.
+ prefix = String(&c, 1);
+ m_repeatingChar = c;
+ } else if (matchMode & MatchPrefix) {
+ prefix = m_buffer.toString();
+ if (m_buffer.length() > 1) {
+ m_repeatingChar = 0;
+ searchStartOffset = 0;
+ } else
+ m_repeatingChar = c;
+ }
+
+ if (!prefix.isEmpty()) {
+ int selected = m_dataSource->indexOfSelectedOption();
+ int index = (selected < 0 ? 0 : selected) + searchStartOffset;
+ index %= optionCount;
+
+ // Compute a case-folded copy of the prefix string before beginning the search for
+ // a matching element. This code uses foldCase to work around the fact that
+ // String::startWith does not fold non-ASCII characters. This code can be changed
+ // to use startWith once that is fixed.
+ String prefixWithCaseFolded(prefix.foldCase());
+ for (int i = 0; i < optionCount; ++i, index = (index + 1) % optionCount) {
+ // Fold the option string and check if its prefix is equal to the folded prefix.
+ String text = m_dataSource->optionAtIndex(index);
+ if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded))
+ return index;
+ }
+ }
+
+ if (matchMode & MatchIndex) {
+ bool ok = false;
+ int index = m_buffer.toString().toInt(&ok);
+ if (index > 0 && index <= optionCount)
+ return index - 1;
+ }
+ return -1;
+}
+
+} // namespace WebCore
Added: trunk/Source/WebCore/html/TypeAhead.h (0 => 136111)
--- trunk/Source/WebCore/html/TypeAhead.h (rev 0)
+++ trunk/Source/WebCore/html/TypeAhead.h 2012-11-29 09:15:57 UTC (rev 136111)
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#ifndef TypeAhead_h
+#define TypeAhead_h
+
+#include "DOMTimeStamp.h"
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class KeyboardEvent;
+
+class TypeAheadDataSource {
+public:
+ virtual ~TypeAheadDataSource() { }
+
+ virtual int indexOfSelectedOption() const = 0;
+ virtual int optionCount() const = 0;
+ virtual String optionAtIndex(int index) const = 0;
+};
+
+class TypeAhead {
+public:
+ TypeAhead(TypeAheadDataSource*);
+
+ enum ModeFlag {
+ MatchPrefix = 1 << 0,
+ CycleFirstChar = 1 << 1,
+ MatchIndex = 1 << 2,
+ };
+ typedef unsigned MatchModeFlags;
+
+ // Returns the index for the matching option.
+ int handleEvent(KeyboardEvent*, MatchModeFlags);
+
+private:
+ TypeAheadDataSource* m_dataSource;
+ DOMTimeStamp m_lastTypeTime;
+ UChar m_repeatingChar;
+ MatchModeFlags m_matchMode;
+ StringBuilder m_buffer;
+};
+
+} // namespace WebCore
+
+#endif // TypeAhead_h
Modified: trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp (136110 => 136111)
--- trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp 2012-11-29 09:15:57 UTC (rev 136111)
@@ -55,6 +55,7 @@
, m_symbols(symbols)
, m_visibleEmptyValue(makeVisibleEmptyValue(symbols))
, m_selectedIndex(-1)
+ , m_typeAhead(this)
{
ASSERT(!symbols.isEmpty());
setHasCustomCallbacks();
@@ -82,12 +83,11 @@
return;
keyboardEvent->setDefaultHandled();
- for (unsigned index = 0; index < m_symbols.size(); ++index) {
- if (!m_symbols[index].isEmpty() && WTF::Unicode::toLower(m_symbols[index][0]) == charCode) {
- setValueAsInteger(index, DispatchEvent);
- return;
- }
- }
+
+ int index = m_typeAhead.handleEvent(keyboardEvent, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar | TypeAhead::MatchIndex);
+ if (index < 0)
+ return;
+ setValueAsInteger(index, DispatchEvent);
}
bool DateTimeSymbolicFieldElement::hasValue() const
@@ -150,6 +150,21 @@
return hasValue() ? m_symbols[m_selectedIndex] : visibleEmptyValue();
}
+int DateTimeSymbolicFieldElement::indexOfSelectedOption() const
+{
+ return m_selectedIndex;
+}
+
+int DateTimeSymbolicFieldElement::optionCount() const
+{
+ return m_symbols.size();
+}
+
+String DateTimeSymbolicFieldElement::optionAtIndex(int index) const
+{
+ return m_symbols[index];
+}
+
} // namespace WebCore
#endif
Modified: trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h (136110 => 136111)
--- trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h 2012-11-29 08:59:11 UTC (rev 136110)
+++ trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h 2012-11-29 09:15:57 UTC (rev 136111)
@@ -28,12 +28,13 @@
#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
#include "DateTimeFieldElement.h"
+#include "TypeAhead.h"
namespace WebCore {
// DateTimeSymbolicFieldElement represents non-numeric field of data time
// format, such as: AM/PM, and month.
-class DateTimeSymbolicFieldElement : public DateTimeFieldElement {
+class DateTimeSymbolicFieldElement : public DateTimeFieldElement, public TypeAheadDataSource {
WTF_MAKE_NONCOPYABLE(DateTimeSymbolicFieldElement);
protected:
@@ -59,12 +60,18 @@
virtual String value() const OVERRIDE FINAL;
virtual String visibleValue() const OVERRIDE FINAL;
+ // TypeAheadDataSource functions.
+ virtual int indexOfSelectedOption() const OVERRIDE;
+ virtual int optionCount() const OVERRIDE;
+ virtual String optionAtIndex(int index) const OVERRIDE;
+
const Vector<String> m_symbols;
// We use AtomicString to share visible empty value among multiple
// DateTimeEditElements in the page.
const AtomicString m_visibleEmptyValue;
int m_selectedIndex;
+ TypeAhead m_typeAhead;
};
} // namespace WebCore