Bug 1339892 - Refactor mozIntl to have a nicer API and thin logic. r=smaug

MozReview-Commit-ID: J4QXXBy7JII

--HG--
rename : toolkit/components/mozintl/MozIntl.cpp => toolkit/components/mozintl/MozIntlHelper.cpp
rename : toolkit/components/mozintl/MozIntl.h => toolkit/components/mozintl/MozIntlHelper.h
rename : toolkit/components/mozintl/mozIMozIntl.idl => toolkit/components/mozintl/mozIMozIntlHelper.idl
extra : rebase_source : 9753dbeea2e11c7fde550df1a20b1c3d1b2063fe
This commit is contained in:
Zibi Braniecki 2017-03-01 16:58:11 -08:00
Родитель 28079200f3
Коммит 3613954ec3
13 изменённых файлов: 244 добавлений и 79 удалений

Просмотреть файл

@ -550,6 +550,11 @@
@RESPATH@/components/PresentationDataChannelSessionTransport.js @RESPATH@/components/PresentationDataChannelSessionTransport.js
@RESPATH@/components/PresentationDataChannelSessionTransport.manifest @RESPATH@/components/PresentationDataChannelSessionTransport.manifest
#ifdef ENABLE_INTL_API
@RESPATH@/components/mozIntl.manifest
@RESPATH@/components/mozIntl.js
#endif
#if defined(ENABLE_TESTS) && defined(MOZ_DEBUG) #if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
@RESPATH@/components/TestInterfaceJS.js @RESPATH@/components/TestInterfaceJS.js
@RESPATH@/components/TestInterfaceJS.manifest @RESPATH@/components/TestInterfaceJS.manifest

Просмотреть файл

