Bug 1339892 - Refactor mozIntl to have a nicer API and thin logic. r=smaug draft
authorZibi Braniecki <gandalf@mozilla.com>
Wed, 01 Mar 2017 16:58:11 -0800
changeset 492092 9753dbeea2e11c7fde550df1a20b1c3d1b2063fe
parent 491621 6e09bb36c1d4fe89ed22c2fc5ef8de3ff8ed484d
child 492633 57d621d6142c2f5d28e486a74f3ee508e935e39c
push id47524
push userzbraniecki@mozilla.com
push dateThu, 02 Mar 2017 19:49:19 +0000
reviewerssmaug
bugs1339892
milestone54.0a1
Bug 1339892 - Refactor mozIntl to have a nicer API and thin logic. r=smaug MozReview-Commit-ID: J4QXXBy7JII
browser/installer/package-manifest.in
mobile/android/installer/package-manifest.in
toolkit/components/mozintl/MozIntl.cpp
toolkit/components/mozintl/MozIntl.h
toolkit/components/mozintl/MozIntlHelper.cpp
toolkit/components/mozintl/MozIntlHelper.h
toolkit/components/mozintl/moz.build
toolkit/components/mozintl/mozIMozIntl.idl
toolkit/components/mozintl/mozIMozIntlHelper.idl
toolkit/components/mozintl/mozIntl.js
toolkit/components/mozintl/mozIntl.manifest
toolkit/components/mozintl/test/test_mozintl.js
toolkit/components/mozintl/test/test_mozintlhelper.js
toolkit/components/mozintl/test/xpcshell.ini
toolkit/content/widgets/datetimepopup.xml
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -545,16 +545,21 @@
 
 @RESPATH@/components/PresentationDeviceInfoManager.manifest
 @RESPATH@/components/PresentationDeviceInfoManager.js
 @RESPATH@/components/BuiltinProviders.manifest
 @RESPATH@/components/PresentationControlService.js
 @RESPATH@/components/PresentationDataChannelSessionTransport.js
 @RESPATH@/components/PresentationDataChannelSessionTransport.manifest
 
+#ifdef ENABLE_INTL_API
+@RESPATH@/components/mozIntl.manifest
+@RESPATH@/components/mozIntl.js
+#endif
+
 #if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
 @RESPATH@/components/TestInterfaceJS.js
 @RESPATH@/components/TestInterfaceJS.manifest
 @RESPATH@/components/TestInterfaceJSMaplike.js
 #endif
 
 ; [Extensions]
 @RESPATH@/components/extensions-toolkit.manifest
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -393,16 +393,21 @@
 @BINPATH@/components/PresentationDataChannelSessionTransport.js
 @BINPATH@/components/PresentationDataChannelSessionTransport.manifest
 @BINPATH@/components/AndroidCastDeviceProvider.manifest
 @BINPATH@/components/AndroidCastDeviceProvider.js
 
 @BINPATH@/components/TVSimulatorService.js
 @BINPATH@/components/TVSimulatorService.manifest
 
+#ifdef ENABLE_INTL_API
+@BINPATH@/components/mozIntl.manifest
+@BINPATH@/components/mozIntl.js
+#endif
+
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
 @BINPATH@/components/nsUrlClassifierListManager.js
 @BINPATH@/components/nsUrlClassifierLib.js
