Support localized numbers in <input type=number>
authortkent@chromium.org <tkent@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Mar 2011 06:54:56 +0000 (06:54 +0000)
committertkent@chromium.org <tkent@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Mar 2011 06:54:56 +0000 (06:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=42484

Reviewed by Dimitri Glazkov.

Source/WebCore:

This change adds support of localized numbers in <input type=number>.
This affects only the UI, and not HTMLInputElement::value.

- Remove the keyboard input restriction feature because it is hard to
  retrieve characters usable for localized numbers in ICU.

- Separate convertFromVisibleValue() from sanitizeValue().
  sanitizeValue() is used for not only converting a renderer value to a
  DOM value.

- Implement LocalizedNumber functions for ICU and NSNumberFormatter.
  It is used only in Chromium for now.

Test: manual-tests/input-number-localization.html

* WebCore.gypi: Use LocalizedNumberICU.cpp.
* WebCore.xcodeproj/project.pbxproj:
  Add LocalizedNumberMac.mm and remove LocalizedNumberNone.cpp.
* dom/InputElement.h: Introduce convertFromVisibleValue().
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::convertFromVisibleValue):
* html/HTMLInputElement.h:
* html/InputType.cpp:
(WebCore::InputType::convertFromVisibleValue):
* html/InputType.h:
* html/NumberInputType.cpp: Remove isHTMLNumberCharacter(),
  isNumberCharacter(), and handleBeforeTextInsertedEvent() because we
  remove the keyboard input restriction feature for type=number.
(WebCore::NumberInputType::convertFromVisibleValue):
(WebCore::NumberInputType::sanitizeValue):
* html/NumberInputType.h:
* manual-tests/input-number-localization.html: Add a manual test because
  the behavior depends on the current locale.
* platform/text/LocalizedNumber.h: Remove isLocalizedNumberCharacter().
* platform/text/LocalizedNumberICU.cpp:
  Implement LocalizedNumber functions with ICU NumberFormat.
(WebCore::createFormatterForCurrentLocale):
(WebCore::parseLocalizedNumber):
(WebCore::formatLocalizedNumber):
* platform/text/LocalizedNumberNone.cpp: Remove isLocalizedNumberCharacter().
* platform/text/mac/LocalizedNumberMac.mm:
  Implement LocalizedNumber functions with NSNumberFormatter.
(WebCore::parseLocalizedNumber):
(WebCore::formatLocalizedNumber):
* rendering/RenderTextControlSingleLine.cpp:
(WebCore::RenderTextControlSingleLine::subtreeHasChanged):
* wml/WMLInputElement.h:
(WebCore::WMLInputElement::convertFromVisibleValue):
  Implemented as a function doing nothing.

LayoutTests:

Update an existing test because of removing the keyboard input
restriction feature.

* fast/forms/input-number-keyoperation-expected.txt:
* fast/forms/script-tests/input-number-keyoperation.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@80096 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/input-number-keyoperation-expected.txt
LayoutTests/fast/forms/script-tests/input-number-keyoperation.js
Source/WebCore/ChangeLog
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/InputElement.h
Source/WebCore/html/HTMLInputElement.cpp
Source/WebCore/html/HTMLInputElement.h
Source/WebCore/html/InputType.cpp
Source/WebCore/html/InputType.h
Source/WebCore/html/NumberInputType.cpp
Source/WebCore/html/NumberInputType.h
Source/WebCore/manual-tests/input-number-localization.html [new file with mode: 0644]
Source/WebCore/platform/text/LocalizedNumber.h
Source/WebCore/platform/text/LocalizedNumberICU.cpp [new file with mode: 0644]
Source/WebCore/platform/text/LocalizedNumberNone.cpp
Source/WebCore/platform/text/mac/LocalizedNumberMac.mm [new file with mode: 0644]
Source/WebCore/rendering/RenderTextControlSingleLine.cpp
Source/WebCore/wml/WMLInputElement.h

index 8f3553f..cc7a837 100644 (file)
@@ -1,3 +1,16 @@
+2011-03-01  Kent Tamura  <tkent@chromium.org>
+
+        Reviewed by Dimitri Glazkov.
+
+        Support localized numbers in <input type=number>
+        https://bugs.webkit.org/show_bug.cgi?id=42484
+
+        Update an existing test because of removing the keyboard input
+        restriction feature.
+
+        * fast/forms/input-number-keyoperation-expected.txt:
+        * fast/forms/script-tests/input-number-keyoperation.js:
+
 2011-03-01  Victoria Kirst  <vrk@chromium.org>
 
         Reviewed by Kenneth Russell.