@ -398,6 +398,11 @@
@BINPATH@/components/TVSimulatorService.js @BINPATH@/components/TVSimulatorService.js
@BINPATH@/components/TVSimulatorService.manifest @BINPATH@/components/TVSimulatorService.manifest
#ifdef ENABLE_INTL_API
@BINPATH@/components/mozIntl.manifest
@BINPATH@/components/mozIntl.js
#endif
; Modules ; Modules
@BINPATH@/modules/* @BINPATH@/modules/*

Просмотреть файл

@ -3,20 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MozIntl.h" #include "MozIntlHelper.h"
#include "jswrapper.h" #include "jswrapper.h"
#include "mozilla/ModuleUtils.h" #include "mozilla/ModuleUtils.h"
#define MOZ_MOZINTL_CID \ #define MOZ_MOZINTLHELPER_CID \
{ 0x83f8f991, 0x6b81, 0x4dd8, { 0xa0, 0x93, 0x72, 0x0b, 0xfb, 0x67, 0x4d, 0x38 } } { 0xb43c96be, 0x2b3a, 0x4dc4, { 0x90, 0xe9, 0xb0, 0x6d, 0x34, 0x21, 0x9b, 0x68 } }
using namespace mozilla; using namespace mozilla;
NS_IMPL_ISUPPORTS(MozIntl, mozIMozIntl) NS_IMPL_ISUPPORTS(MozIntlHelper, mozIMozIntlHelper)
MozIntl::MozIntl() = default; MozIntlHelper::MozIntlHelper() = default;
MozIntl::~MozIntl() = default; MozIntlHelper::~MozIntlHelper() = default;
static nsresult static nsresult
AddFunctions(JSContext* cx, JS::Handle<JS::Value> val, const JSFunctionSpec* funcs) AddFunctions(JSContext* cx, JS::Handle<JS::Value> val, const JSFunctionSpec* funcs)
@ -40,7 +40,7 @@ AddFunctions(JSContext* cx, JS::Handle<JS::Value> val, const JSFunctionSpec* fun
} }
NS_IMETHODIMP NS_IMETHODIMP
MozIntl::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx) MozIntlHelper::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx)
{ {
static const JSFunctionSpec funcs[] = { static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0), JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
@ -51,7 +51,7 @@ MozIntl::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx)
} }
NS_IMETHODIMP NS_IMETHODIMP
MozIntl::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx) MozIntlHelper::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx)
{ {
static const JSFunctionSpec funcs[] = { static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0), JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
@ -62,7 +62,7 @@ MozIntl::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx)
} }
NS_IMETHODIMP NS_IMETHODIMP
MozIntl::AddPluralRulesConstructor(JS::Handle<JS::Value> val, JSContext* cx) MozIntlHelper::AddPluralRulesConstructor(JS::Handle<JS::Value> val, JSContext* cx)
{ {
if (!val.isObject()) { if (!val.isObject()) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
@ -83,7 +83,7 @@ MozIntl::AddPluralRulesConstructor(JS::Handle<JS::Value> val, JSContext* cx)
} }
NS_IMETHODIMP NS_IMETHODIMP
MozIntl::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx) MozIntlHelper::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx)
{ {
static const JSFunctionSpec funcs[] = { static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0), JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
@ -93,27 +93,27 @@ MozIntl::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx)
return AddFunctions(cx, val, funcs); return AddFunctions(cx, val, funcs);
} }
NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntl) NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntlHelper)
NS_DEFINE_NAMED_CID(MOZ_MOZINTL_CID); NS_DEFINE_NAMED_CID(MOZ_MOZINTLHELPER_CID);
static const Module::CIDEntry kMozIntlCIDs[] = { static const Module::CIDEntry kMozIntlHelperCIDs[] = {
{ &kMOZ_MOZINTL_CID, false, nullptr, MozIntlConstructor }, { &kMOZ_MOZINTLHELPER_CID, false, nullptr, MozIntlHelperConstructor },
{ nullptr } { nullptr }
}; };
static const mozilla::Module::ContractIDEntry kMozIntlContracts[] = { static const mozilla::Module::ContractIDEntry kMozIntlHelperContracts[] = {
{ "@mozilla.org/mozintl;1", &kMOZ_MOZINTL_CID }, { "@mozilla.org/mozintlhelper;1", &kMOZ_MOZINTLHELPER_CID },
{ nullptr } { nullptr }
}; };
static const mozilla::Module kMozIntlModule = { static const mozilla::Module kMozIntlHelperModule = {
mozilla::Module::kVersion, mozilla::Module::kVersion,
kMozIntlCIDs, kMozIntlHelperCIDs,
kMozIntlContracts, kMozIntlHelperContracts,
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
nullptr nullptr
}; };
NSMODULE_DEFN(mozMozIntlModule) = &kMozIntlModule; NSMODULE_DEFN(mozMozIntlHelperModule) = &kMozIntlHelperModule;

Просмотреть файл

@ -3,20 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozIMozIntl.h" #include "mozIMozIntlHelper.h"
namespace mozilla { namespace mozilla {
class MozIntl final : public mozIMozIntl class MozIntlHelper final : public mozIMozIntlHelper
{ {
public: public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_MOZIMOZINTL NS_DECL_MOZIMOZINTLHELPER
MozIntl(); MozIntlHelper();
private: private:
~MozIntl(); ~MozIntlHelper();
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -8,12 +8,18 @@ XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
XPIDL_SOURCES += [ XPIDL_SOURCES += [
'mozIMozIntl.idl', 'mozIMozIntl.idl',
'mozIMozIntlHelper.idl',
] ]
XPIDL_MODULE = 'mozintl' XPIDL_MODULE = 'mozintl'
SOURCES += [ SOURCES += [
'MozIntl.cpp', 'MozIntlHelper.cpp',
]
EXTRA_COMPONENTS += [
'mozIntl.js',
'mozIntl.manifest',
] ]
FINAL_LIBRARY = 'xul' FINAL_LIBRARY = 'xul'

Просмотреть файл

@ -5,17 +5,41 @@
#include "nsISupports.idl" #include "nsISupports.idl"
[scriptable, uuid(9f9bc42e-54f4-11e6-9aed-4b1429ac0ba0)] /**
* This is a set of APIs that are of general usefulness for user interface
* internationalization.
*
* They're all in various stages of the standardization process through
* ECMA402, so they are exposed to privileged content only but are written
* in the way to allow for easy migration to standard Intl object once
* the appropriate stage of the ECMA402 is achieved.
*
* The exact structure of the code is a little bit complex because of that:
*
* 1) The core is in SpiderMonkey together with other Intl APIs
*
* This allows us to write the code once, stick to the spec language
* of the proposal, reuse our ICU bindings in Spidermonkey and use
* the code to inform us on refining the spec proposal for the given API itself.
*
* 2) The MozIntlHelper API exposes the SpiderMonkey APIs
*
* This helper API allows attaching the new APIs on any regular object.
*
* 3) The MozIntl API provides the access to those APIs
*
* This API exposes the actual functionality and wraps around the MozIntlHelper
* lazily retrieving and setting the accessors.
* On top of that, the API also binds additional functionality like using
* current application locale by default, and fetching OS regional preferences
* for date time format.
*/
[scriptable, uuid(7f63279a-1a29-4ae6-9e7a-dc9684a23530)]
interface mozIMozIntl : nsISupports interface mozIMozIntl : nsISupports
{ {
[implicit_jscontext] void addGetCalendarInfo(in jsval intlObject); jsval getCalendarInfo([optional] in jsval locales);
[implicit_jscontext] void addGetDisplayNames(in jsval intlObject); jsval getDisplayNames([optional] in jsval locales, [optional] in jsval options);
[implicit_jscontext] void addGetLocaleInfo(in jsval intlObject); jsval getLocaleInfo([optional] in jsval locales);
/** jsval createPluralRules([optional] in jsval locales, [optional] in jsval options);
* Adds a PluralRules constructor to the given object. This function may only
* be called once within a realm/global object: calling it multiple times will
* throw.
*/
[implicit_jscontext] void addPluralRulesConstructor(in jsval intlObject);
}; };

