--- a/browser/components/preferences/applicationManager.xul
+++ b/browser/components/preferences/applicationManager.xul
@@ -2,26 +2,28 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/"?>
<dialog id="appManager"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
buttons="accept,cancel"
onload="gAppManagerDialog.onLoad();"
ondialogaccept="gAppManagerDialog.onOK();"
ondialogcancel="gAppManagerDialog.onCancel();"
data-l10n-id="app-manager-window"
data-l10n-attrs="title, style"
persist="screenX screenY">
- <link rel="localization" href="browser/preferences/applicationManager.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/applicationManager.ftl"/>
+ </linkset>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://global/content/preferencesBindings.js"/>
<script type="application/javascript"
src="chrome://browser/content/preferences/applicationManager.js"/>
--- a/browser/components/preferences/blocklists.xul
+++ b/browser/components/preferences/blocklists.xul
@@ -7,24 +7,26 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<window id="BlocklistsDialog" class="windowDialog"
windowtype="Browser:Blocklists"
data-l10n-id="blocklist-window"
data-l10n-attrs="title, style"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
onload="gBlocklistManager.onLoad();"
onunload="gBlocklistManager.uninit();"
persist="screenX screenY width height"
onkeypress="gBlocklistManager.onWindowKeyPress(event);">
- <link rel="localization" href="branding/brand.ftl"/>
- <link rel="localization" href="browser/preferences/blocklists.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="branding/brand.ftl"/>
+ <html:link rel="localization" href="browser/preferences/blocklists.ftl"/>
+ </linkset>
<script src="chrome://global/content/treeUtils.js"/>
<script src="chrome://browser/content/preferences/blocklists.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<keyset>
--- a/browser/components/preferences/clearSiteData.xul
+++ b/browser/components/preferences/clearSiteData.xul
@@ -6,23 +6,26 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/clearSiteData.css" type="text/css"?>
<window id="ClearSiteDataDialog" class="windowDialog"
windowtype="Browser:ClearSiteData"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="clear-site-data-window"
data-l10n-attrs="title, style"
persist="screenX screenY width height">
- <link rel="localization" href="branding/brand.ftl"/>
- <link rel="localization" href="browser/preferences/clearSiteData.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="branding/brand.ftl"/>
+ <html:link rel="localization" href="browser/preferences/clearSiteData.ftl"/>
+ </linkset>
+
<script src="chrome://browser/content/preferences/clearSiteData.js"/>
<keyset>
<key data-l10n-id="clear-site-data-close-key" modifiers="accel" oncommand="window.close();"/>
</keyset>
<vbox class="contentPane largeDialogContainer" flex="1">
<description control="url" data-l10n-id="clear-site-data-description"/>
--- a/browser/components/preferences/colors.xul
+++ b/browser/components/preferences/colors.xul
@@ -5,26 +5,28 @@
# 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/.
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<dialog id="ColorsDialog" type="child" class="prefwindow"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="colors-window"
data-l10n-attrs="title, style"
buttons="accept,cancel,help"
persist="lastSelected screenX screenY"
role="dialog"
helpTopic="prefs-fonts-and-colors"
ondialoghelp="openPrefsHelp()">
- <link rel="localization" href="browser/preferences/colors.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/colors.ftl"/>
+ </linkset>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
<keyset>
<key data-l10n-id="colors-close-key" modifiers="accel" oncommand="Preferences.close(event)"/>
</keyset>
--- a/browser/components/preferences/connection.xul
+++ b/browser/components/preferences/connection.xul
@@ -4,32 +4,34 @@
- 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/. -->
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<dialog id="ConnectionsDialog" type="child" class="prefwindow"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="connection-window"
data-l10n-attrs="title, style"
buttons="accept,cancel,help"
persist="lastSelected screenX screenY"
role="dialog"
onbeforeaccept="return gConnectionsDialog.beforeAccept();"
onload="gConnectionsDialog.checkForSystemProxy();"
helpTopic="prefs-connection-settings"
ondialoghelp="openPrefsHelp()">
- <link rel="localization" href="browser/preferences/connection.ftl"/>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/connection.ftl"/>
- <!-- Used for extension-controlled lockdown message -->
- <link rel="localization" href="browser/preferences/preferences.ftl"/>
- <link rel="localization" href="branding/brand.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <!-- Used for extension-controlled lockdown message -->
+ <html:link rel="localization" href="browser/preferences/preferences.ftl"/>
+ <html:link rel="localization" href="branding/brand.ftl"/>
+ </linkset>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
<script type="application/javascript" src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>
<keyset>
<key data-l10n-id="connection-close-key" modifiers="accel" oncommand="Preferences.close(event)"/>
</keyset>
--- a/browser/components/preferences/containers.xul
+++ b/browser/components/preferences/containers.xul
@@ -5,24 +5,26 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css" type="text/css"?>
<window id="ContainersDialog" class="windowDialog"
windowtype="Browser:Permissions"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-attrs="title, style"
onload="gContainersManager.onLoad();"
onunload="gContainersManager.uninit();"
persist="screenX screenY width height"
onkeypress="gContainersManager.onWindowKeyPress(event);">
- <link rel="localization" href="browser/preferences/containers.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/containers.ftl"/>
+ </linkset>
<script src="chrome://global/content/treeUtils.js"/>
<script src="chrome://browser/content/preferences/containers.js"/>
<keyset>
<key data-l10n-id="containers-window-close" modifiers="accel" oncommand="window.close();"/>
</keyset>
--- a/browser/components/preferences/fonts.xul
+++ b/browser/components/preferences/fonts.xul
@@ -5,27 +5,29 @@
- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<dialog id="FontsDialog" type="child" class="prefwindow"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="fonts-window"
data-l10n-attrs="title"
buttons="accept,cancel,help"
persist="lastSelected screenX screenY"
role="dialog"
helpTopic="prefs-fonts-and-colors"
ondialoghelp="openPrefsHelp()"
onbeforeaccept="return gFontsDialog.onBeforeAccept();">
- <link rel="localization" href="browser/preferences/fonts.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/fonts.ftl"/>
+ </linkset>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
<keyset>
<key data-l10n-id="fonts-window-close" modifiers="accel" oncommand="Preferences.close(event)"/>
</keyset>
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -32,32 +32,32 @@
]>
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
disablefastfind="true"
data-l10n-id="pref-page"
data-l10n-attrs="title">
- <link rel="localization" href="branding/brand.ftl"/>
- <link rel="localization" href="browser/branding/sync-brand.ftl"/>
- <link rel="localization" href="browser/preferences/preferences.ftl"/>
+ <linkset>
+ <html:link rel="localization" href="branding/brand.ftl"/>
+ <html:link rel="localization" href="browser/branding/sync-brand.ftl"/>
+ <html:link rel="localization" href="browser/preferences/preferences.ftl"/>
- <!-- Links below are only used for search-l10n-ids into subdialogs -->
- <link rel="localization" href="browser/preferences/blocklists.ftl"/>
- <link rel="localization" href="browser/preferences/clearSiteData.ftl"/>
- <link rel="localization" href="browser/preferences/colors.ftl"/>
- <link rel="localization" href="browser/preferences/connection.ftl"/>
- <link rel="localization" href="browser/preferences/fonts.ftl"/>
- <link rel="localization" href="browser/preferences/languages.ftl"/>
- <link rel="localization" href="browser/preferences/permissions.ftl"/>
- <link rel="localization" href="browser/preferences/selectBookmark.ftl"/>
- <link rel="localization" href="browser/preferences/siteDataSettings.ftl"/>
-
- <script type="text/javascript" src="chrome://global/content/l10n.js"></script>
+ <!-- Links below are only used for search-l10n-ids into subdialogs -->
+ <html:link rel="localization" href="browser/preferences/blocklists.ftl"/>
+ <html:link rel="localization" href="browser/preferences/clearSiteData.ftl"/>
+ <html:link rel="localization" href="browser/preferences/colors.ftl"/>
+ <html:link rel="localization" href="browser/preferences/connection.ftl"/>
+ <html:link rel="localization" href="browser/preferences/fonts.ftl"/>
+ <html:link rel="localization" href="browser/preferences/languages.ftl"/>
+ <html:link rel="localization" href="browser/preferences/permissions.ftl"/>
+ <html:link rel="localization" href="browser/preferences/selectBookmark.ftl"/>
+ <html:link rel="localization" href="browser/preferences/siteDataSettings.ftl"/>
+ </linkset>
<html:link rel="shortcut icon"
href="chrome://browser/skin/settings.svg"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://global/content/preferencesBindings.js"/>
--- a/browser/components/preferences/languages.xul
+++ b/browser/components/preferences/languages.xul
@@ -5,27 +5,29 @@
- 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/. -->
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<dialog id="LanguagesDialog" type="child" class="prefwindow"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="languages-window"
data-l10n-attrs="title, style"
buttons="accept,cancel,help"
persist="lastSelected screenX screenY"
role="dialog"
onload="gLanguagesDialog.init();"
helpTopic="prefs-languages"
ondialoghelp="openPrefsHelp()">
- <link rel="localization" href="browser/preferences/languages.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/languages.ftl"/>
+ </linkset>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
<script type="application/javascript" src="chrome://browser/content/preferences/languages.js"/>
<keyset>
<key data-l10n-id="languages-close-key" modifiers="accel" oncommand="Preferences.close(event)"/>
</keyset>
--- a/browser/components/preferences/permissions.xul
+++ b/browser/components/preferences/permissions.xul
@@ -7,23 +7,25 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<window id="PermissionsDialog" class="windowDialog"
windowtype="Browser:Permissions"
data-l10n-id="permissions-window"
data-l10n-attrs="title, style"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
onload="gPermissionManager.onLoad();"
onunload="gPermissionManager.uninit();"
persist="screenX screenY width height"
onkeypress="gPermissionManager.onWindowKeyPress(event);">
- <link rel="localization" href="browser/preferences/permissions.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/permissions.ftl"/>
+ </linkset>
<script src="chrome://global/content/treeUtils.js"/>
<script src="chrome://browser/content/preferences/permissions.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<keyset>
--- a/browser/components/preferences/selectBookmark.xul
+++ b/browser/components/preferences/selectBookmark.xul
@@ -6,24 +6,26 @@
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<dialog id="selectBookmarkDialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="select-bookmark-window"
data-l10n-attrs="title, style"
persist="screenX screenY width height" screenX="24" screenY="24"
onload="SelectBookmarkDialog.init();"
ondialogaccept="SelectBookmarkDialog.accept();">
- <link rel="localization" href="browser/preferences/selectBookmark.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/selectBookmark.ftl"/>
+ </linkset>
<script type="application/javascript"
src="chrome://browser/content/preferences/selectBookmark.js"/>
<script type="application/javascript"
src="chrome://global/content/globalOverlay.js"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"><![CDATA[
--- a/browser/components/preferences/siteDataRemoveSelected.xul
+++ b/browser/components/preferences/siteDataRemoveSelected.xul
@@ -11,20 +11,22 @@
<dialog id="SiteDataRemoveSelectedDialog"
windowtype="Browser:SiteDataRemoveSelected"
width="500"
data-l10n-id="site-data-removing-window"
data-l10n-attrs="title"
onload="gSiteDataRemoveSelected.init();"
ondialogaccept="gSiteDataRemoveSelected.ondialogaccept(); return true;"
ondialogcancel="gSiteDataRemoveSelected.ondialogcancel(); return true;"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
- <link rel="localization" href="browser/preferences/siteDataSettings.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/siteDataSettings.ftl"/>
+ </linkset>
<script src="chrome://browser/content/preferences/siteDataRemoveSelected.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<vbox id="contentContainer">
<hbox flex="1">
--- a/browser/components/preferences/siteDataSettings.xul
+++ b/browser/components/preferences/siteDataSettings.xul
@@ -9,23 +9,25 @@
<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/siteDataSettings.css" type="text/css"?>
<window id="SiteDataSettingsDialog" windowtype="Browser:SiteDataSettings"
data-l10n-id="site-data-settings-window"
data-l10n-attrs="title"
class="windowDialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
style="width: 45em;"
onload="gSiteDataSettings.init();"
onkeypress="gSiteDataSettings.onKeyPress(event);"
persist="screenX screenY width height">
- <link rel="localization" href="browser/preferences/siteDataSettings.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/siteDataSettings.ftl"/>
+ </linkset>
<script src="chrome://browser/content/preferences/siteDataSettings.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
<vbox flex="1">
--- a/browser/components/preferences/sitePermissions.xul
+++ b/browser/components/preferences/sitePermissions.xul
@@ -6,25 +6,27 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/sitePermissions.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<window id="SitePermissionsDialog" class="windowDialog"
windowtype="Browser:SitePermissions"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
data-l10n-id="permissions-window"
data-l10n-attrs="style"
onload="gSitePermissionsManager.onLoad();"
onunload="gSitePermissionsManager.uninit();"
persist="screenX screenY width height"
onkeypress="gSitePermissionsManager.onWindowKeyPress(event);">
- <link rel="localization" href="browser/preferences/permissions.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/permissions.ftl"/>
+ </linkset>
<script src="chrome://browser/content/preferences/sitePermissions.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<keyset>
<key data-l10n-id="permissions-close-key" modifiers="accel" oncommand="window.close();"/>
--- a/browser/components/preferences/translation.xul
+++ b/browser/components/preferences/translation.xul
@@ -7,23 +7,25 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<window id="TranslationDialog" class="windowDialog"
windowtype="Browser:TranslationExceptions"
data-l10n-id="translation-window"
data-l10n-attrs="title, style"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
onload="gTranslationExceptions.onLoad();"
onunload="gTranslationExceptions.uninit();"
persist="screenX screenY width height"
onkeypress="gTranslationExceptions.onWindowKeyPress(event);">
- <link rel="localization" href="browser/preferences/translation.ftl"/>
- <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+ <linkset>
+ <html:link rel="localization" href="browser/preferences/translation.ftl"/>
+ </linkset>
<script src="chrome://browser/content/preferences/translation.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<keyset>
<key data-l10n-id="translation-close-key" modifiers="accel" oncommand="window.close();"/>
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -355,16 +355,19 @@
@RESPATH@/components/TestInterfaceJSMaplike.js
#endif
#if defined(MOZ_DEBUG) || defined(MOZ_DEV_EDITION) || defined(NIGHTLY_BUILD)
@RESPATH@/browser/components/testComponents.manifest
@RESPATH@/browser/components/startupRecorder.js
#endif
+@RESPATH@/components/mozDocumentL10nHelper.js
+@RESPATH@/components/l10n.manifest
+
; [Extensions]
@RESPATH@/components/extensions-toolkit.manifest
@RESPATH@/components/extension-process-script.js
@RESPATH@/browser/components/extensions-browser.manifest
; Modules
@RESPATH@/browser/modules/*
@RESPATH@/modules/*
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -204,16 +204,17 @@
#include "nsWrapperCacheInlines.h"
#include "nsSandboxFlags.h"
#include "mozilla/dom/AnimatableBinding.h"
#include "mozilla/dom/AnonymousContent.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/DocumentL10n.h"
#include "mozilla/dom/DocumentTimeline.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/ImageTracker.h"
#include "mozilla/dom/MediaQueryList.h"
#include "mozilla/dom/NodeFilterBinding.h"
#include "mozilla/OwningNonNull.h"
@@ -1866,16 +1867,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
}
// Traverse all nsIDocument pointer members.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAboutCapabilities)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n)
// Traverse all nsDocument nsCOMPtrs.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
@@ -2021,16 +2023,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAboutCapabilities)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n)
tmp->mParentDocument = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
tmp->ClearAllBoxObjects();
@@ -2219,16 +2222,44 @@ nsIDocument::Reset(nsIChannel* aChannel,
mDocumentBaseURI = baseURI;
mChromeXHRDocBaseURI = nullptr;
}
}
mChannel = aChannel;
}
+bool
+PrincipalAllowsL10n(nsIPrincipal* principal) {
+ // Fast track privileged contexts
+ if (nsContentUtils::IsSystemPrincipal(principal)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = principal->GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv) || !uri) {
+ return false;
+ }
+
+ bool isAbout;
+ rv = uri->SchemeIs("about", &isAbout);
+ if (NS_FAILED(rv) || (!isAbout)) {
+ return false;
+ }
+
+ bool isNonWeb;
+ rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DANGEROUS_TO_LOAD, &isNonWeb);
+ if (NS_FAILED(rv) || !isNonWeb) {
+ return false;
+ }
+
+ return true;
+}
+
void
nsIDocument::ResetToURI(nsIURI* aURI,
nsILoadGroup* aLoadGroup,
nsIPrincipal* aPrincipal)
{
NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
@@ -3186,16 +3217,22 @@ nsIDocument::SetPrincipal(nsIPrincipal *
bool isHTTPS;
if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
isHTTPS) {
mAllowDNSPrefetch = false;
}
}
mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
+ if (aNewPrincipal && PrincipalAllowsL10n(aNewPrincipal)) {
+ mDocumentL10n = new DocumentL10n(this);
+ } else {
+ mDocumentL10n = nullptr;
+ }
+
#ifdef DEBUG
// Validate that the docgroup is set correctly by calling its getter and
// triggering its sanity check.
//
// If we're setting the principal to null, we don't want to perform the check,
// as the document is entering an intermediate state where it does not have a
// principal. It will be given another real principal shortly which we will
// check. It's not unsafe to have a document which has a null principal in the
@@ -3379,16 +3416,103 @@ nsIDocument::GetAboutCapabilities(ErrorR
}
mAboutCapabilities = new AboutCapabilities(jsImplObj, sgo);
}
RefPtr<AboutCapabilities> aboutCapabilities =
static_cast<AboutCapabilities*>(mAboutCapabilities.get());
return aboutCapabilities.forget();
}
+class LoadLocalizationsRunnable : public mozilla::Runnable
+{
+public:
+ explicit LoadLocalizationsRunnable(nsIDocument* aDoc)
+ : mozilla::Runnable("LoadLocalizationsRunnable")
+ , mDoc(aDoc)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mDoc->mDocumentL10n->LoadLocalizations();
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> mDoc;
+};
+
+void
+nsIDocument::LoadDocumentL10n()
+{
+ if (mDocumentL10n) {
+ nsContentUtils::AddScriptRunner(new LoadLocalizationsRunnable(this));
+ }
+}
+
+bool
+nsDocument::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject)
+{
+ return PrincipalAllowsL10n(nsContentUtils::SubjectPrincipal(aCx));
+}
+
+class HandleLocalizationId : public mozilla::Runnable
+{
+public:
+ explicit HandleLocalizationId(nsIDocument* aDoc, const nsAString& aResourceId)
+ : mozilla::Runnable("HandleLocalizationId")
+ , mDoc(aDoc)
+ , mResourceId(aResourceId)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mDoc->mDocumentL10n->AddResourceId(mResourceId);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> mDoc;
+ nsString mResourceId;
+};
+
+void
+nsIDocument::LocalizationLinkAdded(Element* aLinkElement)
+{
+ if (!mDocumentL10n) {
+ return;
+ }
+
+ Element* parent = aLinkElement->GetParentElement();
+ if (!parent) {
+ return;
+ }
+
+ Element* head = GetHeadElement();
+ if (parent != head && !parent->NodeInfo()->Equals(nsGkAtoms::linkset, kNameSpaceID_XUL)) {
+ // TODO log a warning
+ return;
+ }
+
+ nsString href;
+ aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
+ nsContentUtils::AddScriptRunner(new HandleLocalizationId(this, href));
+}
+
+already_AddRefed<DocumentL10n>
+nsIDocument::GetL10n(ErrorResult& aRv)
+{
+ if (!mDocumentL10n) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<DocumentL10n> l10n(mDocumentL10n);
+ return l10n.forget();
+}
+
bool
nsDocument::IsElementAnimateEnabled(JSContext* aCx, JSObject* /*unused*/)
{
MOZ_ASSERT(NS_IsMainThread());
return nsContentUtils::IsSystemCaller(aCx) ||
nsContentUtils::AnimationsAPICoreEnabled() ||
nsContentUtils::AnimationsAPIElementAnimateEnabled();
@@ -5200,16 +5324,20 @@ nsIDocument::DispatchContentLoadedEvents
// Unpin references to preloaded images
mPreloadingImages.Clear();
// DOM manipulation after content loaded should not care if the element
// came from the preloader.
mPreloadedPreconnects.Clear();
+ if (mDocumentL10n) {
+ mDocumentL10n->NotifyLoaded();
+ }
+
if (mTiming) {
mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
}
// Dispatch observer notification to notify observers document is interactive.
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
nsIPrincipal* principal = NodePrincipal();
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -156,16 +156,17 @@ public:
nsISupports* aContainer,
nsIStreamListener **aDocListener,
bool aReset = true,
nsIContentSink* aContentSink = nullptr) override = 0;
virtual void StopDocumentLoad() override;
static bool CallerIsTrustedAboutPage(JSContext* aCx, JSObject* aObject);
+ static bool DocumentSupportsL10n(JSContext* aCx, JSObject* aObject);
static bool IsElementAnimateEnabled(JSContext* aCx, JSObject* aObject);
static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject);
static bool IsWebAnimationsEnabled(mozilla::dom::CallerType aCallerType);
virtual void EndUpdate(nsUpdateType aUpdateType) override;
virtual void BeginLoad() override;
virtual void EndLoad() override;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -113,16 +113,18 @@ class nsRange;
class nsSMILAnimationController;
class nsSVGElement;
class nsTextNode;
class nsUnblockOnloadEvent;
class nsWindowSizes;
class nsDOMCaretPosition;
class nsViewportInfo;
class nsIGlobalObject;
+class LoadLocalizationsRunnable;
+class HandleLocalizationId;
namespace mozilla {
class AbstractThread;
class CSSStyleSheet;
class Encoding;
class ErrorResult;
class EventStates;
class EventListenerManager;
@@ -145,16 +147,17 @@ class Attr;
class BoxObject;
class ClientInfo;
class ClientState;
class CDATASection;
class Comment;
struct CustomElementDefinition;
class DocGroup;
class DocumentFragment;
+class DocumentL10n;
class DocumentTimeline;
class DocumentType;
class DOMImplementation;
class DOMIntersectionObserver;
class DOMStringList;
class Element;
struct ElementCreationOptions;
class Event;
@@ -3191,16 +3194,19 @@ public:
void GetInputEncoding(nsAString& aInputEncoding) const;
already_AddRefed<mozilla::dom::Location> GetLocation() const;
void GetReferrer(nsAString& aReferrer) const;
void GetLastModified(nsAString& aLastModified) const;
void GetReadyState(nsAString& aReadyState) const;
already_AddRefed<mozilla::dom::AboutCapabilities> GetAboutCapabilities(
ErrorResult& aRv);
+ void LocalizationLinkAdded(Element* aLinkElement);
+ void LoadDocumentL10n();
+ already_AddRefed<mozilla::dom::DocumentL10n> GetL10n(ErrorResult& aRv);
void GetTitle(nsAString& aTitle);
void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
void GetDir(nsAString& aDirection) const;
void SetDir(const nsAString& aDirection);
nsIHTMLCollection* Images();
nsIHTMLCollection* Embeds();
nsIHTMLCollection* Plugins()
@@ -3868,16 +3874,19 @@ protected:
// focus has never occurred then mLastFocusTime.IsNull() will be true.
mozilla::TimeStamp mLastFocusTime;
mozilla::EventStates mDocumentState;
RefPtr<mozilla::dom::Promise> mReadyForIdle;
RefPtr<mozilla::dom::AboutCapabilities> mAboutCapabilities;
+ RefPtr<mozilla::dom::DocumentL10n> mDocumentL10n;
+ friend class LoadLocalizationsRunnable;
+ friend class HandleLocalizationId;
// True if BIDI is enabled.
bool mBidiEnabled : 1;
// True if a MathML element has ever been owned by this document.
bool mMathMLEnabled : 1;
// True if this document is the initial document for a window. This should
// basically be true only for documents that exist in newly-opened windows or
--- a/dom/html/HTMLBodyElement.cpp
+++ b/dom/html/HTMLBodyElement.cpp
@@ -294,16 +294,20 @@ nsresult
HTMLBodyElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
+
+ // We now have a body, load any localizations from the head.
+ OwnerDoc()->LoadDocumentL10n();
+
return mAttrsAndChildren.ForceMapped(this, OwnerDoc());
}
nsresult
HTMLBodyElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
nsIPrincipal* aSubjectPrincipal,
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -144,16 +144,23 @@ HTMLLinkElement::BindToTree(nsIDocument*
if (IsInComposedDoc()) {
TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
}
void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
nsContentUtils::AddScriptRunner(
NewRunnableMethod("dom::HTMLLinkElement::BindToTree", this, update));
+ if (aDocument) {
+ nsAutoString rel;
+ GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel);
+ if (rel.EqualsLiteral("localization"))
+ aDocument->LocalizationLinkAdded(this);
+ }
+
CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));
return rv;
}
void
HTMLLinkElement::LinkAdded()
{
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -497,16 +497,20 @@ partial interface Document {
readonly attribute FlashClassification documentFlashClassification;
};
// Allows about: pages to query aboutCapabilities
partial interface Document {
[Throws, Func="nsDocument::CallerIsTrustedAboutPage"] readonly attribute AboutCapabilities aboutCapabilities;
};
+partial interface Document {
+ [Throws, Func="nsDocument::DocumentSupportsL10n"] readonly attribute DocumentL10n l10n;
+};
+
Document implements XPathEvaluator;
Document implements GlobalEventHandlers;
Document implements DocumentAndElementEventHandlers;
Document implements TouchEventHandlers;
Document implements ParentNode;
Document implements OnErrorEventHandlerForNodes;
Document implements GeometryUtils;
Document implements FontFaceSource;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/DocumentL10n.webidl
@@ -0,0 +1,20 @@
+/* -*- 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/.
+ */
+
+dictionary L10nKey {
+ required DOMString id;
+
+ object? args = null;
+};
+
+[NoInterfaceObject]
+interface DocumentL10n {
+ [Throws] void setAttributes(Element aElement, DOMString aId, optional object aArgs);
+
+ [Throws] Promise<DOMString> formatValue(DOMString aId, optional object aArgs);
+
+ [Throws] Promise<sequence<DOMString>> formatValues(sequence<L10nKey> aKeys);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -460,16 +460,17 @@ WEBIDL_FILES = [
'DataTransferItemList.webidl',
'DecoderDoctorNotification.webidl',
'DedicatedWorkerGlobalScope.webidl',
'DelayNode.webidl',
'DeviceMotionEvent.webidl',
'Directory.webidl',
'Document.webidl',
'DocumentFragment.webidl',
+ 'DocumentL10n.webidl',
'DocumentOrShadowRoot.webidl',
'DocumentTimeline.webidl',
'DocumentType.webidl',
'DOMError.webidl',
'DOMException.webidl',
'DOMImplementation.webidl',
'DOMMatrix.webidl',
'DOMParser.webidl',
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -542,16 +542,17 @@ nsXMLContentSink::CloseElement(nsIConten
// properly (eg form state restoration).
if ((nodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
(nodeInfo->NameAtom() == nsGkAtoms::select ||
nodeInfo->NameAtom() == nsGkAtoms::textarea ||
nodeInfo->NameAtom() == nsGkAtoms::video ||
nodeInfo->NameAtom() == nsGkAtoms::audio ||
nodeInfo->NameAtom() == nsGkAtoms::object))
|| nodeInfo->NameAtom() == nsGkAtoms::title
+ || nodeInfo->Equals(nsGkAtoms::linkset, kNameSpaceID_XUL)
) {
aContent->DoneAddingChildren(HaveNotifiedForCurrentContent());
}
if (IsMonolithicContainer(nodeInfo)) {
mInMonolithicContainer--;
}
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -1447,16 +1447,18 @@ XULDocument::AddElementToDocumentPost(El
if (aElement == GetRootElement()) {
ResetDocumentDirection();
}
// We need to pay special attention to the keyset tag to set up a listener
if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
// Create our XUL key listener and hook it up.
nsXBLService::AttachGlobalKeyHandler(aElement);
+ } else if (aElement->NodeInfo()->Equals(nsGkAtoms::linkset, kNameSpaceID_XUL)) {
+ aElement->DoneAddingChildren(true);
}
return NS_OK;
}
nsresult
XULDocument::AddSubtreeToDocument(nsIContent* aContent)
{
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1269,16 +1269,24 @@ nsXULElement::DestroyContent()
frameLoader->Destroy();
}
slots->mFrameLoaderOrOpener = nullptr;
}
nsStyledElement::DestroyContent();
}
+void
+nsXULElement::DoneAddingChildren(bool aHaveNotified)
+{
+ // This is only called for the listset tag to let us initialize the
+ // localizations.
+ OwnerDoc()->LoadDocumentL10n();
+}
+
#ifdef DEBUG
void
nsXULElement::List(FILE* out, int32_t aIndent) const
{
nsCString prefix("XUL");
if (HasSlots()) {
prefix.Append('*');
}
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -366,16 +366,17 @@ public:
// nsIContent
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override;
virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override;
virtual void DestroyContent() override;
+ virtual void DoneAddingChildren(bool aHaveNotified) override;
#ifdef DEBUG
virtual void List(FILE* out, int32_t aIndent) const override;
virtual void DumpContent(FILE* out, int32_t aIndent,bool aDumpAll) const override
{
}
#endif
--- a/intl/l10n/DOMLocalization.jsm
+++ b/intl/l10n/DOMLocalization.jsm
@@ -519,16 +519,21 @@ class DOMLocalization extends Localizati
}
/**
* Translate all roots associated with this `DOMLocalization`.
*
* @returns {Promise}
*/
translateRoots() {
+ // Bail out early if there are no registered translations.
+ if (this.resourceIds.length == 0) {
+ return Promise.resolve();
+ }
+
const roots = Array.from(this.roots);
return Promise.all(
roots.map(root => this.translateFragment(root))
);
}
/**
* Pauses the `MutationObserver`.
new file mode 100644
--- /dev/null
+++ b/intl/l10n/DocumentL10n.cpp
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/dom/DocumentL10n.h"
+#include "mozilla/dom/DocumentL10nBinding.h"
+#include "nsQueryObject.h"
+#include "mozilla/dom/Promise.h"
+#include "nsISupports.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DocumentL10n, mDocument, mHelper)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DocumentL10n)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DocumentL10n)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentL10n)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+DocumentL10n::DocumentL10n(nsIDocument* aDocument)
+ : mDocument(aDocument)
+ , mHelper(nullptr)
+ , mLoading(true)
+{
+}
+
+DocumentL10n::~DocumentL10n()
+{
+ mHelper = nullptr;
+}
+
+bool
+DocumentL10n::EnsureHelper()
+{
+ if (mHelper) {
+ return true;
+ }
+
+ nsresult rv;
+ nsCOMPtr<mozIDocumentL10nHelper> helper = do_CreateInstance("@mozilla.org/intl/documentl10nhelper;1", &rv);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ rv = helper->Init(mDocument, mLoading);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ mHelper = helper;
+
+ if (mPendingResources.Length()) {
+ rv = PassPendingResources();
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+
+ return true;
+}
+
+nsresult
+DocumentL10n::PassPendingResources()
+{
+ nsresult rv = mHelper->AddResourceIds(reinterpret_cast<const char16_t**>(mPendingResources.Elements()), mPendingResources.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mPendingResources.Clear();
+ return NS_OK;
+}
+
+void
+DocumentL10n::NotifyLoaded()
+{
+ mLoading = false;
+}
+
+void
+DocumentL10n::LoadLocalizations()
+{
+ if (mPendingResources.Length()) {
+ EnsureHelper();
+ }
+}
+
+JSObject*
+DocumentL10n::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return DocumentL10nBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+DocumentL10n::AddResourceId(const nsAString& aResourceId)
+{
+ mPendingResources.AppendElement(ToNewUnicode(aResourceId));
+
+ if (mHelper) {
+ PassPendingResources();
+ }
+}
+
+void
+DocumentL10n::SetAttributes(JSContext* cx, Element& aElement, const nsAString& aId, const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv)
+{
+ if (!EnsureHelper()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ JS::RootedValue args(cx);
+
+ if (aArgs.WasPassed()) {
+ args = JS::ObjectValue(*aArgs.Value());
+ } else {
+ args = JS::UndefinedValue();
+ }
+
+ nsresult rv = mHelper->SetAttributes(&aElement, aId, args);
+
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+}
+
+already_AddRefed<Promise>
+DocumentL10n::FormatValue(JSContext* cx, const nsAString& aId, const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv)
+{
+ if (!EnsureHelper()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ JS::RootedValue args(cx);
+
+ if (aArgs.WasPassed()) {
+ args = JS::ObjectValue(*aArgs.Value());
+ } else {
+ args = JS::UndefinedValue();
+ }
+
+ nsCOMPtr<nsISupports> jsPromise;
+ nsresult rv = mHelper->FormatValue(aId, args, getter_AddRefs(jsPromise));
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+
+ RefPtr<Promise> promise = do_QueryObject(jsPromise);
+ if (!promise) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+
+ return promise.forget();
+}
+
+already_AddRefed<Promise>
+DocumentL10n::FormatValues(JSContext* cx, const Sequence<L10nKey>& aKeys, ErrorResult& aRv)
+{
+ if (!EnsureHelper()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ nsTArray<JS::HandleValue> jsKeys;
+ jsKeys.SetCapacity(aKeys.Length());
+
+ for (auto& key : aKeys) {
+ JS::RootedValue jsKey(cx);
+ if (!ToJSValue(cx, key, &jsKey)) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+
+ jsKeys.AppendElement(jsKey);
+ }
+
+ nsCOMPtr<nsISupports> jsPromise;
+ nsresult rv = mHelper->FormatValues(jsKeys.Elements(), jsKeys.Length(), getter_AddRefs(jsPromise));
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+
+ RefPtr<Promise> promise = do_QueryObject(jsPromise);
+ if (!promise) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+
+ return promise.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/intl/l10n/DocumentL10n.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_DocumentL10n_h
+#define mozilla_dom_DocumentL10n_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsIDocument.h"
+#include "mozIDocumentL10nHelper.h"
+
+namespace mozilla {
+namespace dom {
+
+class Element;
+class Promise;
+struct L10nKey;
+
+class DocumentL10n final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DocumentL10n)
+
+public:
+ explicit DocumentL10n(nsIDocument* aDocument);
+
+protected:
+ virtual ~DocumentL10n();
+
+ nsCOMPtr<nsIDocument> mDocument;
+ nsCOMPtr<mozIDocumentL10nHelper> mHelper;
+ nsTArray<UniquePtr<char16_t>> mPendingResources;
+ bool mLoading;
+
+ bool EnsureHelper();
+ nsresult PassPendingResources();
+
+public:
+ void NotifyLoaded();
+ void LoadLocalizations();
+
+ nsIDocument* GetParentObject() const { return mDocument; };
+
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ void AddResourceId(const nsAString& aResourceId);
+
+ void SetAttributes(JSContext* cx, Element& aElement, const nsAString& aId, const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv);
+
+ already_AddRefed<Promise> FormatValue(JSContext* cx, const nsAString& aId, const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv);
+
+ already_AddRefed<Promise> FormatValues(JSContext* cx, const Sequence<L10nKey>& aKeys, ErrorResult& aRv);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DocumentL10n_h
new file mode 100644
--- /dev/null
+++ b/intl/l10n/l10n.manifest
@@ -0,0 +1,2 @@
+component {29cc3895-8835-4c5b-b53a-0c0d1a458dee} mozDocumentL10nHelper.js
+contract @mozilla.org/intl/documentl10nhelper;1 {29cc3895-8835-4c5b-b53a-0c0d1a458dee}
--- a/intl/l10n/moz.build
+++ b/intl/l10n/moz.build
@@ -6,16 +6,39 @@
EXTRA_JS_MODULES += [
'DOMLocalization.jsm',
'L10nRegistry.jsm',
'Localization.jsm',
'MessageContext.jsm',
]
+XPIDL_SOURCES += [
+ 'mozIDocumentL10nHelper.idl',
+]
+
+XPIDL_MODULE = 'locale'
+
+EXTRA_COMPONENTS += [
+ 'l10n.manifest',
+ 'mozDocumentL10nHelper.js',
+]
+
+EXPORTS.mozilla.dom += [
+ 'DocumentL10n.h',
+]
+
+UNIFIED_SOURCES += [
+ 'DocumentL10n.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/dom/base',
+]
+
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
JAR_MANIFESTS += ['jar.mn']
SPHINX_TREES['l10n'] = 'docs'
new file mode 100644
--- /dev/null
+++ b/intl/l10n/mozDocumentL10nHelper.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const { DOMLocalization } = ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
+
+function mozDocumentL10nHelper() {
+ XPCOMUtils.defineLazyGetter(this, "localization", () => {
+ return new DOMLocalization(this.document.defaultView, []);
+ });
+}
+
+mozDocumentL10nHelper.prototype = {
+ document: null,
+
+ init(document, isLoading) {
+ this.document = document;
+
+ if (!isLoading) {
+ this.translateDocument();
+ } else {
+ let event = document.contentType === "application/vnd.mozilla.xul+xml" ?
+ "MozBeforeInitialXULLayout" : "readystate";
+
+ document.addEventListener(event, () => {
+ this.translateDocument();
+ }, { once: true });
+ }
+ },
+
+ translateDocument() {
+ this.localization.registerObservers();
+ this.localization.connectRoot(this.document.documentElement);
+ this.localization.translateRoots();
+ },
+
+ addResourceIds(resourceIds) {
+ this.localization.addResourceIds(resourceIds);
+ this.localization.ctxs.touchNext(2);
+ },
+
+ setAttributes(element, id, args) {
+ return this.localization.setAttributes(element, id, args);
+ },
+
+ formatValues(keys, length) {
+ return this.localization.formatValues(keys);
+ },
+
+ formatValue(id, args) {
+ return this.localization.formatValue(id, args);
+ },
+
+ QueryInterface: ChromeUtils.generateQI([Ci.mozIDocumentL10nHelper]),
+ classID: Components.ID("{29cc3895-8835-4c5b-b53a-0c0d1a458dee}"),
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([mozDocumentL10nHelper]);
new file mode 100644
--- /dev/null
+++ b/intl/l10n/mozIDocumentL10nHelper.idl
@@ -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"
+
+webidl Document;
+webidl Element;
+
+[scriptable, uuid(7c468500-541f-4fe0-98c9-92a53b63ec8d)]
+interface mozIDocumentL10nHelper : nsISupports
+{
+ /**
+ * Initializes the DOMLocalization object for the given document.
+ *
+ * @param document
+ * The document to monitor for l10n.
+ */
+ void init(in Document document, in boolean isLoading);
+
+ void addResourceIds([array, size_is(aLength)] in wstring aResourceIds, in unsigned long aLength);
+
+ void setAttributes(in Element aElement, in DOMString aId, [optional] in jsval aArgs);
+
+ nsISupports formatValue(in DOMString aId, [optional] in jsval aArgs);
+
+ nsISupports formatValues([array, size_is(aLength)] in jsval aKeys, in unsigned long aLength);
+};
--- a/intl/l10n/test/dom/test_domloc.xul
+++ b/intl/l10n/test/dom/test_domloc.xul
@@ -29,17 +29,17 @@ new-tab
yield mc;
}
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
- [],
+ ["dummy"],
generateMessages
);
async function foo() {
domLoc.connectRoot(document);
await domLoc.translateRoots();
is(document.getElementById('file-menu').getAttribute('label'), 'File');
--- a/intl/l10n/test/dom/test_domloc_mutations.html
+++ b/intl/l10n/test/dom/test_domloc_mutations.html
@@ -19,17 +19,17 @@
yield mc;
}
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
- [],
+ ["dummy"],
mockGenerateMessages
);
const h1 = document.querySelectorAll("h1")[0];
domLoc.connectRoot(document.body);
await domLoc.translateRoots();
--- a/intl/l10n/test/dom/test_domloc_translateRoots.html
+++ b/intl/l10n/test/dom/test_domloc_translateRoots.html
@@ -19,17 +19,17 @@
yield mc;
}
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
- [],
+ ["dummy"],
mockGenerateMessages
);
const frag1 = document.querySelectorAll("div")[0];
const frag2 = document.querySelectorAll("div")[1];
const h1 = document.querySelectorAll("h1")[0];
const h2 = document.querySelectorAll("h2")[0];
--- a/xpcom/ds/nsGkAtomList.h
+++ b/xpcom/ds/nsGkAtomList.h
@@ -626,16 +626,17 @@ GK_ATOM(leftmargin, "leftmargin")
GK_ATOM(leftpadding, "leftpadding")
GK_ATOM(legend, "legend")
GK_ATOM(length, "length")
GK_ATOM(letterValue, "letter-value")
GK_ATOM(level, "level")
GK_ATOM(li, "li")
GK_ATOM(line, "line")
GK_ATOM(link, "link")
+GK_ATOM(linkset, "linkset")
//GK_ATOM(list, "list") # "list" is present below
GK_ATOM(listbox, "listbox")
GK_ATOM(listboxbody, "listboxbody")
GK_ATOM(listcell, "listcell")
GK_ATOM(listcol, "listcol")
GK_ATOM(listcols, "listcols")
GK_ATOM(listener, "listener")
GK_ATOM(listhead, "listhead")