index 221ce6f..1c4fc34 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 Inserting "ab123cd":
-PASS input.value is "123"
+PASS input.value is ""
 Press the up arrow key:
 PASS input.value is "124"
 Press the down arrow key:
index f84151d..a7c0e85 100644 (file)
@@ -7,9 +7,10 @@ var input = document.getElementById('number');
 input.focus();
 debug('Inserting "ab123cd":');
 document.execCommand('InsertText', false, 'ab123cd');
-shouldBe('input.value', '"123"');
+shouldBe('input.value', '""');
 
 debug('Press the up arrow key:');
+input.valueAsNumber = 123;
 eventSender.keyDown('upArrow');
 shouldBe('input.value', '"124"');
 
index c5a884a..5edae8b 100644 (file)
@@ -1,3 +1,60 @@
+2011-03-01  Kent Tamura  <tkent@chromium.org>
+
+        Reviewed by Dimitri Glazkov.
+
+        Support localized numbers in <input type=number>
+        https://bugs.webkit.org/show_bug.cgi?id=42484
+
+        This change adds support of localized numbers in <input type=number>.
+        This affects only the UI, and not HTMLInputElement::value.
+
+        - Remove the keyboard input restriction feature because it is hard to
+          retrieve characters usable for localized numbers in ICU.
+
+        - Separate convertFromVisibleValue() from sanitizeValue().
+          sanitizeValue() is used for not only converting a renderer value to a
+          DOM value.
+
+        - Implement LocalizedNumber functions for ICU and NSNumberFormatter.
+          It is used only in Chromium for now.
+
+        Test: manual-tests/input-number-localization.html
+
+        * WebCore.gypi: Use LocalizedNumberICU.cpp.
+        * WebCore.xcodeproj/project.pbxproj:
+          Add LocalizedNumberMac.mm and remove LocalizedNumberNone.cpp.
+        * dom/InputElement.h: Introduce convertFromVisibleValue().
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::convertFromVisibleValue):
+        * html/HTMLInputElement.h:
+        * html/InputType.cpp:
+        (WebCore::InputType::convertFromVisibleValue):
+        * html/InputType.h:
+        * html/NumberInputType.cpp: Remove isHTMLNumberCharacter(),
+          isNumberCharacter(), and handleBeforeTextInsertedEvent() because we
+          remove the keyboard input restriction feature for type=number.
+        (WebCore::NumberInputType::convertFromVisibleValue):
+        (WebCore::NumberInputType::sanitizeValue):
+        * html/NumberInputType.h:
+        * manual-tests/input-number-localization.html: Add a manual test because
+          the behavior depends on the current locale.
+        * platform/text/LocalizedNumber.h: Remove isLocalizedNumberCharacter().
+        * platform/text/LocalizedNumberICU.cpp:
+          Implement LocalizedNumber functions with ICU NumberFormat.
+        (WebCore::createFormatterForCurrentLocale):
+        (WebCore::parseLocalizedNumber):
+        (WebCore::formatLocalizedNumber):
+        * platform/text/LocalizedNumberNone.cpp: Remove isLocalizedNumberCharacter().
+        * platform/text/mac/LocalizedNumberMac.mm:
+          Implement LocalizedNumber functions with NSNumberFormatter.
+        (WebCore::parseLocalizedNumber):
+        (WebCore::formatLocalizedNumber):
+        * rendering/RenderTextControlSingleLine.cpp:
+        (WebCore::RenderTextControlSingleLine::subtreeHasChanged):
+        * wml/WMLInputElement.h:
+        (WebCore::WMLInputElement::convertFromVisibleValue):
+          Implemented as a function doing nothing.
+
 2011-03-01  Yuta Kitamura  <yutak@chromium.org>
 
         Reviewed by Darin Adler.
index 22abeb5..c3c1ff0 100644 (file)
             'platform/text/LineEnding.cpp',
             'platform/text/LineEnding.h',
             'platform/text/LocalizedNumber.h',
-            'platform/text/LocalizedNumberNone.cpp',
+            'platform/text/LocalizedNumberICU.cpp',
             'platform/text/ParserUtilities.h',
             'platform/text/PlatformString.h',
             'platform/text/RegularExpression.cpp',