rename from toolkit/components/mozintl/MozIntl.cpp
rename to toolkit/components/mozintl/MozIntlHelper.cpp
--- a/toolkit/components/mozintl/MozIntl.cpp
+++ b/toolkit/components/mozintl/MozIntlHelper.cpp
@@ -1,27 +1,27 @@
 /* -*- Mode: C++; 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 "MozIntl.h"
+#include "MozIntlHelper.h"
 #include "jswrapper.h"
 #include "mozilla/ModuleUtils.h"
 
-#define MOZ_MOZINTL_CID \
-  { 0x83f8f991, 0x6b81, 0x4dd8, { 0xa0, 0x93, 0x72, 0x0b, 0xfb, 0x67, 0x4d, 0x38 } }
+#define MOZ_MOZINTLHELPER_CID \
+  { 0xb43c96be, 0x2b3a, 0x4dc4, { 0x90, 0xe9, 0xb0, 0x6d, 0x34, 0x21, 0x9b, 0x68 } }
 
 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
 AddFunctions(JSContext* cx, JS::Handle<JS::Value> val, const JSFunctionSpec* funcs)
 {
   if (!val.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -35,39 +35,39 @@ AddFunctions(JSContext* cx, JS::Handle<J
   if (!JS_DefineFunctions(cx, realIntlObj, funcs)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MozIntl::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx)
+MozIntlHelper::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx)
 {
   static const JSFunctionSpec funcs[] = {
     JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
     JS_FS_END
   };
 
   return AddFunctions(cx, val, funcs);
 }
 
 NS_IMETHODIMP
-MozIntl::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx)
+MozIntlHelper::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx)
 {
   static const JSFunctionSpec funcs[] = {
     JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
     JS_FS_END
   };
 
   return AddFunctions(cx, val, funcs);
 }
 
 NS_IMETHODIMP
-MozIntl::AddPluralRulesConstructor(JS::Handle<JS::Value> val, JSContext* cx)
+MozIntlHelper::AddPluralRulesConstructor(JS::Handle<JS::Value> val, JSContext* cx)
 {
   if (!val.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   JS::Rooted<JSObject*> realIntlObj(cx, js::CheckedUnwrap(&val.toObject()));
   if (!realIntlObj) {
     return NS_ERROR_INVALID_ARG;
@@ -78,42 +78,42 @@ MozIntl::AddPluralRulesConstructor(JS::H
   if (!js::AddPluralRulesConstructor(cx, realIntlObj)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MozIntl::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx)
+MozIntlHelper::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx)
 {
   static const JSFunctionSpec funcs[] = {
     JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
     JS_FS_END
   };
 
   return AddFunctions(cx, val, funcs);
 }
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntl)
-NS_DEFINE_NAMED_CID(MOZ_MOZINTL_CID);
+NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntlHelper)
+NS_DEFINE_NAMED_CID(MOZ_MOZINTLHELPER_CID);
 
-static const Module::CIDEntry kMozIntlCIDs[] = {
-  { &kMOZ_MOZINTL_CID, false, nullptr, MozIntlConstructor },
+static const Module::CIDEntry kMozIntlHelperCIDs[] = {
+  { &kMOZ_MOZINTLHELPER_CID, false, nullptr, MozIntlHelperConstructor },
   { nullptr }
 };
 
-static const mozilla::Module::ContractIDEntry kMozIntlContracts[] = {
-  { "@mozilla.org/mozintl;1", &kMOZ_MOZINTL_CID },
+static const mozilla::Module::ContractIDEntry kMozIntlHelperContracts[] = {
+  { "@mozilla.org/mozintlhelper;1", &kMOZ_MOZINTLHELPER_CID },
   { nullptr }
 };
 
-static const mozilla::Module kMozIntlModule = {
+static const mozilla::Module kMozIntlHelperModule = {
   mozilla::Module::kVersion,
-  kMozIntlCIDs,
-  kMozIntlContracts,
+  kMozIntlHelperCIDs,
+  kMozIntlHelperContracts,
   nullptr,
   nullptr,
   nullptr,
   nullptr
 };
 
-NSMODULE_DEFN(mozMozIntlModule) = &kMozIntlModule;
+NSMODULE_DEFN(mozMozIntlHelperModule) = &kMozIntlHelperModule;
rename from toolkit/components/mozintl/MozIntl.h
rename to toolkit/components/mozintl/MozIntlHelper.h
--- a/toolkit/components/mozintl/MozIntl.h
+++ b/toolkit/components/mozintl/MozIntlHelper.h
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; 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 "mozIMozIntl.h"
+#include "mozIMozIntlHelper.h"
 
 namespace mozilla {
 
-class MozIntl final : public mozIMozIntl
+class MozIntlHelper final : public mozIMozIntlHelper
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_MOZIMOZINTL
+  NS_DECL_MOZIMOZINTLHELPER
 
-  MozIntl();
+  MozIntlHelper();
 
 private:
-  ~MozIntl();
+  ~MozIntlHelper();
 };
 
 } // namespace mozilla
--- a/toolkit/components/mozintl/moz.build
+++ b/toolkit/components/mozintl/moz.build
@@ -3,17 +3,23 @@
 # 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/.
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
 
 XPIDL_SOURCES += [
     'mozIMozIntl.idl',
+    'mozIMozIntlHelper.idl',
 ]
 
 XPIDL_MODULE = 'mozintl'
 
 SOURCES += [
-    'MozIntl.cpp',
+    'MozIntlHelper.cpp',
+]
+
+EXTRA_COMPONENTS += [
+    'mozIntl.js',
+    'mozIntl.manifest',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/toolkit/components/mozintl/mozIMozIntl.idl
+++ b/toolkit/components/mozintl/mozIMozIntl.idl
@@ -1,21 +1,45 @@
 /* -*- 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"
 
-[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
 {
-  [implicit_jscontext] void addGetCalendarInfo(in jsval intlObject);
-  [implicit_jscontext] void addGetDisplayNames(in jsval intlObject);
-  [implicit_jscontext] void addGetLocaleInfo(in jsval intlObject);
+  jsval getCalendarInfo([optional] in jsval locales);
+  jsval getDisplayNames([optional] in jsval locales, [optional] in jsval options);
+  jsval getLocaleInfo([optional] in jsval locales);
 
-  /**
-   * 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);
+  jsval createPluralRules([optional] in jsval locales, [optional] in jsval options);
 };
copy from toolkit/components/mozintl/mozIMozIntl.idl
copy to toolkit/components/mozintl/mozIMozIntlHelper.idl
--- a/toolkit/components/mozintl/mozIMozIntl.idl
+++ b/toolkit/components/mozintl/mozIMozIntlHelper.idl
@@ -1,17 +1,25 @@
 /* -*- 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"
 
-[scriptable, uuid(9f9bc42e-54f4-11e6-9aed-4b1429ac0ba0)]
-interface mozIMozIntl : nsISupports
+/**
+ * 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
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mozintl/mozIntl.js
@@ -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);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mozintl/mozIntl.manifest
@@ -0,0 +1,2 @@
+component {35ec195a-e8d0-4300-83af-c8a2cc84b4a3} mozIntl.js
+contract @mozilla.org/mozintl;1 {35ec195a-e8d0-4300-83af-c8a2cc84b4a3}
--- a/toolkit/components/mozintl/test/test_mozintl.js
+++ b/toolkit/components/mozintl/test/test_mozintl.js
@@ -1,50 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function run_test() {
   const mozIntl = Components.classes["@mozilla.org/mozintl;1"]
                             .getService(Components.interfaces.mozIMozIntl);
 
-  test_this_global(mozIntl);
-  test_cross_global(mozIntl);
   test_methods_presence(mozIntl);
+  test_methods_calling(mozIntl);
 
   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_methods_presence(mozIntl) {
+  equal(mozIntl.getCalendarInfo instanceof Function, true);
+  equal(mozIntl.getDisplayNames instanceof Function, true);
+  equal(mozIntl.getLocaleInfo instanceof Function, true);
+  equal(mozIntl.createPluralRules instanceof Function, 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_calling(mozIntl) {
+  let ci = mozIntl.getCalendarInfo('pl');
+  let dn = mozIntl.getDisplayNames('ar');
+  let li = mozIntl.getLocaleInfo('de');
+  let pr = mozIntl.createPluralRules('fr');
+  ok(true);
 }
-
-function test_methods_presence(mozIntl) {
-  equal(mozIntl.addGetCalendarInfo instanceof Function, true);
-  equal(mozIntl.addGetDisplayNames instanceof Function, true);
-  equal(mozIntl.addGetLocaleInfo instanceof Function, true);
-
-  let x = {};
-
-  mozIntl.addGetCalendarInfo(x);
-  equal(x.getCalendarInfo instanceof Function, true);
-
-  mozIntl.addGetDisplayNames(x);
-  equal(x.getDisplayNames instanceof Function, true);
-
-  mozIntl.addGetLocaleInfo(x);
-  equal(x.getLocaleInfo instanceof Function, true);
-}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mozintl/test/test_mozintlhelper.js
@@ -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);
+}
--- a/toolkit/components/mozintl/test/xpcshell.ini
+++ b/toolkit/components/mozintl/test/xpcshell.ini
@@ -1,4 +1,5 @@
 [DEFAULT]
 head =
 
 [test_mozintl.js]
+[test_mozintlhelper.js]
--- a/toolkit/content/widgets/datetimepopup.xml
+++ b/toolkit/content/widgets/datetimepopup.xml
@@ -18,21 +18,18 @@
       <field name="dateTimePopupFrame">
         this.querySelector("#dateTimePopupFrame");
       </field>
       <field name="TIME_PICKER_WIDTH" readonly="true">"12em"</field>
       <field name="TIME_PICKER_HEIGHT" readonly="true">"21em"</field>
       <field name="DATE_PICKER_WIDTH" readonly="true">"23.1em"</field>
       <field name="DATE_PICKER_HEIGHT" readonly="true">"20.7em"</field>
       <constructor><![CDATA[
-        this.l10n = {};
-        const mozIntl = Components.classes["@mozilla.org/mozintl;1"]
-                          .getService(Components.interfaces.mozIMozIntl);
-        mozIntl.addGetCalendarInfo(this.l10n);
-        mozIntl.addGetDisplayNames(this.l10n);
+        this.mozIntl = Components.classes["@mozilla.org/mozintl;1"]
+                                 .getService(Components.interfaces.mozIMozIntl);
       ]]></constructor>
       <method name="loadPicker">
         <parameter name="type"/>
         <parameter name="detail"/>
         <body><![CDATA[
           this.hidden = false;
           this.type = type;
           this.pickerState = {};
@@ -221,17 +218,17 @@
               break;
             }
           }
         ]]></body>
       </method>
       <method name="getCalendarInfo">
         <parameter name="locale"/>
         <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,
           // so they need to be mapped to JavaScript convention with 0 as Sunday
           // and 6 as Saturday
           let firstDayOfWeek = calendarInfo.firstDayOfWeek - 1,
               weekendStart = calendarInfo.weekendStart - 1,
               weekendEnd = calendarInfo.weekendEnd - 1;
 
@@ -254,17 +251,17 @@
           }
         ]]></body>
       </method>
       <method name="getDisplayNames">
         <parameter name="locale"/>
         <parameter name="keys"/>
         <parameter name="style"/>
         <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]);
         ]]></body>
       </method>
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
             case "load": {