Просмотреть файл

@ -0,0 +1,29 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
/**
* This is an internal helper for mozIMozIntl API. There should be virtually
* no reason for you to call this API except from mozIMozIntl implementation.
*
* This API helps accessing the SpiderMonkey Intl APIs, but it is mozIMozIntl
* that exposes the thin wrapper around them that binds the functionality
* to Gecko.
*/
[scriptable, uuid(189eaa7d-b29a-43a9-b1fb-7658990df940)]
interface mozIMozIntlHelper : nsISupports
{
[implicit_jscontext] void addGetCalendarInfo(in jsval intlObject);
[implicit_jscontext] void addGetDisplayNames(in jsval intlObject);
[implicit_jscontext] void addGetLocaleInfo(in jsval intlObject);
/**
* Adds a PluralRules constructor to the given object. This function may only
* be called once within a realm/global object: calling it multiple times will
* throw.
*/
[implicit_jscontext] void addPluralRulesConstructor(in jsval intlObject);
};

Просмотреть файл

@ -0,0 +1,69 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
const mozIntlHelper =
Cc["@mozilla.org/mozintlhelper;1"].getService(Ci.mozIMozIntlHelper);
const localeSvc =
Cc["@mozilla.org/intl/localeservice;1"].getService(Ci.mozILocaleService);
/**
* This helper function retrives currently used app locales, allowing
* all mozIntl APIs to use the current app locales unless called with
* explicitly listed locales.
*/
function getLocales(locales) {
if (!locales) {
return localeSvc.getAppLocales();
}
return locales;
}
class MozIntl {
constructor() {
this._cache = {};
}
getCalendarInfo(locales, ...args) {
if (!this._cache.hasOwnProperty("getCalendarInfo")) {
mozIntlHelper.addGetCalendarInfo(this._cache);
}
return this._cache.getCalendarInfo(getLocales(locales), ...args);
}
getDisplayNames(locales, ...args) {
if (!this._cache.hasOwnProperty("getDisplayNames")) {
mozIntlHelper.addGetDisplayNames(this._cache);
}
return this._cache.getDisplayNames(getLocales(locales), ...args);
}
getLocaleInfo(locales, ...args) {
if (!this._cache.hasOwnProperty("getLocaleInfo")) {
mozIntlHelper.addGetLocaleInfo(this._cache);
}
return this._cache.getLocaleInfo(getLocales(locales), ...args);
}
createPluralRules(locales, ...args) {
if (!this._cache.hasOwnProperty("PluralRules")) {
mozIntlHelper.addPluralRulesConstructor(this._cache);
}
return new this._cache.PluralRules(getLocales(locales), ...args);
}
}
MozIntl.prototype.classID = Components.ID("{35ec195a-e8d0-4300-83af-c8a2cc84b4a3}");
MozIntl.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.mozIMozIntl, Ci.nsISupports]);
var components = [MozIntl];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