index 063c0c3..d12b171 100644 (file)
                F4EAF4AE10C742B1009100D3 /* OpenTypeSanitizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4EAF4AC10C742B1009100D3 /* OpenTypeSanitizer.cpp */; };
                F4EAF4AF10C742B1009100D3 /* OpenTypeSanitizer.h in Headers */ = {isa = PBXBuildFile; fileRef = F4EAF4AD10C742B1009100D3 /* OpenTypeSanitizer.h */; };
                F5142C69123F12B000F5BD4C /* LocalizedNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = F5142C68123F12B000F5BD4C /* LocalizedNumber.h */; };
-               F5142C6B123F12C500F5BD4C /* LocalizedNumberNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5142C6A123F12C500F5BD4C /* LocalizedNumberNone.cpp */; };
                F55B3DAD1251F12D003EF269 /* BaseTextInputType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F55B3D791251F12D003EF269 /* BaseTextInputType.cpp */; };
                F55B3DAE1251F12D003EF269 /* BaseTextInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = F55B3D7A1251F12D003EF269 /* BaseTextInputType.h */; };
                F55B3DAF1251F12D003EF269 /* ButtonInputType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F55B3D7B1251F12D003EF269 /* ButtonInputType.cpp */; };
                F5C041E50FFCA96D00839D4A /* DOMHTMLDataListElementInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C041E00FFCA96D00839D4A /* DOMHTMLDataListElementInternal.h */; };
                F5C041E60FFCA96D00839D4A /* JSHTMLDataListElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5C041E10FFCA96D00839D4A /* JSHTMLDataListElement.cpp */; };
                F5C041E70FFCA96D00839D4A /* JSHTMLDataListElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C041E20FFCA96D00839D4A /* JSHTMLDataListElement.h */; };
