diff --git a/intl/build/nsI18nModule.cpp b/intl/build/nsI18nModule.cpp
index 7512763917bd500820da7117bc4881407d44ca27..ac9ec84bc1a7ac8e818a09b65127e6efb2dced3d 100644
--- a/intl/build/nsI18nModule.cpp
+++ b/intl/build/nsI18nModule.cpp
@@ -44,6 +44,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsEntityConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSaveAsCharset)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeNormalizer)
 
+NS_DEFINE_NAMED_CID(MOZ_LOCALESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_LBRK_CID);
 NS_DEFINE_NAMED_CID(NS_WBRK_CID);
 NS_DEFINE_NAMED_CID(NS_SEMANTICUNITSCANNER_CID);
@@ -70,6 +71,7 @@ NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
 #endif
 
 static const mozilla::Module::CIDEntry kIntlCIDs[] = {
+    { &kMOZ_LOCALESERVICE_CID, false, nullptr, mozilla::intl::LocaleServiceConstructor },
     { &kNS_LBRK_CID, false, nullptr, nsJISx4051LineBreakerConstructor },
     { &kNS_WBRK_CID, false, nullptr, nsSampleWordBreakerConstructor },
     { &kNS_SEMANTICUNITSCANNER_CID, false, nullptr, nsSemanticUnitScannerConstructor },
@@ -98,6 +100,7 @@ static const mozilla::Module::CIDEntry kIntlCIDs[] = {
 };
 
 static const mozilla::Module::ContractIDEntry kIntlContracts[] = {
+    { MOZ_LOCALESERVICE_CONTRACTID, &kMOZ_LOCALESERVICE_CID },
     { NS_LBRK_CONTRACTID, &kNS_LBRK_CID },
     { NS_WBRK_CONTRACTID, &kNS_WBRK_CID },
     { NS_SEMANTICUNITSCANNER_CONTRACTID, &kNS_SEMANTICUNITSCANNER_CID },
diff --git a/intl/locale/LocaleService.cpp b/intl/locale/LocaleService.cpp
index 430ff954cd78cf8d9f0781e3e8b1937bed45f824..36f3c261e6a0addc83f05ab4188d48eb82aad1ed 100644
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -4,6 +4,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LocaleService.h"
+
+#include "jsapi.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
@@ -11,6 +13,10 @@
 
 using namespace mozilla::intl;
 
+NS_IMPL_ISUPPORTS(LocaleService, mozILocaleService)
+
+mozilla::StaticRefPtr<LocaleService> LocaleService::sInstance;
+
 /**
  * This function performs the actual language negotiation for the API.
  *
@@ -35,9 +41,8 @@ ReadAppLocales(nsTArray<nsCString>& aRetVal)
   }
 }
 
-mozilla::StaticAutoPtr<LocaleService> LocaleService::sInstance;
-
-LocaleService* LocaleService::GetInstance()
+LocaleService*
+LocaleService::GetInstance()
 {
   if (!sInstance) {
     sInstance = new LocaleService();
@@ -78,3 +83,36 @@ LocaleService::Refresh()
     }
   }
 }
+
+/**
+ * mozILocaleService methods
+ */
+NS_IMETHODIMP
+LocaleService::GetAppLocales(JSContext* aCtx, JS::MutableHandleValue aRetVal)
+{
+  if (mAppLocales.IsEmpty()) {
+    ReadAppLocales(mAppLocales);
+  }
+
+  uint32_t appLocalesNum = mAppLocales.Length();
+
+  JS::RootedObject locales(aCtx, JS_NewArrayObject(aCtx, appLocalesNum));
+  JS::Rooted<JS::Value> value(aCtx);
+
+  for (size_t i = 0; i < appLocalesNum; i++) {
+    const nsCString& loc = mAppLocales[i];
+    JSString* str = JS_NewStringCopyN(aCtx, loc.get(), loc.Length());
+    value.setString(str);
+    JS_DefineElement(aCtx, locales, i, value, JSPROP_ENUMERATE);
+  }
+
+  aRetVal.setObject(*locales);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LocaleService::GetAppLocale(JSContext* aCtx, nsACString& aRetVal)
+{
+  GetAppLocale(aRetVal);
+  return NS_OK;
+}
diff --git a/intl/locale/LocaleService.h b/intl/locale/LocaleService.h
index e5228418c41a7ef8f3ffa32f22d29d7a937aa60c..2de7673ed4e591294808b406fd3384c522b68f21 100644
--- a/intl/locale/LocaleService.h
+++ b/intl/locale/LocaleService.h
@@ -10,10 +10,11 @@
 #include "nsString.h"
 #include "nsTArray.h"
 
+#include "mozILocaleService.h"
+
 namespace mozilla {
 namespace intl {
 
-
 /**
  * LocaleService is a manager of language negotiation in Gecko.
  *
@@ -21,11 +22,30 @@ namespace intl {
  * requested languages and negotiating them to produce a fallback
  * chain of locales for the application.
  */
-class LocaleService
+class LocaleService : public mozILocaleService
 {
 public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZILOCALESERVICE
+
+  /**
+   * Create (if necessary) and return a raw pointer to the singleton instance.
+   * Use this accessor in C++ code that just wants to call a method on the
+   * instance, but does not need to hold a reference, as in
+   *    nsAutoCString str;
+   *    LocaleService::GetInstance()->GetAppLocale(str);
+   */
   static LocaleService* GetInstance();
 
+  /**
+   * Return an addRef'd pointer to the singleton instance. This is used by the
+   * XPCOM constructor that exists to support usage from JS.
+   */
+  static already_AddRefed<LocaleService> GetInstanceAddRefed()
+  {
+    return RefPtr<LocaleService>(GetInstance()).forget();
+  }
+
   /**
    * Returns a list of locales that the application should be localized to.
    *
@@ -73,7 +93,9 @@ protected:
   nsTArray<nsCString> mAppLocales;
 
 private:
-  static StaticAutoPtr<LocaleService> sInstance;
+  virtual ~LocaleService() {};
+
+  static StaticRefPtr<LocaleService> sInstance;
 };
 
 } // intl
diff --git a/intl/locale/moz.build b/intl/locale/moz.build
index 3d72bdd98b2b2789276bf74e03a33025ef4bc206..9a03810c1f4bbab06103851e8e0801f9c7415012 100644
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -19,6 +19,7 @@ else:
     DIRS += ['unix']
 
 XPIDL_SOURCES += [
+    'mozILocaleService.idl',
     'nsICollation.idl',
     'nsILocale.idl',
     'nsILocaleService.idl',
diff --git a/intl/locale/mozILocaleService.idl b/intl/locale/mozILocaleService.idl
new file mode 100644
index 0000000000000000000000000000000000000000..9e517bcb7053228532d17e04ea8a7c0b6ba023d8
--- /dev/null
+++ b/intl/locale/mozILocaleService.idl
@@ -0,0 +1,21 @@
+/* -*- 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"
+
+%{C++
+// Define Contractid and CID
+#define MOZ_LOCALESERVICE_CID \
+  { 0x92735ff4, 0x6384, 0x4ad6, { 0x85, 0x08, 0x75, 0x70, 0x10, 0xe1, 0x49, 0xee } }
+
+#define MOZ_LOCALESERVICE_CONTRACTID "@mozilla.org/intl/localeservice;1"
+%}
+
+[scriptable, uuid(C27F8983-B48B-4D1A-92D7-FEB8106F212D)]
+interface mozILocaleService : nsISupports
+{
+  [implicit_jscontext] jsval getAppLocales();
+  [implicit_jscontext] ACString getAppLocale();
+};
diff --git a/intl/locale/nsLocaleConstructors.h b/intl/locale/nsLocaleConstructors.h
index a4443ab10db7758abb5fca4157e153e7eeeaf6d0..ce9d3b0a533def52e1fbd7cfadf6a5612bec68e8 100644
--- a/intl/locale/nsLocaleConstructors.h
+++ b/intl/locale/nsLocaleConstructors.h
@@ -13,6 +13,7 @@
 #include "nsIServiceManager.h"
 #include "nsLanguageAtomService.h"
 #include "nsPlatformCharset.h"
+#include "LocaleService.h"
 
 #if defined(XP_MACOSX)
 #define USE_MAC_LOCALE
@@ -57,6 +58,13 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationFactory)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsLanguageAtomService)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPlatformCharset, Init)
 
+namespace mozilla {
+namespace intl {
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(LocaleService,
+                                         LocaleService::GetInstanceAddRefed)
+}
+}
+
 #ifdef XP_WIN
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationWin)
 #endif
diff --git a/intl/locale/tests/unit/test_localeService.js b/intl/locale/tests/unit/test_localeService.js
new file mode 100644
index 0000000000000000000000000000000000000000..4cd9c1742fad3c2f678815f87dcf76491a7fd866
--- /dev/null
+++ b/intl/locale/tests/unit/test_localeService.js
@@ -0,0 +1,27 @@
+/* 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/. */
+
+/**
+ * Make sure the locale service can be instantiated,
+ * and returns something plausible for getAppLocale and getAppLocales.
+ */
+
+function run_test()
+{
+  var localeService =
+    Components.classes["@mozilla.org/intl/localeservice;1"]
+    .getService(Components.interfaces.mozILocaleService);
+
+  var appLocale = localeService.getAppLocale();
+  do_check_true(appLocale != "", "appLocale is non-empty");
+
+  var appLocales = localeService.getAppLocales();
+  do_check_true(Array.isArray(appLocales), "appLocales returns an array");
+
+  do_check_true(appLocale == appLocales[0], "appLocale matches first entry in appLocales");
+
+  var enUScount = 0;
+  appLocales.forEach(function(loc) { if (loc == "en-US") { enUScount++; } });
+  do_check_true(enUScount == 1, "en-US is present exactly one time");
+}
diff --git a/intl/locale/tests/unit/xpcshell.ini b/intl/locale/tests/unit/xpcshell.ini
index e8af701b9a0e2806c2914b6466fb26b84c8ad2a3..ade64253e51167ffd05238ab09508abfa8ad3dd9 100644
--- a/intl/locale/tests/unit/xpcshell.ini
+++ b/intl/locale/tests/unit/xpcshell.ini
@@ -21,3 +21,5 @@ skip-if = toolkit == "android" # bug 1309447
 [test_pluralForm.js]
 [test_pluralForm_english.js]
 [test_pluralForm_makeGetter.js]
+
+[test_localeService.js]