Просмотреть файл

@ -0,0 +1,2 @@
component {35ec195a-e8d0-4300-83af-c8a2cc84b4a3} mozIntl.js
contract @mozilla.org/mozintl;1 {35ec195a-e8d0-4300-83af-c8a2cc84b4a3}

Просмотреть файл

@ -5,46 +5,23 @@ function run_test() {
const mozIntl = Components.classes["@mozilla.org/mozintl;1"] const mozIntl = Components.classes["@mozilla.org/mozintl;1"]
.getService(Components.interfaces.mozIMozIntl); .getService(Components.interfaces.mozIMozIntl);
test_this_global(mozIntl);
test_cross_global(mozIntl);
test_methods_presence(mozIntl); test_methods_presence(mozIntl);
test_methods_calling(mozIntl);
ok(true); ok(true);
} }
function test_this_global(mozIntl) {
let x = {};
mozIntl.addGetCalendarInfo(x);
equal(x.getCalendarInfo instanceof Function, true);
equal(x.getCalendarInfo() instanceof Object, true);
}
function test_cross_global(mozIntl) {
var global = new Components.utils.Sandbox("https://example.com/");
var x = global.Object();
mozIntl.addGetCalendarInfo(x);
var waivedX = Components.utils.waiveXrays(x);
equal(waivedX.getCalendarInfo instanceof Function, false);
equal(waivedX.getCalendarInfo instanceof global.Function, true);
equal(waivedX.getCalendarInfo() instanceof Object, false);
equal(waivedX.getCalendarInfo() instanceof global.Object, true);
}
function test_methods_presence(mozIntl) { function test_methods_presence(mozIntl) {
equal(mozIntl.addGetCalendarInfo instanceof Function, true); equal(mozIntl.getCalendarInfo instanceof Function, true);
equal(mozIntl.addGetDisplayNames instanceof Function, true); equal(mozIntl.getDisplayNames instanceof Function, true);
equal(mozIntl.addGetLocaleInfo instanceof Function, true); equal(mozIntl.getLocaleInfo instanceof Function, true);
equal(mozIntl.createPluralRules instanceof Function, true);
let x = {}; }
mozIntl.addGetCalendarInfo(x); function test_methods_calling(mozIntl) {
equal(x.getCalendarInfo instanceof Function, true); let ci = mozIntl.getCalendarInfo('pl');
let dn = mozIntl.getDisplayNames('ar');
mozIntl.addGetDisplayNames(x); let li = mozIntl.getLocaleInfo('de');
equal(x.getDisplayNames instanceof Function, true); let pr = mozIntl.createPluralRules('fr');
ok(true);
mozIntl.addGetLocaleInfo(x);
equal(x.getLocaleInfo instanceof Function, true);
} }