+               F5CC42DC12F801CA00D5F7E3 /* LocalizedNumberMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5CC42DB12F801CA00D5F7E3 /* LocalizedNumberMac.mm */; };
                F5D3A57C106B83B300545297 /* DateComponents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5D3A57A106B83B300545297 /* DateComponents.cpp */; };
                F5D3A57D106B83B300545297 /* DateComponents.h in Headers */ = {isa = PBXBuildFile; fileRef = F5D3A57B106B83B300545297 /* DateComponents.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F7A034C4126BF6BE007DC19E /* FontOrientation.h in Headers */ = {isa = PBXBuildFile; fileRef = F7A034C3126BF6BE007DC19E /* FontOrientation.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F4EAF4AC10C742B1009100D3 /* OpenTypeSanitizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OpenTypeSanitizer.cpp; path = opentype/OpenTypeSanitizer.cpp; sourceTree = "<group>"; };
                F4EAF4AD10C742B1009100D3 /* OpenTypeSanitizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OpenTypeSanitizer.h; path = opentype/OpenTypeSanitizer.h; sourceTree = "<group>"; };
                F5142C68123F12B000F5BD4C /* LocalizedNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalizedNumber.h; sourceTree = "<group>"; };
-               F5142C6A123F12C500F5BD4C /* LocalizedNumberNone.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LocalizedNumberNone.cpp; sourceTree = "<group>"; };
                F523D23B02DE4396018635CA /* HTMLDocument.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLDocument.cpp; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                F523D23C02DE4396018635CA /* HTMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocument.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                F523D23E02DE4396018635CA /* HTMLElement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLElement.cpp; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                F5C2869302846DCD018635CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
                F5C2869402846DCD018635CA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
                F5C2869502846DCD018635CA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+               F5CC42DB12F801CA00D5F7E3 /* LocalizedNumberMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalizedNumberMac.mm; sourceTree = "<group>"; };
                F5D3A57A106B83B300545297 /* DateComponents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DateComponents.cpp; sourceTree = "<group>"; };
                F5D3A57B106B83B300545297 /* DateComponents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateComponents.h; sourceTree = "<group>"; };
                F7A034C3126BF6BE007DC19E /* FontOrientation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontOrientation.h; sourceTree = "<group>"; };
                                89B5EA9F11E8003D00F2367E /* LineEnding.cpp */,
                                89B5EAA011E8003D00F2367E /* LineEnding.h */,
                                F5142C68123F12B000F5BD4C /* LocalizedNumber.h */,
-                               F5142C6A123F12C500F5BD4C /* LocalizedNumberNone.cpp */,
                                BC76AC110DD7AD5C00415F34 /* ParserUtilities.h */,
                                B2C3D9FB0D006C1D00EF6F26 /* PlatformString.h */,
                                B2C3D9FC0D006C1D00EF6F26 /* RegularExpression.cpp */,
                                B2AFFC850D00A5DF0030074D /* character-sets.txt */,
                                B2C3D9FA0D006C1D00EF6F26 /* CharsetData.h */,
                                375CD239119D44EA00A2A859 /* HyphenationMac.mm */,
+                               F5CC42DB12F801CA00D5F7E3 /* LocalizedNumberMac.mm */,
                                B2AFFC860D00A5DF0030074D /* mac-encodings.txt */,
                                B2AFFC870D00A5DF0030074D /* make-charset-table.pl */,
                                B2AFFC880D00A5DF0030074D /* ShapeArabic.c */,
                                7EE6846812D26E3800E79415 /* LoaderRunLoopCF.cpp in Sources */,
                                06E81EEC0AB5DA9700C87837 /* LocalCurrentGraphicsContext.mm in Sources */,
                                89878567122CA064003AABDA /* LocalFileSystem.cpp in Sources */,
-                               F5142C6B123F12C500F5BD4C /* LocalizedNumberNone.cpp in Sources */,
+                               F5CC42DC12F801CA00D5F7E3 /* LocalizedNumberMac.mm in Sources */,
                                C046E1AC1208A9FE00BA2CF7 /* LocalizedStrings.cpp in Sources */,
                                BC25B52A131C6D3900180E10 /* LocalizedStringsMac.mm in Sources */,
                                511F23170DC160DA004F0032 /* LocalStorageTask.cpp in Sources */,
index d61890f..838adf5 100644 (file)
@@ -61,6 +61,7 @@ public:
     virtual void setValueForUser(const String&) = 0;
     // The value which is drawn by a renderer.
     virtual String visibleValue() const = 0;
+    virtual String convertFromVisibleValue(const String&) const = 0;
 
     // Returns true if the specified string can be set as the value of InputElement.
     virtual bool isAcceptableValue(const String&) const = 0;
index a221d3e..e109578 100644 (file)
@@ -1150,6 +1150,11 @@ String HTMLInputElement::visibleValue() const
     return m_inputType->visibleValue();
 }
 
+String HTMLInputElement::convertFromVisibleValue(const String& visibleValue) const
+{
+    return m_inputType->convertFromVisibleValue(visibleValue);
+}
+
 bool HTMLInputElement::isAcceptableValue(const String& proposedValue) const
 {
     return m_inputType->isAcceptableValue(proposedValue);
index 757992a..40eb195 100644 (file)
@@ -260,6 +260,7 @@ private:
     virtual void cacheSelection(int start, int end);
 
     virtual String visibleValue() const;
+    virtual String convertFromVisibleValue(const String&) const;
     virtual bool isAcceptableValue(const String&) const;
     virtual String sanitizeValue(const String&) const;
     virtual bool hasUnacceptableValue() const;
index b2cc7de..e708a35 100644 (file)
@@ -515,6 +515,11 @@ String InputType::visibleValue() const
     return element()->value();
 }
 
+String InputType::convertFromVisibleValue(const String& visibleValue) const
+{
+    return visibleValue;
+}
+
 bool InputType::isAcceptableValue(const String&)
 {
     return true;
index b0a75a7..2e91387 100644 (file)
@@ -149,6 +149,7 @@ public:
     virtual String valueMissingText() const;
     virtual bool canSetStringValue() const;
     virtual String visibleValue() const;
+    virtual String convertFromVisibleValue(const String&) const;
     virtual bool isAcceptableValue(const String&);
     // Returing the null string means "use the default value."
     virtual String sanitizeValue(const String&);
index 1d7f941..3397248 100644 (file)
@@ -53,18 +53,6 @@ using namespace std;
 static const double numberDefaultStep = 1.0;
 static const double numberStepScaleFactor = 1.0;
 
-// Returns true if the specified character can be a part of 'valid floating
-// point number' of HTML5.
-static bool isHTMLNumberCharacter(UChar ch)
-{
-    return ch == '+' || ch == '-' || ch == '.' || ch == 'e' || ch == 'E' || isASCIIDigit(ch);
-}
-
-static bool isNumberCharacter(UChar ch)
-{
-    return isLocalizedNumberCharacter(ch) || isHTMLNumberCharacter(ch);
-}
-
 PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element)
 {
     return adoptPtr(new NumberInputType(element));
@@ -183,30 +171,6 @@ void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
         TextFieldInputType::handleKeydownEvent(event);
 }
 
-void NumberInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
-{
-    unsigned length = event->text().length();
-    bool hasInvalidChar = false;
-    for (unsigned i = 0; i < length; ++i) {
-        if (!isNumberCharacter(event->text()[i])) {
-            hasInvalidChar = true;
-            break;
-        }
-    }
-    if (hasInvalidChar) {
-        Vector<UChar> stripped;
-        stripped.reserveCapacity(length);
-        for (unsigned i = 0; i < length; ++i) {
-            UChar ch = event->text()[i];
-            if (!isNumberCharacter(ch))
-                continue;
-            stripped.append(ch);
-        }
-        event->setText(String::adopt(stripped));
-    }
-    TextFieldInputType::handleBeforeTextInsertedEvent(event);
-}
-
 void NumberInputType::handleWheelEvent(WheelEvent* event)
 {
     handleWheelEventForSpinButton(event);
@@ -264,6 +228,14 @@ String NumberInputType::visibleValue() const
     return localized.isEmpty() ? currentValue : localized;
 }
 
+String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
+{
+    if (visibleValue.isEmpty())
+        return visibleValue;
+    double parsedNumber = parseLocalizedNumber(visibleValue);
+    return isfinite(parsedNumber) ? serializeForNumberType(parsedNumber) : visibleValue;
+}
+
 bool NumberInputType::isAcceptableValue(const String& proposedValue)
 {
     return proposedValue.isEmpty() || isfinite(parseLocalizedNumber(proposedValue)) || parseToDoubleForNumberType(proposedValue, 0);
@@ -273,11 +245,6 @@ String NumberInputType::sanitizeValue(const String& proposedValue)
 {
     if (proposedValue.isEmpty())
         return proposedValue;
-    // Try to parse the value as a localized number, then try to parse it as
-    // the standard format.
-    double parsedValue = parseLocalizedNumber(proposedValue);
-    if (isfinite(parsedValue))
-        return serializeForNumberType(parsedValue);
     return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string();
 }
 
index 9d97134..ea5dc10 100644 (file)
@@ -57,7 +57,6 @@ private:
     virtual double defaultStep() const;
     virtual double stepScaleFactor() const;
     virtual void handleKeydownEvent(KeyboardEvent*);
-    virtual void handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*);
     virtual void handleWheelEvent(WheelEvent*);
     virtual double parseToDouble(const String&, double) const;
     virtual double parseToDoubleWithDecimalPlaces(const String&, double, unsigned*) const;
@@ -65,6 +64,7 @@ private:
     virtual double acceptableError(double) const;
     virtual void handleBlurEvent();
     virtual String visibleValue() const;
+    virtual String convertFromVisibleValue(const String&) const;
     virtual bool isAcceptableValue(const String&);
     virtual String sanitizeValue(const String&);
     virtual bool hasUnacceptableValue();
diff --git a/Source/WebCore/manual-tests/input-number-localization.html b/Source/WebCore/manual-tests/input-number-localization.html
new file mode 100644 (file)
index 0000000..4647aa5
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Number type input shows and accepts localized numbers</title>
+<style>
+:invalid {
+  border-color: red;
+  -webkit-box-shadow: 4px 4px 8px #ff8888;
+}
+</style>
+</head>
+<body>
+<div id="console"></div>
+
+<p>Output test: The following text field should have a localized representation for "-1234.56".
+e.g. "-1,234.56" for en_US locale, "-1.234,56" for fr_FR locale.</p>
+<div><input type=number value="-1234.56" step=any></div>
+
+<p>Input test: Type a localized representation of a number into the following text field.
+You'll see an equivalent number in the standard format on the bottom of the text field.</p>
+<div><input type=number id=target step=any oninput="handleInput()"></div>
+<div>Standard format: <output id=output></output></div>
+
+<script>
+function handleInput() {
+  document.getElementById('output').value = document.getElementById('target').value;
+}
+</script>
+</body>
+</html>
index 45873b8..d70541f 100644 (file)
@@ -49,11 +49,6 @@ double parseLocalizedNumber(const String&);
 // return an empty string.
 String formatLocalizedNumber(double);
 
-// Returns true if the input character can be used to represent a
-// number in the browser locale. For example, this should return true for 0-9 .
-// , + - for en-US locale.
-bool isLocalizedNumberCharacter(UChar32);
-
 } // namespace WebCore
 
 #endif // LocalizedNumber_h
