diff options
Diffstat (limited to 'chromium/v8/src/extensions/i18n')
-rw-r--r-- | chromium/v8/src/extensions/i18n/collator.cc | 366 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/collator.h | 68 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/collator.js | 11 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/date-format.cc | 329 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/date-format.h | 71 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/date-format.js | 10 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/i18n-extension.cc | 40 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/i18n-utils.cc | 46 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/i18n-utils.js | 11 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/locale.cc | 251 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/locale.h | 56 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/locale.js | 4 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/number-format.cc | 418 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/number-format.h | 69 | ||||
-rw-r--r-- | chromium/v8/src/extensions/i18n/number-format.js | 16 |
15 files changed, 1726 insertions, 40 deletions
diff --git a/chromium/v8/src/extensions/i18n/collator.cc b/chromium/v8/src/extensions/i18n/collator.cc new file mode 100644 index 00000000000..61b1d63e5c2 --- /dev/null +++ b/chromium/v8/src/extensions/i18n/collator.cc @@ -0,0 +1,366 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#include "collator.h" + +#include "i18n-utils.h" +#include "unicode/coll.h" +#include "unicode/locid.h" +#include "unicode/ucol.h" + +namespace v8_i18n { + +static icu::Collator* InitializeCollator( + v8::Handle<v8::String>, v8::Handle<v8::Object>, v8::Handle<v8::Object>); + +static icu::Collator* CreateICUCollator( + const icu::Locale&, v8::Handle<v8::Object>); + +static bool SetBooleanAttribute( + UColAttribute, const char*, v8::Handle<v8::Object>, icu::Collator*); + +static void SetResolvedSettings( + const icu::Locale&, icu::Collator*, v8::Handle<v8::Object>); + +static void SetBooleanSetting( + UColAttribute, icu::Collator*, const char*, v8::Handle<v8::Object>); + +icu::Collator* Collator::UnpackCollator(v8::Handle<v8::Object> obj) { + v8::HandleScope handle_scope; + + if (obj->HasOwnProperty(v8::String::New("collator"))) { + return static_cast<icu::Collator*>( + obj->GetAlignedPointerFromInternalField(0)); + } + + return NULL; +} + +void Collator::DeleteCollator(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + void* param) { + // First delete the hidden C++ object. + // Unpacking should never return NULL here. That would only happen if + // this method is used as the weak callback for persistent handles not + // pointing to a collator. + v8::HandleScope handle_scope(isolate); + v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object); + delete UnpackCollator(handle); + + // Then dispose of the persistent handle to JS object. + object->Dispose(isolate); +} + + +// Throws a JavaScript exception. +static v8::Handle<v8::Value> ThrowUnexpectedObjectError() { + // Returns undefined, and schedules an exception to be thrown. + return v8::ThrowException(v8::Exception::Error( + v8::String::New("Collator method called on an object " + "that is not a Collator."))); +} + + +// When there's an ICU error, throw a JavaScript error with |message|. +static v8::Handle<v8::Value> ThrowExceptionForICUError(const char* message) { + return v8::ThrowException(v8::Exception::Error(v8::String::New(message))); +} + + +// static +void Collator::JSInternalCompare( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 3 || !args[0]->IsObject() || + !args[1]->IsString() || !args[2]->IsString()) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Collator and two string arguments are required."))); + return; + } + + icu::Collator* collator = UnpackCollator(args[0]->ToObject()); + if (!collator) { + ThrowUnexpectedObjectError(); + return; + } + + v8::String::Value string_value1(args[1]); + v8::String::Value string_value2(args[2]); + const UChar* string1 = reinterpret_cast<const UChar*>(*string_value1); + const UChar* string2 = reinterpret_cast<const UChar*>(*string_value2); + UErrorCode status = U_ZERO_ERROR; + UCollationResult result = collator->compare( + string1, string_value1.length(), string2, string_value2.length(), status); + + if (U_FAILURE(status)) { + ThrowExceptionForICUError( + "Internal error. Unexpected failure in Collator.compare."); + return; + } + + args.GetReturnValue().Set(result); +} + +void Collator::JSCreateCollator( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() || + !args[2]->IsObject()) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Internal error, wrong parameters."))); + return; + } + + v8::Isolate* isolate = args.GetIsolate(); + v8::Local<v8::ObjectTemplate> intl_collator_template = + Utils::GetTemplate(isolate); + + // Create an empty object wrapper. + v8::Local<v8::Object> local_object = intl_collator_template->NewInstance(); + // But the handle shouldn't be empty. + // That can happen if there was a stack overflow when creating the object. + if (local_object.IsEmpty()) { + args.GetReturnValue().Set(local_object); + return; + } + + // Set collator as internal field of the resulting JS object. + icu::Collator* collator = InitializeCollator( + args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject()); + + if (!collator) { + v8::ThrowException(v8::Exception::Error(v8::String::New( + "Internal error. Couldn't create ICU collator."))); + return; + } else { + local_object->SetAlignedPointerInInternalField(0, collator); + + // Make it safer to unpack later on. + v8::TryCatch try_catch; + local_object->Set(v8::String::New("collator"), v8::String::New("valid")); + if (try_catch.HasCaught()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Internal error, couldn't set property."))); + return; + } + } + + v8::Persistent<v8::Object> wrapper(isolate, local_object); + // Make object handle weak so we can delete iterator once GC kicks in. + wrapper.MakeWeak<void>(NULL, &DeleteCollator); + args.GetReturnValue().Set(wrapper); + wrapper.ClearAndLeak(); +} + +static icu::Collator* InitializeCollator(v8::Handle<v8::String> locale, + v8::Handle<v8::Object> options, + v8::Handle<v8::Object> resolved) { + // Convert BCP47 into ICU locale format. + UErrorCode status = U_ZERO_ERROR; + icu::Locale icu_locale; + char icu_result[ULOC_FULLNAME_CAPACITY]; + int icu_length = 0; + v8::String::AsciiValue bcp47_locale(locale); + if (bcp47_locale.length() != 0) { + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, + &icu_length, &status); + if (U_FAILURE(status) || icu_length == 0) { + return NULL; + } + icu_locale = icu::Locale(icu_result); + } + + icu::Collator* collator = CreateICUCollator(icu_locale, options); + if (!collator) { + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + collator = CreateICUCollator(no_extension_locale, options); + + // Set resolved settings (pattern, numbering system). + SetResolvedSettings(no_extension_locale, collator, resolved); + } else { + SetResolvedSettings(icu_locale, collator, resolved); + } + + return collator; +} + +static icu::Collator* CreateICUCollator( + const icu::Locale& icu_locale, v8::Handle<v8::Object> options) { + // Make collator from options. + icu::Collator* collator = NULL; + UErrorCode status = U_ZERO_ERROR; + collator = icu::Collator::createInstance(icu_locale, status); + + if (U_FAILURE(status)) { + delete collator; + return NULL; + } + + // Set flags first, and then override them with sensitivity if necessary. + SetBooleanAttribute(UCOL_NUMERIC_COLLATION, "numeric", options, collator); + + // Normalization is always on, by the spec. We are free to optimize + // if the strings are already normalized (but we don't have a way to tell + // that right now). + collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); + + icu::UnicodeString case_first; + if (Utils::ExtractStringSetting(options, "caseFirst", &case_first)) { + if (case_first == UNICODE_STRING_SIMPLE("upper")) { + collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status); + } else if (case_first == UNICODE_STRING_SIMPLE("lower")) { + collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status); + } else { + // Default (false/off). + collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status); + } + } + + icu::UnicodeString sensitivity; + if (Utils::ExtractStringSetting(options, "sensitivity", &sensitivity)) { + if (sensitivity == UNICODE_STRING_SIMPLE("base")) { + collator->setStrength(icu::Collator::PRIMARY); + } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) { + collator->setStrength(icu::Collator::SECONDARY); + } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) { + collator->setStrength(icu::Collator::PRIMARY); + collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status); + } else { + // variant (default) + collator->setStrength(icu::Collator::TERTIARY); + } + } + + bool ignore; + if (Utils::ExtractBooleanSetting(options, "ignorePunctuation", &ignore)) { + if (ignore) { + collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status); + } + } + + return collator; +} + +static bool SetBooleanAttribute(UColAttribute attribute, + const char* name, + v8::Handle<v8::Object> options, + icu::Collator* collator) { + UErrorCode status = U_ZERO_ERROR; + bool result; + if (Utils::ExtractBooleanSetting(options, name, &result)) { + collator->setAttribute(attribute, result ? UCOL_ON : UCOL_OFF, status); + if (U_FAILURE(status)) { + return false; + } + } + + return true; +} + +static void SetResolvedSettings(const icu::Locale& icu_locale, + icu::Collator* collator, + v8::Handle<v8::Object> resolved) { + SetBooleanSetting(UCOL_NUMERIC_COLLATION, collator, "numeric", resolved); + + UErrorCode status = U_ZERO_ERROR; + + switch (collator->getAttribute(UCOL_CASE_FIRST, status)) { + case UCOL_LOWER_FIRST: + resolved->Set(v8::String::New("caseFirst"), v8::String::New("lower")); + break; + case UCOL_UPPER_FIRST: + resolved->Set(v8::String::New("caseFirst"), v8::String::New("upper")); + break; + default: + resolved->Set(v8::String::New("caseFirst"), v8::String::New("false")); + } + + switch (collator->getAttribute(UCOL_STRENGTH, status)) { + case UCOL_PRIMARY: { + resolved->Set(v8::String::New("strength"), v8::String::New("primary")); + + // case level: true + s1 -> case, s1 -> base. + if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) { + resolved->Set(v8::String::New("sensitivity"), v8::String::New("case")); + } else { + resolved->Set(v8::String::New("sensitivity"), v8::String::New("base")); + } + break; + } + case UCOL_SECONDARY: + resolved->Set(v8::String::New("strength"), v8::String::New("secondary")); + resolved->Set(v8::String::New("sensitivity"), v8::String::New("accent")); + break; + case UCOL_TERTIARY: + resolved->Set(v8::String::New("strength"), v8::String::New("tertiary")); + resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); + break; + case UCOL_QUATERNARY: + // We shouldn't get quaternary and identical from ICU, but if we do + // put them into variant. + resolved->Set(v8::String::New("strength"), v8::String::New("quaternary")); + resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); + break; + default: + resolved->Set(v8::String::New("strength"), v8::String::New("identical")); + resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); + } + + if (UCOL_SHIFTED == collator->getAttribute(UCOL_ALTERNATE_HANDLING, status)) { + resolved->Set(v8::String::New("ignorePunctuation"), + v8::Boolean::New(true)); + } else { + resolved->Set(v8::String::New("ignorePunctuation"), + v8::Boolean::New(false)); + } + + // Set the locale + char result[ULOC_FULLNAME_CAPACITY]; + status = U_ZERO_ERROR; + uloc_toLanguageTag( + icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); + if (U_SUCCESS(status)) { + resolved->Set(v8::String::New("locale"), v8::String::New(result)); + } else { + // This would never happen, since we got the locale from ICU. + resolved->Set(v8::String::New("locale"), v8::String::New("und")); + } +} + +static void SetBooleanSetting(UColAttribute attribute, + icu::Collator* collator, + const char* property, + v8::Handle<v8::Object> resolved) { + UErrorCode status = U_ZERO_ERROR; + if (UCOL_ON == collator->getAttribute(attribute, status)) { + resolved->Set(v8::String::New(property), v8::Boolean::New(true)); + } else { + resolved->Set(v8::String::New(property), v8::Boolean::New(false)); + } +} + +} // namespace v8_i18n diff --git a/chromium/v8/src/extensions/i18n/collator.h b/chromium/v8/src/extensions/i18n/collator.h new file mode 100644 index 00000000000..a3991b9ed24 --- /dev/null +++ b/chromium/v8/src/extensions/i18n/collator.h @@ -0,0 +1,68 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#ifndef V8_EXTENSIONS_I18N_COLLATOR_H_ +#define V8_EXTENSIONS_I18N_COLLATOR_H_ + +#include "unicode/uversion.h" +#include "v8.h" + +namespace U_ICU_NAMESPACE { +class Collator; +class UnicodeString; +} + +namespace v8_i18n { + +class Collator { + public: + static void JSCreateCollator(const v8::FunctionCallbackInfo<v8::Value>& args); + + // Helper methods for various bindings. + + // Unpacks collator object from corresponding JavaScript object. + static icu::Collator* UnpackCollator(v8::Handle<v8::Object> obj); + + // Release memory we allocated for the Collator once the JS object that + // holds the pointer gets garbage collected. + static void DeleteCollator(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + void* param); + + // Compare two strings and returns -1, 0 and 1 depending on + // whether string1 is smaller than, equal to or larger than string2. + static void JSInternalCompare( + const v8::FunctionCallbackInfo<v8::Value>& args); + + private: + Collator() {} +}; + +} // namespace v8_i18n + +#endif // V8_EXTENSIONS_I18N_COLLATOR_H_ diff --git a/chromium/v8/src/extensions/i18n/collator.js b/chromium/v8/src/extensions/i18n/collator.js index d8d247b36f4..3483515bef2 100644 --- a/chromium/v8/src/extensions/i18n/collator.js +++ b/chromium/v8/src/extensions/i18n/collator.js @@ -35,6 +35,8 @@ * Useful for subclassing. */ function initializeCollator(collator, locales, options) { + native function NativeJSCreateCollator(); + if (collator.hasOwnProperty('__initializedIntlObject')) { throw new TypeError('Trying to re-initialize Collator object.'); } @@ -101,9 +103,9 @@ function initializeCollator(collator, locales, options) { usage: {value: internalOptions.usage, writable: true} }); - var internalCollator = %CreateCollator(requestedLocale, - internalOptions, - resolved); + var internalCollator = NativeJSCreateCollator(requestedLocale, + internalOptions, + resolved); // Writable, configurable and enumerable are set to false by default. Object.defineProperty(collator, 'collator', {value: internalCollator}); @@ -202,7 +204,8 @@ function initializeCollator(collator, locales, options) { * the sort order, or x comes after y in the sort order, respectively. */ function compare(collator, x, y) { - return %InternalCompare(collator.collator, String(x), String(y)); + native function NativeJSInternalCompare(); + return NativeJSInternalCompare(collator.collator, String(x), String(y)); }; diff --git a/chromium/v8/src/extensions/i18n/date-format.cc b/chromium/v8/src/extensions/i18n/date-format.cc new file mode 100644 index 00000000000..1058e37a58c --- /dev/null +++ b/chromium/v8/src/extensions/i18n/date-format.cc @@ -0,0 +1,329 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#include "date-format.h" + +#include <string.h> + +#include "i18n-utils.h" +#include "unicode/calendar.h" +#include "unicode/dtfmtsym.h" +#include "unicode/dtptngen.h" +#include "unicode/locid.h" +#include "unicode/numsys.h" +#include "unicode/smpdtfmt.h" +#include "unicode/timezone.h" + +namespace v8_i18n { + +static icu::SimpleDateFormat* InitializeDateTimeFormat(v8::Handle<v8::String>, + v8::Handle<v8::Object>, + v8::Handle<v8::Object>); +static icu::SimpleDateFormat* CreateICUDateFormat(const icu::Locale&, + v8::Handle<v8::Object>); +static void SetResolvedSettings(const icu::Locale&, + icu::SimpleDateFormat*, + v8::Handle<v8::Object>); + +icu::SimpleDateFormat* DateFormat::UnpackDateFormat( + v8::Handle<v8::Object> obj) { + v8::HandleScope handle_scope; + + if (obj->HasOwnProperty(v8::String::New("dateFormat"))) { + return static_cast<icu::SimpleDateFormat*>( + obj->GetAlignedPointerFromInternalField(0)); + } + + return NULL; +} + +void DateFormat::DeleteDateFormat(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + void* param) { + // First delete the hidden C++ object. + // Unpacking should never return NULL here. That would only happen if + // this method is used as the weak callback for persistent handles not + // pointing to a date time formatter. + v8::HandleScope handle_scope(isolate); + v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object); + delete UnpackDateFormat(handle); + + // Then dispose of the persistent handle to JS object. + object->Dispose(isolate); +} + +void DateFormat::JSInternalFormat( + const v8::FunctionCallbackInfo<v8::Value>& args) { + double millis = 0.0; + if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsDate()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New( + "Internal error. Formatter and date value have to be specified."))); + return; + } else { + millis = v8::Date::Cast(*args[1])->NumberValue(); + } + + icu::SimpleDateFormat* date_format = UnpackDateFormat(args[0]->ToObject()); + if (!date_format) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("DateTimeFormat method called on an object " + "that is not a DateTimeFormat."))); + return; + } + + icu::UnicodeString result; + date_format->format(millis, result); + + args.GetReturnValue().Set(v8::String::New( + reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length())); +} + +void DateFormat::JSInternalParse( + const v8::FunctionCallbackInfo<v8::Value>& args) { + icu::UnicodeString string_date; + if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New( + "Internal error. Formatter and string have to be specified."))); + return; + } else { + if (!Utils::V8StringToUnicodeString(args[1], &string_date)) { + string_date = ""; + } + } + + icu::SimpleDateFormat* date_format = UnpackDateFormat(args[0]->ToObject()); + if (!date_format) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("DateTimeFormat method called on an object " + "that is not a DateTimeFormat."))); + return; + } + + UErrorCode status = U_ZERO_ERROR; + UDate date = date_format->parse(string_date, status); + if (U_FAILURE(status)) { + return; + } + + args.GetReturnValue().Set(v8::Date::New(static_cast<double>(date))); +} + +void DateFormat::JSCreateDateTimeFormat( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 3 || + !args[0]->IsString() || + !args[1]->IsObject() || + !args[2]->IsObject()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Internal error, wrong parameters."))); + return; + } + + v8::Isolate* isolate = args.GetIsolate(); + v8::Local<v8::ObjectTemplate> date_format_template = + Utils::GetTemplate(isolate); + + // Create an empty object wrapper. + v8::Local<v8::Object> local_object = date_format_template->NewInstance(); + // But the handle shouldn't be empty. + // That can happen if there was a stack overflow when creating the object. + if (local_object.IsEmpty()) { + args.GetReturnValue().Set(local_object); + return; + } + + // Set date time formatter as internal field of the resulting JS object. + icu::SimpleDateFormat* date_format = InitializeDateTimeFormat( + args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject()); + + if (!date_format) { + v8::ThrowException(v8::Exception::Error(v8::String::New( + "Internal error. Couldn't create ICU date time formatter."))); + return; + } else { + local_object->SetAlignedPointerInInternalField(0, date_format); + + v8::TryCatch try_catch; + local_object->Set(v8::String::New("dateFormat"), v8::String::New("valid")); + if (try_catch.HasCaught()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Internal error, couldn't set property."))); + return; + } + } + + v8::Persistent<v8::Object> wrapper(isolate, local_object); + // Make object handle weak so we can delete iterator once GC kicks in. + wrapper.MakeWeak<void>(NULL, &DeleteDateFormat); + args.GetReturnValue().Set(wrapper); + wrapper.ClearAndLeak(); +} + +static icu::SimpleDateFormat* InitializeDateTimeFormat( + v8::Handle<v8::String> locale, + v8::Handle<v8::Object> options, + v8::Handle<v8::Object> resolved) { + // Convert BCP47 into ICU locale format. + UErrorCode status = U_ZERO_ERROR; + icu::Locale icu_locale; + char icu_result[ULOC_FULLNAME_CAPACITY]; + int icu_length = 0; + v8::String::AsciiValue bcp47_locale(locale); + if (bcp47_locale.length() != 0) { + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, + &icu_length, &status); + if (U_FAILURE(status) || icu_length == 0) { + return NULL; + } + icu_locale = icu::Locale(icu_result); + } + + icu::SimpleDateFormat* date_format = CreateICUDateFormat(icu_locale, options); + if (!date_format) { + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + date_format = CreateICUDateFormat(no_extension_locale, options); + + // Set resolved settings (pattern, numbering system, calendar). + SetResolvedSettings(no_extension_locale, date_format, resolved); + } else { + SetResolvedSettings(icu_locale, date_format, resolved); + } + + return date_format; +} + +static icu::SimpleDateFormat* CreateICUDateFormat( + const icu::Locale& icu_locale, v8::Handle<v8::Object> options) { + // Create time zone as specified by the user. We have to re-create time zone + // since calendar takes ownership. + icu::TimeZone* tz = NULL; + icu::UnicodeString timezone; + if (Utils::ExtractStringSetting(options, "timeZone", &timezone)) { + tz = icu::TimeZone::createTimeZone(timezone); + } else { + tz = icu::TimeZone::createDefault(); + } + + // Create a calendar using locale, and apply time zone to it. + UErrorCode status = U_ZERO_ERROR; + icu::Calendar* calendar = + icu::Calendar::createInstance(tz, icu_locale, status); + + // Make formatter from skeleton. Calendar and numbering system are added + // to the locale as Unicode extension (if they were specified at all). + icu::SimpleDateFormat* date_format = NULL; + icu::UnicodeString skeleton; + if (Utils::ExtractStringSetting(options, "skeleton", &skeleton)) { + icu::DateTimePatternGenerator* generator = + icu::DateTimePatternGenerator::createInstance(icu_locale, status); + icu::UnicodeString pattern; + if (U_SUCCESS(status)) { + pattern = generator->getBestPattern(skeleton, status); + delete generator; + } + + date_format = new icu::SimpleDateFormat(pattern, icu_locale, status); + if (U_SUCCESS(status)) { + date_format->adoptCalendar(calendar); + } + } + + if (U_FAILURE(status)) { + delete calendar; + delete date_format; + date_format = NULL; + } + + return date_format; +} + +static void SetResolvedSettings(const icu::Locale& icu_locale, + icu::SimpleDateFormat* date_format, + v8::Handle<v8::Object> resolved) { + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString pattern; + date_format->toPattern(pattern); + resolved->Set(v8::String::New("pattern"), + v8::String::New(reinterpret_cast<const uint16_t*>( + pattern.getBuffer()), pattern.length())); + + // Set time zone and calendar. + if (date_format) { + const icu::Calendar* calendar = date_format->getCalendar(); + const char* calendar_name = calendar->getType(); + resolved->Set(v8::String::New("calendar"), v8::String::New(calendar_name)); + + const icu::TimeZone& tz = calendar->getTimeZone(); + icu::UnicodeString time_zone; + tz.getID(time_zone); + + icu::UnicodeString canonical_time_zone; + icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status); + if (U_SUCCESS(status)) { + if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) { + resolved->Set(v8::String::New("timeZone"), v8::String::New("UTC")); + } else { + resolved->Set(v8::String::New("timeZone"), + v8::String::New(reinterpret_cast<const uint16_t*>( + canonical_time_zone.getBuffer()), + canonical_time_zone.length())); + } + } + } + + // Ugly hack. ICU doesn't expose numbering system in any way, so we have + // to assume that for given locale NumberingSystem constructor produces the + // same digits as NumberFormat/Calendar would. + status = U_ZERO_ERROR; + icu::NumberingSystem* numbering_system = + icu::NumberingSystem::createInstance(icu_locale, status); + if (U_SUCCESS(status)) { + const char* ns = numbering_system->getName(); + resolved->Set(v8::String::New("numberingSystem"), v8::String::New(ns)); + } else { + resolved->Set(v8::String::New("numberingSystem"), v8::Undefined()); + } + delete numbering_system; + + // Set the locale + char result[ULOC_FULLNAME_CAPACITY]; + status = U_ZERO_ERROR; + uloc_toLanguageTag( + icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); + if (U_SUCCESS(status)) { + resolved->Set(v8::String::New("locale"), v8::String::New(result)); + } else { + // This would never happen, since we got the locale from ICU. + resolved->Set(v8::String::New("locale"), v8::String::New("und")); + } +} + +} // namespace v8_i18n diff --git a/chromium/v8/src/extensions/i18n/date-format.h b/chromium/v8/src/extensions/i18n/date-format.h new file mode 100644 index 00000000000..daa5964e254 --- /dev/null +++ b/chromium/v8/src/extensions/i18n/date-format.h @@ -0,0 +1,71 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#ifndef V8_EXTENSIONS_I18N_DATE_FORMAT_H_ +#define V8_EXTENSIONS_I18N_DATE_FORMAT_H_ + +#include "unicode/uversion.h" +#include "v8.h" + +namespace U_ICU_NAMESPACE { +class SimpleDateFormat; +} + +namespace v8_i18n { + +class DateFormat { + public: + static void JSCreateDateTimeFormat( + const v8::FunctionCallbackInfo<v8::Value>& args); + + // Helper methods for various bindings. + + // Unpacks date format object from corresponding JavaScript object. + static icu::SimpleDateFormat* UnpackDateFormat( + v8::Handle<v8::Object> obj); + + // Release memory we allocated for the DateFormat once the JS object that + // holds the pointer gets garbage collected. + static void DeleteDateFormat(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + void* param); + + // Formats date and returns corresponding string. + static void JSInternalFormat(const v8::FunctionCallbackInfo<v8::Value>& args); + + // Parses date and returns corresponding Date object or undefined if parse + // failed. + static void JSInternalParse(const v8::FunctionCallbackInfo<v8::Value>& args); + + private: + DateFormat(); +}; + +} // namespace v8_i18n + +#endif // V8_EXTENSIONS_I18N_DATE_FORMAT_H_ diff --git a/chromium/v8/src/extensions/i18n/date-format.js b/chromium/v8/src/extensions/i18n/date-format.js index b1d28e535cd..04e7a7c7b9d 100644 --- a/chromium/v8/src/extensions/i18n/date-format.js +++ b/chromium/v8/src/extensions/i18n/date-format.js @@ -235,6 +235,7 @@ function toDateTimeOptions(options, required, defaults) { * Useful for subclassing. */ function initializeDateTimeFormat(dateFormat, locales, options) { + native function NativeJSCreateDateTimeFormat(); if (dateFormat.hasOwnProperty('__initializedIntlObject')) { throw new TypeError('Trying to re-initialize DateTimeFormat object.'); @@ -291,7 +292,7 @@ function initializeDateTimeFormat(dateFormat, locales, options) { year: {writable: true} }); - var formatter = %CreateDateTimeFormat( + var formatter = NativeJSCreateDateTimeFormat( requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved); if (tz !== undefined && tz !== resolved.timeZone) { @@ -408,6 +409,8 @@ function initializeDateTimeFormat(dateFormat, locales, options) { * DateTimeFormat. */ function formatDate(formatter, dateValue) { + native function NativeJSInternalDateFormat(); + var dateMs; if (dateValue === undefined) { dateMs = Date.now(); @@ -419,7 +422,7 @@ function formatDate(formatter, dateValue) { throw new RangeError('Provided date is not in valid range.'); } - return %InternalDateFormat(formatter.formatter, new Date(dateMs)); + return NativeJSInternalDateFormat(formatter.formatter, new Date(dateMs)); } @@ -430,7 +433,8 @@ function formatDate(formatter, dateValue) { * Returns undefined if date string cannot be parsed. */ function parseDate(formatter, value) { - return %InternalDateParse(formatter.formatter, String(value)); + native function NativeJSInternalDateParse(); + return NativeJSInternalDateParse(formatter.formatter, String(value)); } diff --git a/chromium/v8/src/extensions/i18n/i18n-extension.cc b/chromium/v8/src/extensions/i18n/i18n-extension.cc index e2cba8eb9ef..1c77b8899fb 100644 --- a/chromium/v8/src/extensions/i18n/i18n-extension.cc +++ b/chromium/v8/src/extensions/i18n/i18n-extension.cc @@ -29,7 +29,11 @@ #include "i18n-extension.h" #include "break-iterator.h" +#include "collator.h" +#include "date-format.h" +#include "locale.h" #include "natives.h" +#include "number-format.h" using v8::internal::I18NNatives; @@ -45,6 +49,42 @@ Extension::Extension() v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction( v8::Handle<v8::String> name) { + // Standalone, helper methods. + if (name->Equals(v8::String::New("NativeJSCanonicalizeLanguageTag"))) { + return v8::FunctionTemplate::New(JSCanonicalizeLanguageTag); + } else if (name->Equals(v8::String::New("NativeJSAvailableLocalesOf"))) { + return v8::FunctionTemplate::New(JSAvailableLocalesOf); + } else if (name->Equals(v8::String::New("NativeJSGetDefaultICULocale"))) { + return v8::FunctionTemplate::New(JSGetDefaultICULocale); + } else if (name->Equals(v8::String::New("NativeJSGetLanguageTagVariants"))) { + return v8::FunctionTemplate::New(JSGetLanguageTagVariants); + } + + // Date format and parse. + if (name->Equals(v8::String::New("NativeJSCreateDateTimeFormat"))) { + return v8::FunctionTemplate::New(DateFormat::JSCreateDateTimeFormat); + } else if (name->Equals(v8::String::New("NativeJSInternalDateFormat"))) { + return v8::FunctionTemplate::New(DateFormat::JSInternalFormat); + } else if (name->Equals(v8::String::New("NativeJSInternalDateParse"))) { + return v8::FunctionTemplate::New(DateFormat::JSInternalParse); + } + + // Number format and parse. + if (name->Equals(v8::String::New("NativeJSCreateNumberFormat"))) { + return v8::FunctionTemplate::New(NumberFormat::JSCreateNumberFormat); + } else if (name->Equals(v8::String::New("NativeJSInternalNumberFormat"))) { + return v8::FunctionTemplate::New(NumberFormat::JSInternalFormat); + } else if (name->Equals(v8::String::New("NativeJSInternalNumberParse"))) { + return v8::FunctionTemplate::New(NumberFormat::JSInternalParse); + } + + // Collator. + if (name->Equals(v8::String::New("NativeJSCreateCollator"))) { + return v8::FunctionTemplate::New(Collator::JSCreateCollator); + } else if (name->Equals(v8::String::New("NativeJSInternalCompare"))) { + return v8::FunctionTemplate::New(Collator::JSInternalCompare); + } + // Break iterator. if (name->Equals(v8::String::New("NativeJSCreateBreakIterator"))) { return v8::FunctionTemplate::New(BreakIterator::JSCreateBreakIterator); diff --git a/chromium/v8/src/extensions/i18n/i18n-utils.cc b/chromium/v8/src/extensions/i18n/i18n-utils.cc index 8c87f0715b9..eac11669047 100644 --- a/chromium/v8/src/extensions/i18n/i18n-utils.cc +++ b/chromium/v8/src/extensions/i18n/i18n-utils.cc @@ -141,37 +141,35 @@ void Utils::AsciiToUChar(const char* source, } -static v8::Local<v8::ObjectTemplate> ToLocal(i::Handle<i::Object> handle) { - return v8::Utils::ToLocal(i::Handle<i::ObjectTemplateInfo>::cast(handle)); -} - - -template<int internal_fields, i::EternalHandles::SingletonHandle field> -static v8::Local<v8::ObjectTemplate> GetEternal(v8::Isolate* external) { - i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external); - if (isolate->eternal_handles()->Exists(field)) { - return ToLocal(isolate->eternal_handles()->GetSingleton(field)); - } - v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New()); - raw_template->SetInternalFieldCount(internal_fields); - return ToLocal( - isolate->eternal_handles()->CreateSingleton( - isolate, - *v8::Utils::OpenHandle(*raw_template), - field)); -} - - // static v8::Local<v8::ObjectTemplate> Utils::GetTemplate(v8::Isolate* isolate) { - return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate); + i::Isolate* internal = reinterpret_cast<i::Isolate*>(isolate); + if (internal->heap()->i18n_template_one() == + internal->heap()->the_hole_value()) { + v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New()); + raw_template->SetInternalFieldCount(1); + internal->heap() + ->SetI18nTemplateOne(*v8::Utils::OpenHandle(*raw_template)); + } + + return v8::Utils::ToLocal(i::Handle<i::ObjectTemplateInfo>::cast( + internal->factory()->i18n_template_one())); } // static v8::Local<v8::ObjectTemplate> Utils::GetTemplate2(v8::Isolate* isolate) { - return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate); -} + i::Isolate* internal = reinterpret_cast<i::Isolate*>(isolate); + if (internal->heap()->i18n_template_two() == + internal->heap()->the_hole_value()) { + v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New()); + raw_template->SetInternalFieldCount(2); + internal->heap() + ->SetI18nTemplateTwo(*v8::Utils::OpenHandle(*raw_template)); + } + return v8::Utils::ToLocal(i::Handle<i::ObjectTemplateInfo>::cast( + internal->factory()->i18n_template_two())); +} } // namespace v8_i18n diff --git a/chromium/v8/src/extensions/i18n/i18n-utils.js b/chromium/v8/src/extensions/i18n/i18n-utils.js index 545082ecbba..d7e9486c507 100644 --- a/chromium/v8/src/extensions/i18n/i18n-utils.js +++ b/chromium/v8/src/extensions/i18n/i18n-utils.js @@ -255,6 +255,8 @@ function resolveLocale(service, requestedLocales, options) { * lookup algorithm. */ function lookupMatcher(service, requestedLocales) { + native function NativeJSGetDefaultICULocale(); + if (service.match(SERVICE_RE) === null) { throw new Error('Internal error, wrong service type: ' + service); } @@ -285,7 +287,7 @@ function lookupMatcher(service, requestedLocales) { // Didn't find a match, return default. if (DEFAULT_ICU_LOCALE === undefined) { - DEFAULT_ICU_LOCALE = %GetDefaultICULocale(); + DEFAULT_ICU_LOCALE = NativeJSGetDefaultICULocale(); } return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1}; @@ -444,12 +446,14 @@ function getOptimalLanguageTag(original, resolved) { // Returns Array<Object>, where each object has maximized and base properties. // Maximized: zh -> zh-Hans-CN // Base: zh-CN-u-ca-gregory -> zh-CN + native function NativeJSGetLanguageTagVariants(); + // Take care of grandfathered or simple cases. if (original === resolved) { return original; } - var locales = %GetLanguageTagVariants([original, resolved]); + var locales = NativeJSGetLanguageTagVariants([original, resolved]); if (locales[0].maximized !== locales[1].maximized) { return resolved; } @@ -467,7 +471,8 @@ function getOptimalLanguageTag(original, resolved) { * that is supported. This is required by the spec. */ function getAvailableLocalesOf(service) { - var available = %AvailableLocalesOf(service); + native function NativeJSAvailableLocalesOf(); + var available = NativeJSAvailableLocalesOf(service); for (var i in available) { if (available.hasOwnProperty(i)) { diff --git a/chromium/v8/src/extensions/i18n/locale.cc b/chromium/v8/src/extensions/i18n/locale.cc new file mode 100644 index 00000000000..6b6f9ac314d --- /dev/null +++ b/chromium/v8/src/extensions/i18n/locale.cc @@ -0,0 +1,251 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#include "locale.h" + +#include <string.h> + +#include "unicode/brkiter.h" +#include "unicode/coll.h" +#include "unicode/datefmt.h" +#include "unicode/numfmt.h" +#include "unicode/uloc.h" +#include "unicode/uversion.h" + +namespace v8_i18n { + +void JSCanonicalizeLanguageTag( + const v8::FunctionCallbackInfo<v8::Value>& args) { + // Expect locale id which is a string. + if (args.Length() != 1 || !args[0]->IsString()) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Locale identifier, as a string, is required."))); + return; + } + + UErrorCode error = U_ZERO_ERROR; + + char icu_result[ULOC_FULLNAME_CAPACITY]; + int icu_length = 0; + + // Return value which denotes invalid language tag. + const char* const kInvalidTag = "invalid-tag"; + + v8::String::AsciiValue locale_id(args[0]->ToString()); + if (*locale_id == NULL) { + args.GetReturnValue().Set(v8::String::New(kInvalidTag)); + return; + } + + uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY, + &icu_length, &error); + if (U_FAILURE(error) || icu_length == 0) { + args.GetReturnValue().Set(v8::String::New(kInvalidTag)); + return; + } + + char result[ULOC_FULLNAME_CAPACITY]; + + // Force strict BCP47 rules. + uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error); + + if (U_FAILURE(error)) { + args.GetReturnValue().Set(v8::String::New(kInvalidTag)); + return; + } + + args.GetReturnValue().Set(v8::String::New(result)); +} + + +void JSAvailableLocalesOf(const v8::FunctionCallbackInfo<v8::Value>& args) { + // Expect service name which is a string. + if (args.Length() != 1 || !args[0]->IsString()) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Service identifier, as a string, is required."))); + return; + } + + const icu::Locale* available_locales = NULL; + + int32_t count = 0; + v8::String::AsciiValue service(args[0]->ToString()); + if (strcmp(*service, "collator") == 0) { + available_locales = icu::Collator::getAvailableLocales(count); + } else if (strcmp(*service, "numberformat") == 0) { + available_locales = icu::NumberFormat::getAvailableLocales(count); + } else if (strcmp(*service, "dateformat") == 0) { + available_locales = icu::DateFormat::getAvailableLocales(count); + } else if (strcmp(*service, "breakiterator") == 0) { + available_locales = icu::BreakIterator::getAvailableLocales(count); + } + + v8::TryCatch try_catch; + UErrorCode error = U_ZERO_ERROR; + char result[ULOC_FULLNAME_CAPACITY]; + v8::Handle<v8::Object> locales = v8::Object::New(); + + for (int32_t i = 0; i < count; ++i) { + const char* icu_name = available_locales[i].getName(); + + error = U_ZERO_ERROR; + // No need to force strict BCP47 rules. + uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error); + if (U_FAILURE(error)) { + // This shouldn't happen, but lets not break the user. + continue; + } + + // Index is just a dummy value for the property value. + locales->Set(v8::String::New(result), v8::Integer::New(i)); + if (try_catch.HasCaught()) { + // Ignore error, but stop processing and return. + break; + } + } + + args.GetReturnValue().Set(locales); +} + + +void JSGetDefaultICULocale(const v8::FunctionCallbackInfo<v8::Value>& args) { + icu::Locale default_locale; + + // Set the locale + char result[ULOC_FULLNAME_CAPACITY]; + UErrorCode status = U_ZERO_ERROR; + uloc_toLanguageTag( + default_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); + if (U_SUCCESS(status)) { + args.GetReturnValue().Set(v8::String::New(result)); + return; + } + + args.GetReturnValue().Set(v8::String::New("und")); +} + + +void JSGetLanguageTagVariants(const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::TryCatch try_catch; + + // Expect an array of strings. + if (args.Length() != 1 || !args[0]->IsArray()) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Internal error. Expected Array<String>."))); + return; + } + + v8::Local<v8::Array> input = v8::Local<v8::Array>::Cast(args[0]); + v8::Handle<v8::Array> output = v8::Array::New(input->Length()); + for (unsigned int i = 0; i < input->Length(); ++i) { + v8::Local<v8::Value> locale_id = input->Get(i); + if (try_catch.HasCaught()) { + break; + } + + if (!locale_id->IsString()) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Internal error. Array element is missing " + "or it isn't a string."))); + return; + } + + v8::String::AsciiValue ascii_locale_id(locale_id); + if (*ascii_locale_id == NULL) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Internal error. Non-ASCII locale identifier."))); + return; + } + + UErrorCode error = U_ZERO_ERROR; + + // Convert from BCP47 to ICU format. + // de-DE-u-co-phonebk -> de_DE@collation=phonebook + char icu_locale[ULOC_FULLNAME_CAPACITY]; + int icu_locale_length = 0; + uloc_forLanguageTag(*ascii_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY, + &icu_locale_length, &error); + if (U_FAILURE(error) || icu_locale_length == 0) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Internal error. Failed to convert locale to ICU."))); + return; + } + + // Maximize the locale. + // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook + char icu_max_locale[ULOC_FULLNAME_CAPACITY]; + uloc_addLikelySubtags( + icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY, &error); + + // Remove extensions from maximized locale. + // de_Latn_DE@collation=phonebook -> de_Latn_DE + char icu_base_max_locale[ULOC_FULLNAME_CAPACITY]; + uloc_getBaseName( + icu_max_locale, icu_base_max_locale, ULOC_FULLNAME_CAPACITY, &error); + + // Get original name without extensions. + // de_DE@collation=phonebook -> de_DE + char icu_base_locale[ULOC_FULLNAME_CAPACITY]; + uloc_getBaseName( + icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY, &error); + + // Convert from ICU locale format to BCP47 format. + // de_Latn_DE -> de-Latn-DE + char base_max_locale[ULOC_FULLNAME_CAPACITY]; + uloc_toLanguageTag(icu_base_max_locale, base_max_locale, + ULOC_FULLNAME_CAPACITY, FALSE, &error); + + // de_DE -> de-DE + char base_locale[ULOC_FULLNAME_CAPACITY]; + uloc_toLanguageTag( + icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY, FALSE, &error); + + if (U_FAILURE(error)) { + v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Internal error. Couldn't generate maximized " + "or base locale."))); + return; + } + + v8::Handle<v8::Object> result = v8::Object::New(); + result->Set(v8::String::New("maximized"), v8::String::New(base_max_locale)); + result->Set(v8::String::New("base"), v8::String::New(base_locale)); + if (try_catch.HasCaught()) { + break; + } + + output->Set(i, result); + if (try_catch.HasCaught()) { + break; + } + } + + args.GetReturnValue().Set(output); +} + +} // namespace v8_i18n diff --git a/chromium/v8/src/extensions/i18n/locale.h b/chromium/v8/src/extensions/i18n/locale.h new file mode 100644 index 00000000000..c39568e5d9d --- /dev/null +++ b/chromium/v8/src/extensions/i18n/locale.h @@ -0,0 +1,56 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#ifndef V8_EXTENSIONS_I18N_SRC_LOCALE_H_ +#define V8_EXTENSIONS_I18N_SRC_LOCALE_H_ + +#include "unicode/uversion.h" +#include "v8.h" + +namespace v8_i18n { + +// Canonicalizes the BCP47 language tag using BCP47 rules. +// Returns 'invalid-tag' in case input was not well formed. +void JSCanonicalizeLanguageTag(const v8::FunctionCallbackInfo<v8::Value>& args); + +// Returns a list of available locales for collator, date or number formatter. +void JSAvailableLocalesOf(const v8::FunctionCallbackInfo<v8::Value>& args); + +// Returns default ICU locale. +void JSGetDefaultICULocale(const v8::FunctionCallbackInfo<v8::Value>& args); + +// Returns an array of objects, that have maximized and base names of inputs. +// Unicode extensions are dropped from both. +// Input: ['zh-TW-u-nu-thai', 'sr'] +// Output: [{maximized: 'zh-Hant-TW', base: 'zh-TW'}, +// {maximized: 'sr-Cyrl-RS', base: 'sr'}] +void JSGetLanguageTagVariants(const v8::FunctionCallbackInfo<v8::Value>& args); + +} // namespace v8_i18n + +#endif // V8_EXTENSIONS_I18N_LOCALE_H_ diff --git a/chromium/v8/src/extensions/i18n/locale.js b/chromium/v8/src/extensions/i18n/locale.js index e4783277e64..ea95b87192e 100644 --- a/chromium/v8/src/extensions/i18n/locale.js +++ b/chromium/v8/src/extensions/i18n/locale.js @@ -34,6 +34,8 @@ * Canonicalizes the language tag, or throws in case the tag is invalid. */ function canonicalizeLanguageTag(localeID) { + native function NativeJSCanonicalizeLanguageTag(); + // null is typeof 'object' so we have to do extra check. if (typeof localeID !== 'string' && typeof localeID !== 'object' || localeID === null) { @@ -50,7 +52,7 @@ function canonicalizeLanguageTag(localeID) { // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265. // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after // upgrade to ICU 4.9. - var tag = %CanonicalizeLanguageTag(localeString); + var tag = NativeJSCanonicalizeLanguageTag(localeString); if (tag === 'invalid-tag') { throw new RangeError('Invalid language tag: ' + localeString); } diff --git a/chromium/v8/src/extensions/i18n/number-format.cc b/chromium/v8/src/extensions/i18n/number-format.cc new file mode 100644 index 00000000000..136471561c4 --- /dev/null +++ b/chromium/v8/src/extensions/i18n/number-format.cc @@ -0,0 +1,418 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#include "number-format.h" + +#include <string.h> + +#include "i18n-utils.h" +#include "unicode/curramt.h" +#include "unicode/dcfmtsym.h" +#include "unicode/decimfmt.h" +#include "unicode/locid.h" +#include "unicode/numfmt.h" +#include "unicode/numsys.h" +#include "unicode/uchar.h" +#include "unicode/ucurr.h" +#include "unicode/unum.h" +#include "unicode/uversion.h" + +namespace v8_i18n { + +static icu::DecimalFormat* InitializeNumberFormat(v8::Handle<v8::String>, + v8::Handle<v8::Object>, + v8::Handle<v8::Object>); +static icu::DecimalFormat* CreateICUNumberFormat(const icu::Locale&, + v8::Handle<v8::Object>); +static void SetResolvedSettings(const icu::Locale&, + icu::DecimalFormat*, + v8::Handle<v8::Object>); + +icu::DecimalFormat* NumberFormat::UnpackNumberFormat( + v8::Handle<v8::Object> obj) { + v8::HandleScope handle_scope; + + // v8::ObjectTemplate doesn't have HasInstance method so we can't check + // if obj is an instance of NumberFormat class. We'll check for a property + // that has to be in the object. The same applies to other services, like + // Collator and DateTimeFormat. + if (obj->HasOwnProperty(v8::String::New("numberFormat"))) { + return static_cast<icu::DecimalFormat*>( + obj->GetAlignedPointerFromInternalField(0)); + } + + return NULL; +} + +void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + void* param) { + // First delete the hidden C++ object. + // Unpacking should never return NULL here. That would only happen if + // this method is used as the weak callback for persistent handles not + // pointing to a date time formatter. + v8::HandleScope handle_scope(isolate); + v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object); + delete UnpackNumberFormat(handle); + + // Then dispose of the persistent handle to JS object. + object->Dispose(isolate); +} + +void NumberFormat::JSInternalFormat( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsNumber()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Formatter and numeric value have to be specified."))); + return; + } + + icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject()); + if (!number_format) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("NumberFormat method called on an object " + "that is not a NumberFormat."))); + return; + } + + // ICU will handle actual NaN value properly and return NaN string. + icu::UnicodeString result; + number_format->format(args[1]->NumberValue(), result); + + args.GetReturnValue().Set(v8::String::New( + reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length())); +} + +void NumberFormat::JSInternalParse( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Formatter and string have to be specified."))); + return; + } + + icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject()); + if (!number_format) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("NumberFormat method called on an object " + "that is not a NumberFormat."))); + return; + } + + // ICU will handle actual NaN value properly and return NaN string. + icu::UnicodeString string_number; + if (!Utils::V8StringToUnicodeString(args[1]->ToString(), &string_number)) { + string_number = ""; + } + + UErrorCode status = U_ZERO_ERROR; + icu::Formattable result; + // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49 + // to be part of Chrome. + // TODO(cira): Include currency parsing code using parseCurrency call. + // We need to check if the formatter parses all currencies or only the + // one it was constructed with (it will impact the API - how to return ISO + // code and the value). + number_format->parse(string_number, result, status); + if (U_FAILURE(status)) { + return; + } + + switch (result.getType()) { + case icu::Formattable::kDouble: + args.GetReturnValue().Set(result.getDouble()); + return; + case icu::Formattable::kLong: + args.GetReturnValue().Set(result.getLong()); + return; + case icu::Formattable::kInt64: + args.GetReturnValue().Set(static_cast<double>(result.getInt64())); + return; + default: + return; + } +} + +void NumberFormat::JSCreateNumberFormat( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 3 || + !args[0]->IsString() || + !args[1]->IsObject() || + !args[2]->IsObject()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Internal error, wrong parameters."))); + return; + } + + v8::Isolate* isolate = args.GetIsolate(); + v8::Local<v8::ObjectTemplate> number_format_template = + Utils::GetTemplate(isolate); + + // Create an empty object wrapper. + v8::Local<v8::Object> local_object = number_format_template->NewInstance(); + // But the handle shouldn't be empty. + // That can happen if there was a stack overflow when creating the object. + if (local_object.IsEmpty()) { + args.GetReturnValue().Set(local_object); + return; + } + + // Set number formatter as internal field of the resulting JS object. + icu::DecimalFormat* number_format = InitializeNumberFormat( + args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject()); + + if (!number_format) { + v8::ThrowException(v8::Exception::Error(v8::String::New( + "Internal error. Couldn't create ICU number formatter."))); + return; + } else { + local_object->SetAlignedPointerInInternalField(0, number_format); + + v8::TryCatch try_catch; + local_object->Set(v8::String::New("numberFormat"), + v8::String::New("valid")); + if (try_catch.HasCaught()) { + v8::ThrowException(v8::Exception::Error( + v8::String::New("Internal error, couldn't set property."))); + return; + } + } + + v8::Persistent<v8::Object> wrapper(isolate, local_object); + // Make object handle weak so we can delete iterator once GC kicks in. + wrapper.MakeWeak<void>(NULL, &DeleteNumberFormat); + args.GetReturnValue().Set(wrapper); + wrapper.ClearAndLeak(); +} + +static icu::DecimalFormat* InitializeNumberFormat( + v8::Handle<v8::String> locale, + v8::Handle<v8::Object> options, + v8::Handle<v8::Object> resolved) { + // Convert BCP47 into ICU locale format. + UErrorCode status = U_ZERO_ERROR; + icu::Locale icu_locale; + char icu_result[ULOC_FULLNAME_CAPACITY]; + int icu_length = 0; + v8::String::AsciiValue bcp47_locale(locale); + if (bcp47_locale.length() != 0) { + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, + &icu_length, &status); + if (U_FAILURE(status) || icu_length == 0) { + return NULL; + } + icu_locale = icu::Locale(icu_result); + } + + icu::DecimalFormat* number_format = + CreateICUNumberFormat(icu_locale, options); + if (!number_format) { + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + number_format = CreateICUNumberFormat(no_extension_locale, options); + + // Set resolved settings (pattern, numbering system). + SetResolvedSettings(no_extension_locale, number_format, resolved); + } else { + SetResolvedSettings(icu_locale, number_format, resolved); + } + + return number_format; +} + +static icu::DecimalFormat* CreateICUNumberFormat( + const icu::Locale& icu_locale, v8::Handle<v8::Object> options) { + // Make formatter from options. Numbering system is added + // to the locale as Unicode extension (if it was specified at all). + UErrorCode status = U_ZERO_ERROR; + icu::DecimalFormat* number_format = NULL; + icu::UnicodeString style; + icu::UnicodeString currency; + if (Utils::ExtractStringSetting(options, "style", &style)) { + if (style == UNICODE_STRING_SIMPLE("currency")) { + Utils::ExtractStringSetting(options, "currency", ¤cy); + + icu::UnicodeString display; + Utils::ExtractStringSetting(options, "currencyDisplay", &display); +#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6) + icu::NumberFormat::EStyles style; + if (display == UNICODE_STRING_SIMPLE("code")) { + style = icu::NumberFormat::kIsoCurrencyStyle; + } else if (display == UNICODE_STRING_SIMPLE("name")) { + style = icu::NumberFormat::kPluralCurrencyStyle; + } else { + style = icu::NumberFormat::kCurrencyStyle; + } +#else // ICU version is 4.8 or above (we ignore versions below 4.0). + UNumberFormatStyle style; + if (display == UNICODE_STRING_SIMPLE("code")) { + style = UNUM_CURRENCY_ISO; + } else if (display == UNICODE_STRING_SIMPLE("name")) { + style = UNUM_CURRENCY_PLURAL; + } else { + style = UNUM_CURRENCY; + } +#endif + + number_format = static_cast<icu::DecimalFormat*>( + icu::NumberFormat::createInstance(icu_locale, style, status)); + } else if (style == UNICODE_STRING_SIMPLE("percent")) { + number_format = static_cast<icu::DecimalFormat*>( + icu::NumberFormat::createPercentInstance(icu_locale, status)); + if (U_FAILURE(status)) { + delete number_format; + return NULL; + } + // Make sure 1.1% doesn't go into 2%. + number_format->setMinimumFractionDigits(1); + } else { + // Make a decimal instance by default. + number_format = static_cast<icu::DecimalFormat*>( + icu::NumberFormat::createInstance(icu_locale, status)); + } + } + + if (U_FAILURE(status)) { + delete number_format; + return NULL; + } + + // Set all options. + if (!currency.isEmpty()) { + number_format->setCurrency(currency.getBuffer(), status); + } + + int32_t digits; + if (Utils::ExtractIntegerSetting( + options, "minimumIntegerDigits", &digits)) { + number_format->setMinimumIntegerDigits(digits); + } + + if (Utils::ExtractIntegerSetting( + options, "minimumFractionDigits", &digits)) { + number_format->setMinimumFractionDigits(digits); + } + + if (Utils::ExtractIntegerSetting( + options, "maximumFractionDigits", &digits)) { + number_format->setMaximumFractionDigits(digits); + } + + bool significant_digits_used = false; + if (Utils::ExtractIntegerSetting( + options, "minimumSignificantDigits", &digits)) { + number_format->setMinimumSignificantDigits(digits); + significant_digits_used = true; + } + + if (Utils::ExtractIntegerSetting( + options, "maximumSignificantDigits", &digits)) { + number_format->setMaximumSignificantDigits(digits); + significant_digits_used = true; + } + + number_format->setSignificantDigitsUsed(significant_digits_used); + + bool grouping; + if (Utils::ExtractBooleanSetting(options, "useGrouping", &grouping)) { + number_format->setGroupingUsed(grouping); + } + + // Set rounding mode. + number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp); + + return number_format; +} + +static void SetResolvedSettings(const icu::Locale& icu_locale, + icu::DecimalFormat* number_format, + v8::Handle<v8::Object> resolved) { + icu::UnicodeString pattern; + number_format->toPattern(pattern); + resolved->Set(v8::String::New("pattern"), + v8::String::New(reinterpret_cast<const uint16_t*>( + pattern.getBuffer()), pattern.length())); + + // Set resolved currency code in options.currency if not empty. + icu::UnicodeString currency(number_format->getCurrency()); + if (!currency.isEmpty()) { + resolved->Set(v8::String::New("currency"), + v8::String::New(reinterpret_cast<const uint16_t*>( + currency.getBuffer()), currency.length())); + } + + // Ugly hack. ICU doesn't expose numbering system in any way, so we have + // to assume that for given locale NumberingSystem constructor produces the + // same digits as NumberFormat would. + UErrorCode status = U_ZERO_ERROR; + icu::NumberingSystem* numbering_system = + icu::NumberingSystem::createInstance(icu_locale, status); + if (U_SUCCESS(status)) { + const char* ns = numbering_system->getName(); + resolved->Set(v8::String::New("numberingSystem"), v8::String::New(ns)); + } else { + resolved->Set(v8::String::New("numberingSystem"), v8::Undefined()); + } + delete numbering_system; + + resolved->Set(v8::String::New("useGrouping"), + v8::Boolean::New(number_format->isGroupingUsed())); + + resolved->Set(v8::String::New("minimumIntegerDigits"), + v8::Integer::New(number_format->getMinimumIntegerDigits())); + + resolved->Set(v8::String::New("minimumFractionDigits"), + v8::Integer::New(number_format->getMinimumFractionDigits())); + + resolved->Set(v8::String::New("maximumFractionDigits"), + v8::Integer::New(number_format->getMaximumFractionDigits())); + + if (resolved->HasOwnProperty(v8::String::New("minimumSignificantDigits"))) { + resolved->Set(v8::String::New("minimumSignificantDigits"), v8::Integer::New( + number_format->getMinimumSignificantDigits())); + } + + if (resolved->HasOwnProperty(v8::String::New("maximumSignificantDigits"))) { + resolved->Set(v8::String::New("maximumSignificantDigits"), v8::Integer::New( + number_format->getMaximumSignificantDigits())); + } + + // Set the locale + char result[ULOC_FULLNAME_CAPACITY]; + status = U_ZERO_ERROR; + uloc_toLanguageTag( + icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); + if (U_SUCCESS(status)) { + resolved->Set(v8::String::New("locale"), v8::String::New(result)); + } else { + // This would never happen, since we got the locale from ICU. + resolved->Set(v8::String::New("locale"), v8::String::New("und")); + } +} + +} // namespace v8_i18n diff --git a/chromium/v8/src/extensions/i18n/number-format.h b/chromium/v8/src/extensions/i18n/number-format.h new file mode 100644 index 00000000000..d4dbc4d6f3b --- /dev/null +++ b/chromium/v8/src/extensions/i18n/number-format.h @@ -0,0 +1,69 @@ +// Copyright 2013 the V8 project authors. 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. +// limitations under the License. + +#ifndef V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_ +#define V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_ + +#include "unicode/uversion.h" +#include "v8.h" + +namespace U_ICU_NAMESPACE { +class DecimalFormat; +} + +namespace v8_i18n { + +class NumberFormat { + public: + static void JSCreateNumberFormat( + const v8::FunctionCallbackInfo<v8::Value>& args); + + // Helper methods for various bindings. + + // Unpacks date format object from corresponding JavaScript object. + static icu::DecimalFormat* UnpackNumberFormat(v8::Handle<v8::Object> obj); + + // Release memory we allocated for the NumberFormat once the JS object that + // holds the pointer gets garbage collected. + static void DeleteNumberFormat(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + void* param); + + // Formats number and returns corresponding string. + static void JSInternalFormat(const v8::FunctionCallbackInfo<v8::Value>& args); + + // Parses a string and returns a number. + static void JSInternalParse(const v8::FunctionCallbackInfo<v8::Value>& args); + + private: + NumberFormat(); +}; + +} // namespace v8_i18n + +#endif // V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_ diff --git a/chromium/v8/src/extensions/i18n/number-format.js b/chromium/v8/src/extensions/i18n/number-format.js index 5722a5dc1f3..1cd3db13554 100644 --- a/chromium/v8/src/extensions/i18n/number-format.js +++ b/chromium/v8/src/extensions/i18n/number-format.js @@ -65,6 +65,8 @@ function getNumberOption(options, property, min, max, fallback) { * Useful for subclassing. */ function initializeNumberFormat(numberFormat, locales, options) { + native function NativeJSCreateNumberFormat(); + if (numberFormat.hasOwnProperty('__initializedIntlObject')) { throw new TypeError('Trying to re-initialize NumberFormat object.'); } @@ -146,9 +148,9 @@ function initializeNumberFormat(numberFormat, locales, options) { if (internalOptions.hasOwnProperty('maximumSignificantDigits')) { defineWEProperty(resolved, 'maximumSignificantDigits', undefined); } - var formatter = %CreateNumberFormat(requestedLocale, - internalOptions, - resolved); + var formatter = NativeJSCreateNumberFormat(requestedLocale, + internalOptions, + resolved); // We can't get information about number or currency style from ICU, so we // assume user request was fulfilled. @@ -267,13 +269,15 @@ function initializeNumberFormat(numberFormat, locales, options) { * NumberFormat. */ function formatNumber(formatter, value) { + native function NativeJSInternalNumberFormat(); + // Spec treats -0 and +0 as 0. var number = Number(value); if (number === -0) { number = 0; } - return %InternalNumberFormat(formatter.formatter, number); + return NativeJSInternalNumberFormat(formatter.formatter, number); } @@ -281,7 +285,9 @@ function formatNumber(formatter, value) { * Returns a Number that represents string value that was passed in. */ function parseNumber(formatter, value) { - return %InternalNumberParse(formatter.formatter, String(value)); + native function NativeJSInternalNumberParse(); + + return NativeJSInternalNumberParse(formatter.formatter, String(value)); } |