Просмотреть файл

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
const miHelper = Components.classes["@mozilla.org/mozintlhelper;1"]
.getService(Components.interfaces.mozIMozIntlHelper);
test_this_global(miHelper);
test_cross_global(miHelper);
test_methods_presence(miHelper);
ok(true);
}
function test_this_global(miHelper) {
let x = {};
miHelper.addGetCalendarInfo(x);
equal(x.getCalendarInfo instanceof Function, true);
equal(x.getCalendarInfo() instanceof Object, true);
}
function test_cross_global(miHelper) {
var global = new Components.utils.Sandbox("https://example.com/");
var x = global.Object();
miHelper.addGetCalendarInfo(x);
var waivedX = Components.utils.waiveXrays(x);
equal(waivedX.getCalendarInfo instanceof Function, false);
equal(waivedX.getCalendarInfo instanceof global.Function, true);
equal(waivedX.getCalendarInfo() instanceof Object, false);
equal(waivedX.getCalendarInfo() instanceof global.Object, true);
}
function test_methods_presence(miHelper) {
equal(miHelper.addGetCalendarInfo instanceof Function, true);
equal(miHelper.addGetDisplayNames instanceof Function, true);
equal(miHelper.addGetLocaleInfo instanceof Function, true);
let x = {};
miHelper.addGetCalendarInfo(x);
equal(x.getCalendarInfo instanceof Function, true);
miHelper.addGetDisplayNames(x);
equal(x.getDisplayNames instanceof Function, true);
miHelper.addGetLocaleInfo(x);
equal(x.getLocaleInfo instanceof Function, true);
}

Просмотреть файл

@ -2,3 +2,4 @@
head = head =
[test_mozintl.js] [test_mozintl.js]
[test_mozintlhelper.js]

Просмотреть файл

@ -23,11 +23,8 @@
<field name="DATE_PICKER_WIDTH" readonly="true">"23.1em"</field> <field name="DATE_PICKER_WIDTH" readonly="true">"23.1em"</field>
<field name="DATE_PICKER_HEIGHT" readonly="true">"20.7em"</field> <field name="DATE_PICKER_HEIGHT" readonly="true">"20.7em"</field>
<constructor><![CDATA[ <constructor><![CDATA[
this.l10n = {}; this.mozIntl = Components.classes["@mozilla.org/mozintl;1"]
const mozIntl = Components.classes["@mozilla.org/mozintl;1"] .getService(Components.interfaces.mozIMozIntl);
.getService(Components.interfaces.mozIMozIntl);
mozIntl.addGetCalendarInfo(this.l10n);
mozIntl.addGetDisplayNames(this.l10n);
]]></constructor> ]]></constructor>
<method name="loadPicker"> <method name="loadPicker">
<parameter name="type"/> <parameter name="type"/>
@ -226,7 +223,7 @@
<method name="getCalendarInfo"> <method name="getCalendarInfo">
<parameter name="locale"/> <parameter name="locale"/>
<body><![CDATA[ <body><![CDATA[
const calendarInfo = this.l10n.getCalendarInfo(locale); const calendarInfo = this.mozIntl.getCalendarInfo(locale);
// Day of week from calendarInfo starts from 1 as Sunday to 7 as Saturday, // Day of week from calendarInfo starts from 1 as Sunday to 7 as Saturday,
// so they need to be mapped to JavaScript convention with 0 as Sunday // so they need to be mapped to JavaScript convention with 0 as Sunday
@ -259,7 +256,7 @@
<parameter name="keys"/> <parameter name="keys"/>
<parameter name="style"/> <parameter name="style"/>
<body><![CDATA[ <body><![CDATA[
const displayNames = this.l10n.getDisplayNames(locale, {keys, style}); const displayNames = this.mozIntl.getDisplayNames(locale, {keys, style});
return keys.map(key => displayNames.values[key]); return keys.map(key => displayNames.values[key]);
]]></body> ]]></body>
</method> </method>