diff --git a/Source/WebCore/platform/text/LocalizedNumberICU.cpp b/Source/WebCore/platform/text/LocalizedNumberICU.cpp
new file mode 100644 (file)
index 0000000..4a82dd6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 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 "LocalizedNumber.h"
+
+#include <limits>
+#include <unicode/numfmt.h>
+#include <wtf/PassOwnPtr.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static inline PassOwnPtr<NumberFormat> createFormatterForCurrentLocale()
+{
+    UErrorCode status = U_ZERO_ERROR;
+    return adoptPtr(NumberFormat::createInstance(status));
+}
+
+double parseLocalizedNumber(const String& numberString)
+{
+    if (numberString.isEmpty())
+        return numeric_limits<double>::quiet_NaN();
+    OwnPtr<NumberFormat> formatter = createFormatterForCurrentLocale();
+    if (!formatter)
+        return numeric_limits<double>::quiet_NaN();
+    UnicodeString numberUnicodeString(numberString.characters(), numberString.length());
+    UErrorCode status = U_ZERO_ERROR;
+    Formattable result;
+    formatter->parse(numberUnicodeString, result, status);
+    if (status != U_ZERO_ERROR)
+        return numeric_limits<double>::quiet_NaN();
+    double numericResult = result.getDouble(status);
+    return status == U_ZERO_ERROR ? numericResult : numeric_limits<double>::quiet_NaN();
+}
+
+String formatLocalizedNumber(double number)
+{
+    OwnPtr<NumberFormat> formatter = createFormatterForCurrentLocale();
+    if (!formatter)
+        return String();
+    UnicodeString result;
+    formatter->format(number, result);
+    return String(result.getBuffer(), result.length());
+}
+
+} // namespace WebCore
index 6f017e9..729f2f1 100644 (file)
@@ -47,9 +47,4 @@ String formatLocalizedNumber(double)
     return String();
 }
 
-bool isLocalizedNumberCharacter(UChar32)
-{
-    return false;
-}
-
 } // namespace WebCore
diff --git a/Source/WebCore/platform/text/mac/LocalizedNumberMac.mm b/Source/WebCore/platform/text/mac/LocalizedNumberMac.mm
new file mode 100644 (file)
index 0000000..86e07f3
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 "LocalizedNumber.h"
+
+#include <limits>
+#import <Foundation/NSNumberFormatter.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/text/CString.h>
+
+using namespace std;
+
+namespace WebCore {
+
+double parseLocalizedNumber(const String& numberString)
+{
+    if (numberString.isEmpty())
+        return numeric_limits<double>::quiet_NaN();
+    RetainPtr<NSNumberFormatter> formatter(AdoptNS, [[NSNumberFormatter alloc] init]);
+    [formatter.get() setLocalizesFormat:YES];
+    [formatter.get() setNumberStyle:NSNumberFormatterDecimalStyle];
+    NSNumber *number = [formatter.get() numberFromString:numberString];
+    if (!number)
+        return numeric_limits<double>::quiet_NaN();
+    return [number doubleValue];
+}
+
+String formatLocalizedNumber(double inputNumber)
+{
+    RetainPtr<NSNumber> number(AdoptNS, [[NSNumber alloc] initWithDouble:inputNumber]);
+    RetainPtr<NSNumberFormatter> formatter(AdoptNS, [[NSNumberFormatter alloc] init]);
+    [formatter.get() setLocalizesFormat:YES];
+    [formatter.get() setNumberStyle:NSNumberFormatterDecimalStyle];
+    return String([formatter.get() stringFromNumber:number.get()]);
+}
+
+} // namespace WebCore
+
index 6ded0ea..2853e78 100644 (file)
@@ -191,7 +191,7 @@ void RenderTextControlSingleLine::subtreeHasChanged()
     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
     String value = text();
     if (input->isAcceptableValue(value))
-        input->setValueFromRenderer(input->sanitizeValue(value));
+        input->setValueFromRenderer(input->sanitizeValue(input->convertFromVisibleValue(value)));
     if (node()->isHTMLElement()) {
         // Recalc for :invalid and hasUnacceptableValue() change.
         static_cast<HTMLInputElement*>(input)->setNeedsStyleRecalc();
index 6c66410..1162ac1 100644 (file)
@@ -64,6 +64,7 @@ public:
     virtual void setValue(const String&, bool sendChangeEvent = false);
     virtual void setValueForUser(const String&);
     virtual String visibleValue() const { return value(); }
+    virtual String convertFromVisibleValue(const String& value) const { return value; }
     virtual void setValueFromRenderer(const String&);
 
     virtual bool saveFormControlState(String& value) const;