summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/loader
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/loader')
-rw-r--r--js/xpconnect/loader/ISO8601DateUtils.jsm144
-rw-r--r--js/xpconnect/loader/XPCOMUtils.jsm471
-rw-r--r--js/xpconnect/loader/moz.build28
-rw-r--r--js/xpconnect/loader/mozJSComponentLoader.cpp1437
-rw-r--r--js/xpconnect/loader/mozJSComponentLoader.h161
-rw-r--r--js/xpconnect/loader/mozJSLoaderUtils.cpp85
-rw-r--r--js/xpconnect/loader/mozJSLoaderUtils.h37
-rw-r--r--js/xpconnect/loader/mozJSSubScriptLoader.cpp929
-rw-r--r--js/xpconnect/loader/mozJSSubScriptLoader.h53
9 files changed, 3345 insertions, 0 deletions
diff --git a/js/xpconnect/loader/ISO8601DateUtils.jsm b/js/xpconnect/loader/ISO8601DateUtils.jsm
new file mode 100644
index 000000000..3eff78501
--- /dev/null
+++ b/js/xpconnect/loader/ISO8601DateUtils.jsm
@@ -0,0 +1,144 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+const HOURS_TO_MINUTES = 60;
+const MINUTES_TO_SECONDS = 60;
+const SECONDS_TO_MILLISECONDS = 1000;
+const MINUTES_TO_MILLISECONDS = MINUTES_TO_SECONDS * SECONDS_TO_MILLISECONDS;
+const HOURS_TO_MILLISECONDS = HOURS_TO_MINUTES * MINUTES_TO_MILLISECONDS;
+
+this.EXPORTED_SYMBOLS = ["ISO8601DateUtils"];
+
+debug("*** loading ISO8601DateUtils\n");
+
+this.ISO8601DateUtils = {
+
+ /**
+ * XXX Thunderbird's W3C-DTF function
+ *
+ * Converts a W3C-DTF (subset of ISO 8601) date string to a Javascript
+ * date object. W3C-DTF is described in this note:
+ * http://www.w3.org/TR/NOTE-datetime IETF is obtained via the Date
+ * object's toUTCString() method. The object's toString() method is
+ * insufficient because it spells out timezones on Win32
+ * (f.e. "Pacific Standard Time" instead of "PST"), which Mail doesn't
+ * grok. For info, see
+ * http://lxr.mozilla.org/mozilla/source/js/src/jsdate.c#1526.
+ */
+ parse: function ISO8601_parse(aDateString) {
+ var dateString = aDateString;
+ if (!dateString.match('-')) {
+ // Workaround for server sending
+ // dates such as: 20030530T11:18:50-08:00
+ // instead of: 2003-05-30T11:18:50-08:00
+ var year = dateString.slice(0, 4);
+ var month = dateString.slice(4, 6);
+ var rest = dateString.slice(6, dateString.length);
+ dateString = year + "-" + month + "-" + rest;
+ }
+
+ var parts = dateString.match(/(\d{4})(-(\d{2,3}))?(-(\d{2}))?(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|([+-])(\d{2}):(\d{2}))?)?/);
+
+ // Here's an example of a W3C-DTF date string and what .match returns for it.
+ //
+ // date: 2003-05-30T11:18:50.345-08:00
+ // date.match returns array values:
+ //
+ // 0: 2003-05-30T11:18:50-08:00,
+ // 1: 2003,
+ // 2: -05,
+ // 3: 05,
+ // 4: -30,
+ // 5: 30,
+ // 6: T11:18:50-08:00,
+ // 7: 11,
+ // 8: 18,
+ // 9: :50,
+ // 10: 50,
+ // 11: .345,
+ // 12: 345,
+ // 13: -08:00,
+ // 14: -,
+ // 15: 08,
+ // 16: 00
+
+ // Create a Date object from the date parts. Note that the Date
+ // object apparently can't deal with empty string parameters in lieu
+ // of numbers, so optional values (like hours, minutes, seconds, and
+ // milliseconds) must be forced to be numbers.
+ var date = new Date(parts[1], parts[3] - 1, parts[5], parts[7] || 0,
+ parts[8] || 0, parts[10] || 0, parts[12] || 0);
+
+ // We now have a value that the Date object thinks is in the local
+ // timezone but which actually represents the date/time in the
+ // remote timezone (f.e. the value was "10:00 EST", and we have
+ // converted it to "10:00 PST" instead of "07:00 PST"). We need to
+ // correct that. To do so, we're going to add the offset between
+ // the remote timezone and UTC (to convert the value to UTC), then
+ // add the offset between UTC and the local timezone //(to convert
+ // the value to the local timezone).
+
+ // Ironically, W3C-DTF gives us the offset between UTC and the
+ // remote timezone rather than the other way around, while the
+ // getTimezoneOffset() method of a Date object gives us the offset
+ // between the local timezone and UTC rather than the other way
+ // around. Both of these are the additive inverse (i.e. -x for x)
+ // of what we want, so we have to invert them to use them by
+ // multipying by -1 (f.e. if "the offset between UTC and the remote
+ // timezone" is -5 hours, then "the offset between the remote
+ // timezone and UTC" is -5*-1 = 5 hours).
+
+ // Note that if the timezone portion of the date/time string is
+ // absent (which violates W3C-DTF, although ISO 8601 allows it), we
+ // assume the value to be in UTC.
+
+ // The offset between the remote timezone and UTC in milliseconds.
+ var remoteToUTCOffset = 0;
+ if (parts[13] && parts[13] != "Z") {
+ var direction = (parts[14] == "+" ? 1 : -1);
+ if (parts[15])
+ remoteToUTCOffset += direction * parts[15] * HOURS_TO_MILLISECONDS;
+ if (parts[16])
+ remoteToUTCOffset += direction * parts[16] * MINUTES_TO_MILLISECONDS;
+ }
+ remoteToUTCOffset = remoteToUTCOffset * -1; // invert it
+
+ // The offset between UTC and the local timezone in milliseconds.
+ var UTCToLocalOffset = date.getTimezoneOffset() * MINUTES_TO_MILLISECONDS;
+ UTCToLocalOffset = UTCToLocalOffset * -1; // invert it
+ date.setTime(date.getTime() + remoteToUTCOffset + UTCToLocalOffset);
+
+ return date;
+ },
+
+ create: function ISO8601_create(aDate) {
+ function zeropad (s, l) {
+ s = s.toString(); // force it to a string
+ while (s.length < l) {
+ s = '0' + s;
+ }
+ return s;
+ }
+
+ var myDate;
+ // if d is a number, turn it into a date
+ if (typeof aDate == 'number') {
+ myDate = new Date()
+ myDate.setTime(aDate);
+ } else {
+ myDate = aDate;
+ }
+
+ // YYYY-MM-DDThh:mm:ssZ
+ var result = zeropad(myDate.getUTCFullYear (), 4) +
+ zeropad(myDate.getUTCMonth () + 1, 2) +
+ zeropad(myDate.getUTCDate (), 2) + 'T' +
+ zeropad(myDate.getUTCHours (), 2) + ':' +
+ zeropad(myDate.getUTCMinutes (), 2) + ':' +
+ zeropad(myDate.getUTCSeconds (), 2) + 'Z';
+
+ return result;
+ }
+}
diff --git a/js/xpconnect/loader/XPCOMUtils.jsm b/js/xpconnect/loader/XPCOMUtils.jsm
new file mode 100644
index 000000000..eb35258de
--- /dev/null
+++ b/js/xpconnect/loader/XPCOMUtils.jsm
@@ -0,0 +1,471 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et filetype=javascript
+ * 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/. */
+
+/**
+ * Utilities for JavaScript components loaded by the JS component
+ * loader.
+ *
+ * Import into a JS component using
+ * 'Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");'
+ *
+ * Exposing a JS 'class' as a component using these utility methods consists
+ * of several steps:
+ * 0. Import XPCOMUtils, as described above.
+ * 1. Declare the 'class' (or multiple classes) implementing the component(s):
+ * function MyComponent() {
+ * // constructor
+ * }
+ * MyComponent.prototype = {
+ * // properties required for XPCOM registration:
+ * classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"),
+ *
+ * // [optional] custom factory (an object implementing nsIFactory). If not
+ * // provided, the default factory is used, which returns
+ * // |(new MyComponent()).QueryInterface(iid)| in its createInstance().
+ * _xpcom_factory: { ... },
+ *
+ * // QueryInterface implementation, e.g. using the generateQI helper
+ * QueryInterface: XPCOMUtils.generateQI(
+ * [Components.interfaces.nsIObserver,
+ * Components.interfaces.nsIMyInterface,
+ * "nsIFoo",
+ * "nsIBar" ]),
+ *
+ * // [optional] classInfo implementation, e.g. using the generateCI helper.
+ * // Will be automatically returned from QueryInterface if that was
+ * // generated with the generateQI helper.
+ * classInfo: XPCOMUtils.generateCI(
+ * {classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"),
+ * contractID: "@example.com/xxx;1",
+ * classDescription: "unique text description",
+ * interfaces: [Components.interfaces.nsIObserver,
+ * Components.interfaces.nsIMyInterface,
+ * "nsIFoo",
+ * "nsIBar"],
+ * flags: Ci.nsIClassInfo.SINGLETON}),
+ *
+ * // The following properties were used prior to Mozilla 2, but are no
+ * // longer supported. They may still be included for compatibility with
+ * // prior versions of XPCOMUtils. In Mozilla 2, this information is
+ * // included in the .manifest file which registers this JS component.
+ * classDescription: "unique text description",
+ * contractID: "@example.com/xxx;1",
+ *
+ * // [optional] an array of categories to register this component in.
+ * _xpcom_categories: [{
+ * // Each object in the array specifies the parameters to pass to
+ * // nsICategoryManager.addCategoryEntry(). 'true' is passed for
+ * // both aPersist and aReplace params.
+ * category: "some-category",
+ * // optional, defaults to the object's classDescription
+ * entry: "entry name",
+ * // optional, defaults to the object's contractID (unless
+ * // 'service' is specified)
+ * value: "...",
+ * // optional, defaults to false. When set to true, and only if 'value'
+ * // is not specified, the concatenation of the string "service," and the
+ * // object's contractID is passed as aValue parameter of addCategoryEntry.
+ * service: true,
+ * // optional, it can be an array of applications' IDs in the form:
+ * // [ "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", ... ]
+ * // If defined the component will be registered in this category only for
+ * // the provided applications.
+ * apps: [...]
+ * }],
+ *
+ * // ...component implementation...
+ * };
+ *
+ * 2. Create an array of component constructors (like the one
+ * created in step 1):
+ * var components = [MyComponent];
+ *
+ * 3. Define the NSGetFactory entry point:
+ * this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
+ */
+
+
+this.EXPORTED_SYMBOLS = [ "XPCOMUtils" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+this.XPCOMUtils = {
+ /**
+ * Generate a QueryInterface implementation. The returned function must be
+ * assigned to the 'QueryInterface' property of a JS object. When invoked on
+ * that object, it checks if the given iid is listed in the |interfaces|
+ * param, and if it is, returns |this| (the object it was called on).
+ * If the JS object has a classInfo property it'll be returned for the
+ * nsIClassInfo IID, generateCI can be used to generate the classInfo
+ * property.
+ */
+ generateQI: function XPCU_generateQI(interfaces) {
+ /* Note that Ci[Ci.x] == Ci.x for all x */
+ let a = [];
+ if (interfaces) {
+ for (let i = 0; i < interfaces.length; i++) {
+ let iface = interfaces[i];
+ if (Ci[iface]) {
+ a.push(Ci[iface].name);
+ }
+ }
+ }
+ return makeQI(a);
+ },
+
+ /**
+ * Generate a ClassInfo implementation for a component. The returned object
+ * must be assigned to the 'classInfo' property of a JS object. The first and
+ * only argument should be an object that contains a number of optional
+ * properties: "interfaces", "contractID", "classDescription", "classID" and
+ * "flags". The values of the properties will be returned as the values of the
+ * various properties of the nsIClassInfo implementation.
+ */
+ generateCI: function XPCU_generateCI(classInfo)
+ {
+ if (QueryInterface in classInfo)
+ throw Error("In generateCI, don't use a component for generating classInfo");
+ /* Note that Ci[Ci.x] == Ci.x for all x */
+ let _interfaces = [];
+ for (let i = 0; i < classInfo.interfaces.length; i++) {
+ let iface = classInfo.interfaces[i];
+ if (Ci[iface]) {
+ _interfaces.push(Ci[iface]);
+ }
+ }
+ return {
+ getInterfaces: function XPCU_getInterfaces(countRef) {
+ countRef.value = _interfaces.length;
+ return _interfaces;
+ },
+ getScriptableHelper: function XPCU_getScriptableHelper() {
+ return null;
+ },
+ contractID: classInfo.contractID,
+ classDescription: classInfo.classDescription,
+ classID: classInfo.classID,
+ flags: classInfo.flags,
+ QueryInterface: this.generateQI([Ci.nsIClassInfo])
+ };
+ },
+
+ /**
+ * Generate a NSGetFactory function given an array of components.
+ */
+ generateNSGetFactory: function XPCU_generateNSGetFactory(componentsArray) {
+ let classes = {};
+ for (let i = 0; i < componentsArray.length; i++) {
+ let component = componentsArray[i];
+ if (!(component.prototype.classID instanceof Components.ID))
+ throw Error("In generateNSGetFactory, classID missing or incorrect for component " + component);
+
+ classes[component.prototype.classID] = this._getFactory(component);
+ }
+ return function NSGetFactory(cid) {
+ let cidstring = cid.toString();
+ if (cidstring in classes)
+ return classes[cidstring];
+ throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+ },
+
+ /**
+ * Defines a getter on a specified object that will be created upon first use.
+ *
+ * @param aObject
+ * The object to define the lazy getter on.
+ * @param aName
+ * The name of the getter to define on aObject.
+ * @param aLambda
+ * A function that returns what the getter should return. This will
+ * only ever be called once.
+ */
+ defineLazyGetter: function XPCU_defineLazyGetter(aObject, aName, aLambda)
+ {
+ Object.defineProperty(aObject, aName, {
+ get: function () {
+ // Redefine this accessor property as a data property.
+ // Delete it first, to rule out "too much recursion" in case aObject is
+ // a proxy whose defineProperty handler might unwittingly trigger this
+ // getter again.
+ delete aObject[aName];
+ let value = aLambda.apply(aObject);
+ Object.defineProperty(aObject, aName, {
+ value,
+ writable: true,
+ configurable: true,
+ enumerable: true
+ });
+ return value;
+ },
+ configurable: true,
+ enumerable: true
+ });
+ },
+
+ /**
+ * Defines a getter on a specified object for a service. The service will not
+ * be obtained until first use.
+ *
+ * @param aObject
+ * The object to define the lazy getter on.
+ * @param aName
+ * The name of the getter to define on aObject for the service.
+ * @param aContract
+ * The contract used to obtain the service.
+ * @param aInterfaceName
+ * The name of the interface to query the service to.
+ */
+ defineLazyServiceGetter: function XPCU_defineLazyServiceGetter(aObject, aName,
+ aContract,
+ aInterfaceName)
+ {
+ this.defineLazyGetter(aObject, aName, function XPCU_serviceLambda() {
+ return Cc[aContract].getService(Ci[aInterfaceName]);
+ });
+ },
+
+ /**
+ * Defines a getter on a specified object for a module. The module will not
+ * be imported until first use. The getter allows to execute setup and
+ * teardown code (e.g. to register/unregister to services) and accepts
+ * a proxy object which acts on behalf of the module until it is imported.
+ *
+ * @param aObject
+ * The object to define the lazy getter on.
+ * @param aName
+ * The name of the getter to define on aObject for the module.
+ * @param aResource
+ * The URL used to obtain the module.
+ * @param aSymbol
+ * The name of the symbol exported by the module.
+ * This parameter is optional and defaults to aName.
+ * @param aPreLambda
+ * A function that is executed when the proxy is set up.
+ * This will only ever be called once.
+ * @param aPostLambda
+ * A function that is executed when the module has been imported to
+ * run optional teardown procedures on the proxy object.
+ * This will only ever be called once.
+ * @param aProxy
+ * An object which acts on behalf of the module to be imported until
+ * the module has been imported.
+ */
+ defineLazyModuleGetter: function XPCU_defineLazyModuleGetter(
+ aObject, aName, aResource, aSymbol,
+ aPreLambda, aPostLambda, aProxy)
+ {
+ let proxy = aProxy || {};
+
+ if (typeof(aPreLambda) === "function") {
+ aPreLambda.apply(proxy);
+ }
+
+ this.defineLazyGetter(aObject, aName, function XPCU_moduleLambda() {
+ var temp = {};
+ try {
+ Cu.import(aResource, temp);
+
+ if (typeof(aPostLambda) === "function") {
+ aPostLambda.apply(proxy);
+ }
+ } catch (ex) {
+ Cu.reportError("Failed to load module " + aResource + ".");
+ throw ex;
+ }
+ return temp[aSymbol || aName];
+ });
+ },
+
+ /**
+ * Defines a getter on a specified object for preference value. The
+ * preference is read the first time that the property is accessed,
+ * and is thereafter kept up-to-date using a preference observer.
+ *
+ * @param aObject
+ * The object to define the lazy getter on.
+ * @param aName
+ * The name of the getter property to define on aObject.
+ * @param aPreference
+ * The name of the preference to read.
+ * @param aDefaultValue
+ * The default value to use, if the preference is not defined.
+ */
+ defineLazyPreferenceGetter: function XPCU_defineLazyPreferenceGetter(
+ aObject, aName, aPreference, aDefaultValue = null)
+ {
+ // Note: We need to keep a reference to this observer alive as long
+ // as aObject is alive. This means that all of our getters need to
+ // explicitly close over the variable that holds the object, and we
+ // cannot define a value in place of a getter after we read the
+ // preference.
+ let observer = {
+ QueryInterface: this.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
+
+ value: undefined,
+
+ observe(subject, topic, data) {
+ if (data == aPreference) {
+ this.value = undefined;
+ }
+ },
+ }
+
+ let defineGetter = get => {
+ Object.defineProperty(aObject, aName, {
+ configurable: true,
+ enumerable: true,
+ get,
+ });
+ };
+
+ function lazyGetter() {
+ if (observer.value === undefined) {
+ observer.value = Preferences.get(aPreference, aDefaultValue);
+ }
+ return observer.value;
+ }
+
+ defineGetter(() => {
+ Services.prefs.addObserver(aPreference, observer, true);
+
+ defineGetter(lazyGetter);
+ return lazyGetter();
+ });
+ },
+
+ /**
+ * Helper which iterates over a nsISimpleEnumerator.
+ * @param e The nsISimpleEnumerator to iterate over.
+ * @param i The expected interface for each element.
+ */
+ IterSimpleEnumerator: function* XPCU_IterSimpleEnumerator(e, i)
+ {
+ while (e.hasMoreElements())
+ yield e.getNext().QueryInterface(i);
+ },
+
+ /**
+ * Helper which iterates over a string enumerator.
+ * @param e The string enumerator (nsIUTF8StringEnumerator or
+ * nsIStringEnumerator) over which to iterate.
+ */
+ IterStringEnumerator: function* XPCU_IterStringEnumerator(e)
+ {
+ while (e.hasMore())
+ yield e.getNext();
+ },
+
+ /**
+ * Helper which iterates over the entries in a category.
+ * @param aCategory The name of the category over which to iterate.
+ */
+ enumerateCategoryEntries: function* XPCOMUtils_enumerateCategoryEntries(aCategory)
+ {
+ let category = this.categoryManager.enumerateCategory(aCategory);
+ for (let entry of this.IterSimpleEnumerator(category, Ci.nsISupportsCString)) {
+ yield [entry.data, this.categoryManager.getCategoryEntry(aCategory, entry.data)];
+ }
+ },
+
+ /**
+ * Returns an nsIFactory for |component|.
+ */
+ _getFactory: function XPCOMUtils__getFactory(component) {
+ var factory = component.prototype._xpcom_factory;
+ if (!factory) {
+ factory = {
+ createInstance: function(outer, iid) {
+ if (outer)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return (new component()).QueryInterface(iid);
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+ }
+ }
+ return factory;
+ },
+
+ /**
+ * Allows you to fake a relative import. Expects the global object from the
+ * module that's calling us, and the relative filename that we wish to import.
+ */
+ importRelative: function XPCOMUtils__importRelative(that, path, scope) {
+ if (!("__URI__" in that))
+ throw Error("importRelative may only be used from a JSM, and its first argument "+
+ "must be that JSM's global object (hint: use this)");
+ let uri = that.__URI__;
+ let i = uri.lastIndexOf("/");
+ Components.utils.import(uri.substring(0, i+1) + path, scope || that);
+ },
+
+ /**
+ * generates a singleton nsIFactory implementation that can be used as
+ * the _xpcom_factory of the component.
+ * @param aServiceConstructor
+ * Constructor function of the component.
+ */
+ generateSingletonFactory:
+ function XPCOMUtils_generateSingletonFactory(aServiceConstructor) {
+ return {
+ _instance: null,
+ createInstance: function XPCU_SF_createInstance(aOuter, aIID) {
+ if (aOuter !== null) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ if (this._instance === null) {
+ this._instance = new aServiceConstructor();
+ }
+ return this._instance.QueryInterface(aIID);
+ },
+ lockFactory: function XPCU_SF_lockFactory(aDoLock) {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+ };
+ },
+
+ /**
+ * Defines a non-writable property on an object.
+ */
+ defineConstant: function XPCOMUtils__defineConstant(aObj, aName, aValue) {
+ Object.defineProperty(aObj, aName, {
+ value: aValue,
+ enumerable: true,
+ writable: false
+ });
+ },
+};
+
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+ "resource://gre/modules/Preferences.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+ "resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(XPCOMUtils, "categoryManager",
+ "@mozilla.org/categorymanager;1",
+ "nsICategoryManager");
+
+/**
+ * Helper for XPCOMUtils.generateQI to avoid leaks - see bug 381651#c1
+ */
+function makeQI(interfaceNames) {
+ return function XPCOMUtils_QueryInterface(iid) {
+ if (iid.equals(Ci.nsISupports))
+ return this;
+ if (iid.equals(Ci.nsIClassInfo) && "classInfo" in this)
+ return this.classInfo;
+ for (let i = 0; i < interfaceNames.length; i++) {
+ if (Ci[interfaceNames[i]].equals(iid))
+ return this;
+ }
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ };
+}
diff --git a/js/xpconnect/loader/moz.build b/js/xpconnect/loader/moz.build
new file mode 100644
index 000000000..3dc03d6db
--- /dev/null
+++ b/js/xpconnect/loader/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# These files cannot be built in unified mode because they rely on plarena.h
+SOURCES += [
+ 'mozJSComponentLoader.cpp',
+ 'mozJSLoaderUtils.cpp',
+ 'mozJSSubScriptLoader.cpp',
+]
+
+EXTRA_JS_MODULES += [
+ 'ISO8601DateUtils.jsm',
+ 'XPCOMUtils.jsm',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../src',
+ '../wrappers',
+ '/dom/base',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-shadow']
diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp
new file mode 100644
index 000000000..95c214867
--- /dev/null
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -0,0 +1,1437 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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/Attributes.h"
+
+#include <cstdarg>
+
+#include "mozilla/Logging.h"
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#include "jsapi.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIComponentManager.h"
+#include "mozilla/Module.h"
+#include "nsIFile.h"
+#include "mozJSComponentLoader.h"
+#include "mozJSLoaderUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIObserverService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIFileURL.h"
+#include "nsIJARURI.h"
+#include "nsNetUtil.h"
+#include "jsprf.h"
+#include "nsJSPrincipals.h"
+#include "nsJSUtils.h"
+#include "xpcprivate.h"
+#include "xpcpublic.h"
+#include "nsContentUtils.h"
+#include "nsXULAppAPI.h"
+#include "WrapperFactory.h"
+
+#include "mozilla/AddonPathService.h"
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla;
+using namespace mozilla::scache;
+using namespace xpc;
+using namespace JS;
+
+// This JSClass exists to trick silly code that expects toString()ing the
+// global in a component scope to return something with "BackstagePass" in it
+// to continue working.
+static const JSClass kFakeBackstagePassJSClass = { "FakeBackstagePass" };
+
+static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
+static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
+static const char kJSCachePrefix[] = "jsloader";
+
+#define HAVE_PR_MEMMAP
+
+/**
+ * Buffer sizes for serialization and deserialization of scripts.
+ * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
+ */
+#define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
+#define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
+
+// MOZ_LOG=JSComponentLoader:5
+static LazyLogModule gJSCLLog("JSComponentLoader");
+
+#define LOG(args) MOZ_LOG(gJSCLLog, mozilla::LogLevel::Debug, args)
+
+// Components.utils.import error messages
+#define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
+#define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
+#define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
+#define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS."
+#define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
+#define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
+#define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
+
+static bool
+Dump(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (args.length() == 0)
+ return true;
+
+ RootedString str(cx, JS::ToString(cx, args[0]));
+ if (!str)
+ return false;
+
+ JSAutoByteString utf8str;
+ if (!utf8str.encodeUtf8(cx, str))
+ return false;
+
+#ifdef ANDROID
+ __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
+#endif
+#ifdef XP_WIN
+ if (IsDebuggerPresent()) {
+ nsAutoJSString wstr;
+ if (!wstr.init(cx, str))
+ return false;
+ OutputDebugStringW(wstr.get());
+ }
+#endif
+ fputs(utf8str.ptr(), stdout);
+ fflush(stdout);
+ return true;
+}
+
+static bool
+Debug(JSContext* cx, unsigned argc, Value* vp)
+{
+#ifdef DEBUG
+ return Dump(cx, argc, vp);
+#else
+ return true;
+#endif
+}
+
+static const JSFunctionSpec gGlobalFun[] = {
+ JS_FS("dump", Dump, 1,0),
+ JS_FS("debug", Debug, 1,0),
+ JS_FS("atob", Atob, 1,0),
+ JS_FS("btoa", Btoa, 1,0),
+ JS_FS_END
+};
+
+class MOZ_STACK_CLASS JSCLContextHelper
+{
+public:
+ explicit JSCLContextHelper(JSContext* aCx);
+ ~JSCLContextHelper();
+
+ void reportErrorAfterPop(char* buf);
+
+private:
+ JSContext* mContext;
+ char* mBuf;
+
+ // prevent copying and assignment
+ JSCLContextHelper(const JSCLContextHelper&) = delete;
+ const JSCLContextHelper& operator=(const JSCLContextHelper&) = delete;
+};
+
+static nsresult
+MOZ_FORMAT_PRINTF(2, 3)
+ReportOnCallerUTF8(JSContext* callerContext,
+ const char* format, ...) {
+ if (!callerContext) {
+ return NS_ERROR_FAILURE;
+ }
+
+ va_list ap;
+ va_start(ap, format);
+
+ char* buf = JS_vsmprintf(format, ap);
+ if (!buf) {
+ va_end(ap);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ JS_ReportErrorUTF8(callerContext, "%s", buf);
+ JS_smprintf_free(buf);
+
+ va_end(ap);
+ return NS_OK;
+}
+
+static nsresult
+MOZ_FORMAT_PRINTF(2, 3)
+ReportOnCallerUTF8(JSCLContextHelper& helper,
+ const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+
+ char* buf = JS_vsmprintf(format, ap);
+ if (!buf) {
+ va_end(ap);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ helper.reportErrorAfterPop(buf);
+ va_end(ap);
+ return NS_OK;
+}
+
+mozJSComponentLoader::mozJSComponentLoader()
+ : mModules(16),
+ mImports(16),
+ mInProgressImports(16),
+ mInitialized(false),
+ mReuseLoaderGlobal(false)
+{
+ MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
+
+ sSelf = this;
+}
+
+#define ENSURE_DEP(name) { nsresult rv = Ensure##name(); NS_ENSURE_SUCCESS(rv, rv); }
+#define ENSURE_DEPS(...) MOZ_FOR_EACH(ENSURE_DEP, (), (__VA_ARGS__));
+#define BEGIN_ENSURE(self, ...) { \
+ if (m##self) \
+ return NS_OK; \
+ ENSURE_DEPS(__VA_ARGS__); \
+}
+
+class MOZ_STACK_CLASS ComponentLoaderInfo {
+ public:
+ explicit ComponentLoaderInfo(const nsACString& aLocation) : mLocation(aLocation) {}
+
+ nsIIOService* IOService() { MOZ_ASSERT(mIOService); return mIOService; }
+ nsresult EnsureIOService() {
+ if (mIOService)
+ return NS_OK;
+ nsresult rv;
+ mIOService = do_GetIOService(&rv);
+ return rv;
+ }
+
+ nsIURI* URI() { MOZ_ASSERT(mURI); return mURI; }
+ nsresult EnsureURI() {
+ BEGIN_ENSURE(URI, IOService);
+ return mIOService->NewURI(mLocation, nullptr, nullptr, getter_AddRefs(mURI));
+ }
+
+ nsIChannel* ScriptChannel() { MOZ_ASSERT(mScriptChannel); return mScriptChannel; }
+ nsresult EnsureScriptChannel() {
+ BEGIN_ENSURE(ScriptChannel, IOService, URI);
+ return NS_NewChannel(getter_AddRefs(mScriptChannel),
+ mURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_SCRIPT,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL,
+ mIOService);
+ }
+
+ nsIURI* ResolvedURI() { MOZ_ASSERT(mResolvedURI); return mResolvedURI; }
+ nsresult EnsureResolvedURI() {
+ BEGIN_ENSURE(ResolvedURI, ScriptChannel);
+ return mScriptChannel->GetURI(getter_AddRefs(mResolvedURI));
+ }
+
+ nsAutoCString& Key() { return *mKey; }
+ nsresult EnsureKey() {
+ ENSURE_DEPS(ResolvedURI);
+ mKey.emplace();
+ return mResolvedURI->GetSpec(*mKey);
+ }
+
+ MOZ_MUST_USE nsresult GetLocation(nsCString& aLocation) {
+ nsresult rv = EnsureURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mURI->GetSpec(aLocation);
+ }
+
+ private:
+ const nsACString& mLocation;
+ nsCOMPtr<nsIIOService> mIOService;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIChannel> mScriptChannel;
+ nsCOMPtr<nsIURI> mResolvedURI;
+ Maybe<nsAutoCString> mKey; // This is safe because we're MOZ_STACK_CLASS
+};
+
+#undef BEGIN_ENSURE
+#undef ENSURE_DEPS
+#undef ENSURE_DEP
+
+mozJSComponentLoader::~mozJSComponentLoader()
+{
+ if (mInitialized) {
+ NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
+ UnloadModules();
+ }
+
+ sSelf = nullptr;
+}
+
+mozJSComponentLoader*
+mozJSComponentLoader::sSelf;
+
+NS_IMPL_ISUPPORTS(mozJSComponentLoader,
+ mozilla::ModuleLoader,
+ xpcIJSModuleLoader,
+ nsIObserver)
+
+nsresult
+mozJSComponentLoader::ReallyInit()
+{
+ nsresult rv;
+
+ mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal");
+
+ // XXXkhuey B2G child processes have some sort of preferences race that
+ // results in getting the wrong value.
+ // But we don't want that on Firefox Mulet as it break most Firefox JSMs...
+ // Also disable on debug builds to break js components that rely on this.
+#if defined(MOZ_B2G) && !defined(MOZ_MULET) && !defined(DEBUG)
+ mReuseLoaderGlobal = true;
+#endif
+
+ nsCOMPtr<nsIScriptSecurityManager> secman =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ if (!secman)
+ return NS_ERROR_FAILURE;
+
+ rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
+ if (NS_FAILED(rv) || !mSystemPrincipal)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIObserverService> obsSvc =
+ do_GetService(kObserverServiceContractID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInitialized = true;
+
+ return NS_OK;
+}
+
+// For terrible compatibility reasons, we need to consider both the global
+// lexical environment and the global of modules when searching for exported
+// symbols.
+static JSObject*
+ResolveModuleObjectProperty(JSContext* aCx, HandleObject aModObj, const char* name)
+{
+ if (JS_HasExtensibleLexicalEnvironment(aModObj)) {
+ RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj));
+ bool found;
+ if (!JS_HasOwnProperty(aCx, lexical, name, &found)) {
+ return nullptr;
+ }
+ if (found) {
+ return lexical;
+ }
+ }
+ return aModObj;
+}
+
+const mozilla::Module*
+mozJSComponentLoader::LoadModule(FileLocation& aFile)
+{
+ if (!NS_IsMainThread()) {
+ MOZ_ASSERT(false, "Don't use JS components off the main thread");
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
+
+ nsCString spec;
+ aFile.GetURIString(spec);
+ ComponentLoaderInfo info(spec);
+ nsresult rv = info.EnsureURI();
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ if (!mInitialized) {
+ rv = ReallyInit();
+ if (NS_FAILED(rv))
+ return nullptr;
+ }
+
+ ModuleEntry* mod;
+ if (mModules.Get(spec, &mod))
+ return mod;
+
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ nsAutoPtr<ModuleEntry> entry(new ModuleEntry(RootingContext::get(cx)));
+ RootedValue dummy(cx);
+ rv = ObjectForLocation(info, file, &entry->obj, &entry->thisObjectKey,
+ &entry->location, false, &dummy);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
+ &rv);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsCOMPtr<nsIComponentManager> cm;
+ rv = NS_GetComponentManager(getter_AddRefs(cm));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ JSAutoCompartment ac(cx, entry->obj);
+ RootedObject entryObj(cx, entry->obj);
+
+ RootedObject NSGetFactoryHolder(cx, ResolveModuleObjectProperty(cx, entryObj, "NSGetFactory"));
+ RootedValue NSGetFactory_val(cx);
+ if (!NSGetFactoryHolder ||
+ !JS_GetProperty(cx, NSGetFactoryHolder, "NSGetFactory", &NSGetFactory_val) ||
+ NSGetFactory_val.isUndefined())
+ {
+ return nullptr;
+ }
+
+ if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
+ /*
+ * spec's encoding is ASCII unless it's zip file, otherwise it's
+ * random encoding. Latin1 variant is safe for random encoding.
+ */
+ JS_ReportErrorLatin1(cx, "%s has NSGetFactory property that is not a function",
+ spec.get());
+ return nullptr;
+ }
+
+ RootedObject jsGetFactoryObj(cx);
+ if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
+ !jsGetFactoryObj) {
+ /* XXX report error properly */
+ return nullptr;
+ }
+
+ rv = xpc->WrapJS(cx, jsGetFactoryObj,
+ NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
+ if (NS_FAILED(rv)) {
+ /* XXX report error properly */
+#ifdef DEBUG
+ fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
+#endif
+ return nullptr;
+ }
+
+ // Cache this module for later
+ mModules.Put(spec, entry);
+
+ // Set the location information for the new global, so that tools like
+ // about:memory may use that information
+ if (!mReuseLoaderGlobal) {
+ xpc::SetLocationForGlobal(entryObj, spec);
+ }
+
+ // The hash owns the ModuleEntry now, forget about it
+ return entry.forget();
+}
+
+nsresult
+mozJSComponentLoader::FindTargetObject(JSContext* aCx,
+ MutableHandleObject aTargetObject)
+{
+ aTargetObject.set(nullptr);
+
+ RootedObject targetObject(aCx);
+ if (mReuseLoaderGlobal) {
+ JSFunction* fun = js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
+ if (fun) {
+ JSObject* funParent = js::GetNearestEnclosingWithEnvironmentObjectForFunction(fun);
+ if (JS_GetClass(funParent) == &kFakeBackstagePassJSClass)
+ targetObject = funParent;
+ }
+ }
+
+ // The above could fail, even if mReuseLoaderGlobal, if the scripted
+ // caller is not a component/JSM (it could be a DOM scope, for
+ // instance).
+ if (!targetObject) {
+ // Our targetObject is the caller's global object. Let's get it.
+ targetObject = CurrentGlobalOrNull(aCx);
+ }
+
+ aTargetObject.set(targetObject);
+ return NS_OK;
+}
+
+// This requires that the keys be strings and the values be pointers.
+template <class Key, class Data, class UserData>
+static size_t
+SizeOfTableExcludingThis(const nsBaseHashtable<Key, Data, UserData>& aTable,
+ MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ return n;
+}
+
+size_t
+mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ n += SizeOfTableExcludingThis(mModules, aMallocSizeOf);
+ n += SizeOfTableExcludingThis(mImports, aMallocSizeOf);
+ n += SizeOfTableExcludingThis(mInProgressImports, aMallocSizeOf);
+ return n;
+}
+
+// Some stack based classes for cleaning up on early return
+#ifdef HAVE_PR_MEMMAP
+class FileAutoCloser
+{
+ public:
+ explicit FileAutoCloser(PRFileDesc* file) : mFile(file) {}
+ ~FileAutoCloser() { PR_Close(mFile); }
+ private:
+ PRFileDesc* mFile;
+};
+
+class FileMapAutoCloser
+{
+ public:
+ explicit FileMapAutoCloser(PRFileMap* map) : mMap(map) {}
+ ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
+ private:
+ PRFileMap* mMap;
+};
+#else
+class ANSIFileAutoCloser
+{
+ public:
+ explicit ANSIFileAutoCloser(FILE* file) : mFile(file) {}
+ ~ANSIFileAutoCloser() { fclose(mFile); }
+ private:
+ FILE* mFile;
+};
+#endif
+
+JSObject*
+mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
+ nsIFile* aComponentFile,
+ nsIURI* aURI,
+ bool aReuseLoaderGlobal,
+ bool* aRealFile)
+{
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+ if (aReuseLoaderGlobal) {
+ holder = mLoaderGlobal;
+ }
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIXPConnect> xpc =
+ do_GetService(kXPConnectServiceContractID, &rv);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ bool createdNewGlobal = false;
+
+ if (!mLoaderGlobal) {
+ RefPtr<BackstagePass> backstagePass;
+ rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ CompartmentOptions options;
+
+ options.creationOptions()
+ .setZone(SystemZone)
+ .setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI));
+
+ options.behaviors().setVersion(JSVERSION_LATEST);
+
+ if (xpc::SharedMemoryEnabled())
+ options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+
+ // Defer firing OnNewGlobalObject until after the __URI__ property has
+ // been defined so the JS debugger can tell what module the global is
+ // for
+ rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
+ static_cast<nsIGlobalObject*>(backstagePass),
+ mSystemPrincipal,
+ nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
+ options,
+ getter_AddRefs(holder));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ createdNewGlobal = true;
+
+ RootedObject global(aCx, holder->GetJSObject());
+ NS_ENSURE_TRUE(global, nullptr);
+
+ backstagePass->SetGlobalObject(global);
+
+ JSAutoCompartment ac(aCx, global);
+ if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
+ !JS_DefineProfilingFunctions(aCx, global)) {
+ return nullptr;
+ }
+
+ if (aReuseLoaderGlobal) {
+ mLoaderGlobal = holder;
+ }
+ }
+
+ RootedObject obj(aCx, holder->GetJSObject());
+ NS_ENSURE_TRUE(obj, nullptr);
+
+ JSAutoCompartment ac(aCx, obj);
+
+ if (aReuseLoaderGlobal) {
+ // If we're reusing the loader global, we don't actually use the
+ // global, but rather we use a different object as the 'this' object.
+ obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass);
+ NS_ENSURE_TRUE(obj, nullptr);
+ }
+
+ *aRealFile = false;
+
+ // need to be extra careful checking for URIs pointing to files
+ // EnsureFile may not always get called, especially on resource URIs
+ // so we need to call GetFile to make sure this is a valid file
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
+ nsCOMPtr<nsIFile> testFile;
+ if (NS_SUCCEEDED(rv)) {
+ fileURL->GetFile(getter_AddRefs(testFile));
+ }
+
+ if (testFile) {
+ *aRealFile = true;
+
+ if (XRE_IsParentProcess()) {
+ RootedObject locationObj(aCx);
+
+ rv = xpc->WrapNative(aCx, obj, aComponentFile,
+ NS_GET_IID(nsIFile),
+ locationObj.address());
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ NS_ENSURE_TRUE(locationObj, nullptr);
+
+ if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0))
+ return nullptr;
+ }
+ }
+
+ nsAutoCString nativePath;
+ rv = aURI->GetSpec(nativePath);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ // Expose the URI from which the script was imported through a special
+ // variable that we insert into the JSM.
+ RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
+ NS_ENSURE_TRUE(exposedUri, nullptr);
+
+ if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0))
+ return nullptr;
+
+ if (createdNewGlobal) {
+ // AutoEntryScript required to invoke debugger hook, which is a
+ // Gecko-specific concept at present.
+ dom::AutoEntryScript aes(holder->GetJSObject(),
+ "component loader report global");
+ RootedObject global(aes.cx(), holder->GetJSObject());
+ JS_FireOnNewGlobalObject(aes.cx(), global);
+ }
+
+ return obj;
+}
+
+nsresult
+mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
+ nsIFile* aComponentFile,
+ MutableHandleObject aObject,
+ MutableHandleScript aTableScript,
+ char** aLocation,
+ bool aPropagateExceptions,
+ MutableHandleValue aException)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ bool realFile = false;
+ nsresult rv = aInfo.EnsureURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+ RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aInfo.URI(),
+ mReuseLoaderGlobal, &realFile));
+ NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
+ MOZ_ASSERT(JS_IsGlobalObject(obj) == !mReuseLoaderGlobal);
+
+ JSAutoCompartment ac(cx, obj);
+
+ RootedScript script(cx);
+ RootedFunction function(cx);
+
+ nsAutoCString nativePath;
+ rv = aInfo.URI()->GetSpec(nativePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Before compiling the script, first check to see if we have it in
+ // the startupcache. Note: as a rule, startupcache errors are not fatal
+ // to loading the script, since we can always slow-load.
+
+ bool writeToCache = false;
+ StartupCache* cache = StartupCache::GetSingleton();
+
+ nsAutoCString cachePath(kJSCachePrefix);
+ rv = PathifyURI(aInfo.URI(), cachePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (cache) {
+ if (!mReuseLoaderGlobal) {
+ rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
+ } else {
+ rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
+ function.address());
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
+ } else {
+ // This is ok, it just means the script is not yet in the
+ // cache. Could mean that the cache was corrupted and got removed,
+ // but either way we're going to write this out.
+ writeToCache = true;
+ // ReadCachedScript and ReadCachedFunction may have set a pending
+ // exception.
+ JS_ClearPendingException(cx);
+ }
+ }
+
+ if (!script && !function) {
+ // The script wasn't in the cache , so compile it now.
+ LOG(("Slow loading %s\n", nativePath.get()));
+
+ // Use lazy source if both of these conditions hold:
+ //
+ // (1) mReuseLoaderGlobal is false. If mReuseLoaderGlobal is true, we
+ // can't do lazy source because we compile things as functions
+ // (rather than script), and lazy source isn't supported in that
+ // configuration. That's ok though, because we only do
+ // mReuseLoaderGlobal on b2g, where we invoke setDiscardSource(true)
+ // on the entire global.
+ //
+ // (2) We're using the startup cache. Non-lazy source + startup cache
+ // regresses installer size (due to source code stored in XDR
+ // encoded modules in omni.ja). Also, XDR decoding is relatively
+ // fast. Content processes don't use the startup cache, so we want
+ // them to use non-lazy source code to enable lazy parsing.
+ // See bug 1303754.
+ CompileOptions options(cx);
+ options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
+ .setVersion(JSVERSION_LATEST)
+ .setFileAndLine(nativePath.get(), 1)
+ .setSourceIsLazy(!mReuseLoaderGlobal && !!cache);
+
+ if (realFile) {
+#ifdef HAVE_PR_MEMMAP
+ int64_t fileSize;
+ rv = aComponentFile->GetFileSize(&fileSize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ int64_t maxSize = UINT32_MAX;
+ if (fileSize > maxSize) {
+ NS_ERROR("file too large");
+ return NS_ERROR_FAILURE;
+ }
+
+ PRFileDesc* fileHandle;
+ rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // Make sure the file is closed, no matter how we return.
+ FileAutoCloser fileCloser(fileHandle);
+
+ // We don't provide the file size here. If we did, PR_CreateFileMap
+ // would simply stat() the file to verify that the size we provided
+ // didn't require extending the file. We know that the file doesn't
+ // need to be extended, so skip the extra work by not providing the
+ // size.
+ PRFileMap* map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY);
+ if (!map) {
+ NS_ERROR("Failed to create file map");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the file map is closed, no matter how we return.
+ FileMapAutoCloser mapCloser(map);
+
+ uint32_t fileSize32 = fileSize;
+
+ char* buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
+ if (!buf) {
+ NS_WARNING("Failed to map file");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mReuseLoaderGlobal) {
+ Compile(cx, options, buf, fileSize32, &script);
+ } else {
+ // Note: exceptions will get handled further down;
+ // don't early return for them here.
+ AutoObjectVector envChain(cx);
+ if (envChain.append(obj)) {
+ CompileFunction(cx, envChain,
+ options, nullptr, 0, nullptr,
+ buf, fileSize32, &function);
+ }
+ }
+
+ PR_MemUnmap(buf, fileSize32);
+
+#else /* HAVE_PR_MEMMAP */
+
+ /**
+ * No memmap implementation, so fall back to
+ * reading in the file
+ */
+
+ FILE* fileHandle;
+ rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // Ensure file fclose
+ ANSIFileAutoCloser fileCloser(fileHandle);
+
+ int64_t len;
+ rv = aComponentFile->GetFileSize(&len);
+ if (NS_FAILED(rv) || len < 0) {
+ NS_WARNING("Failed to get file size");
+ return NS_ERROR_FAILURE;
+ }
+
+ char* buf = (char*) malloc(len * sizeof(char));
+ if (!buf) {
+ return NS_ERROR_FAILURE;
+ }
+
+ size_t rlen = fread(buf, 1, len, fileHandle);
+ if (rlen != (uint64_t)len) {
+ free(buf);
+ NS_WARNING("Failed to read file");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mReuseLoaderGlobal) {
+ script = Compile(cx, options, buf, fileSize32);
+ } else {
+ // Note: exceptions will get handled further down;
+ // don't early return for them here.
+ AutoObjectVector envChain(cx);
+ if (envChain.append(obj)) {
+ CompileFunction(cx, envChain,
+ options, nullptr, 0, nullptr,
+ buf, fileSize32, &function);
+ }
+ }
+
+ free(buf);
+
+#endif /* HAVE_PR_MEMMAP */
+ } else {
+ rv = aInfo.EnsureScriptChannel();
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIInputStream> scriptStream;
+ rv = NS_MaybeOpenChannelUsingOpen2(aInfo.ScriptChannel(),
+ getter_AddRefs(scriptStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint64_t len64;
+ uint32_t bytesRead;
+
+ rv = scriptStream->Available(&len64);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+ if (!len64)
+ return NS_ERROR_FAILURE;
+ uint32_t len = (uint32_t)len64;
+
+ /* malloc an internal buf the size of the file */
+ auto buf = MakeUniqueFallible<char[]>(len + 1);
+ if (!buf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ /* read the file in one swoop */
+ rv = scriptStream->Read(buf.get(), len, &bytesRead);
+ if (bytesRead != len)
+ return NS_BASE_STREAM_OSERROR;
+
+ buf[len] = '\0';
+
+ if (!mReuseLoaderGlobal) {
+ Compile(cx, options, buf.get(), bytesRead, &script);
+ } else {
+ // Note: exceptions will get handled further down;
+ // don't early return for them here.
+ AutoObjectVector envChain(cx);
+ if (envChain.append(obj)) {
+ CompileFunction(cx, envChain,
+ options, nullptr, 0, nullptr,
+ buf.get(), bytesRead, &function);
+ }
+ }
+ }
+ // Propagate the exception, if one exists. Also, don't leave the stale
+ // exception on this context.
+ if (!script && !function && aPropagateExceptions &&
+ jsapi.HasException()) {
+ if (!jsapi.StealException(aException))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (!script && !function) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // We must have a script or a function (but not both!) here. We have a
+ // script when we're not reusing the loader global, and a function
+ // otherwise.
+ MOZ_ASSERT(!!script != !!function);
+ MOZ_ASSERT(!!script == JS_IsGlobalObject(obj));
+
+ if (writeToCache) {
+ // We successfully compiled the script, so cache it.
+ if (script) {
+ rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal,
+ script);
+ } else {
+ rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal,
+ function);
+ }
+
+ // Don't treat failure to write as fatal, since we might be working
+ // with a read-only cache.
+ if (NS_SUCCEEDED(rv)) {
+ LOG(("Successfully wrote to cache\n"));
+ } else {
+ LOG(("Failed to write to cache\n"));
+ }
+ }
+
+ // Assign aObject here so that it's available to recursive imports.
+ // See bug 384168.
+ aObject.set(obj);
+
+ RootedScript tableScript(cx, script);
+ if (!tableScript) {
+ tableScript = JS_GetFunctionScript(cx, function);
+ MOZ_ASSERT(tableScript);
+ }
+
+ aTableScript.set(tableScript);
+
+
+ { // Scope for AutoEntryScript
+
+ // We're going to run script via JS_ExecuteScript or
+ // JS_CallFunction, so we need an AutoEntryScript.
+ // This is Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(CurrentGlobalOrNull(cx),
+ "component loader load module");
+ JSContext* aescx = aes.cx();
+ bool ok;
+ if (script) {
+ ok = JS_ExecuteScript(aescx, script);
+ } else {
+ RootedValue rval(cx);
+ ok = JS_CallFunction(aescx, obj, function,
+ JS::HandleValueArray::empty(), &rval);
+ }
+
+ if (!ok) {
+ if (aPropagateExceptions && aes.HasException()) {
+ // Ignore return value because we're returning an error code
+ // anyway.
+ Unused << aes.StealException(aException);
+ }
+ aObject.set(nullptr);
+ aTableScript.set(nullptr);
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ /* Freed when we remove from the table. */
+ *aLocation = ToNewCString(nativePath);
+ if (!*aLocation) {
+ aObject.set(nullptr);
+ aTableScript.set(nullptr);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+void
+mozJSComponentLoader::UnloadModules()
+{
+ mInitialized = false;
+
+ if (mLoaderGlobal) {
+ MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
+
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ RootedObject global(cx, mLoaderGlobal->GetJSObject());
+ if (global) {
+ JSAutoCompartment ac(cx, global);
+ if (JS_HasExtensibleLexicalEnvironment(global)) {
+ JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalEnvironment(global));
+ }
+ JS_SetAllNonReservedSlotsToUndefined(cx, global);
+ } else {
+ NS_WARNING("Going to leak!");
+ }
+
+ mLoaderGlobal = nullptr;
+ }
+
+ mInProgressImports.Clear();
+ mImports.Clear();
+
+ for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
+ iter.Data()->Clear();
+ iter.Remove();
+ }
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::Import(const nsACString& registryLocation,
+ HandleValue targetValArg,
+ JSContext* cx,
+ uint8_t optionalArgc,
+ MutableHandleValue retval)
+{
+ MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+ RootedValue targetVal(cx, targetValArg);
+ RootedObject targetObject(cx, nullptr);
+ if (optionalArgc) {
+ // The caller passed in the optional second argument. Get it.
+ if (targetVal.isObject()) {
+ // If we're passing in something like a content DOM window, chances
+ // are the caller expects the properties to end up on the object
+ // proper and not on the Xray holder. This is dubious, but can be used
+ // during testing. Given that dumb callers can already leak JSMs into
+ // content by passing a raw content JS object (where Xrays aren't
+ // possible), we aim for consistency here. Waive xray.
+ if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
+ !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
+ {
+ return NS_ERROR_FAILURE;
+ }
+ targetObject = &targetVal.toObject();
+ } else if (!targetVal.isNull()) {
+ // If targetVal isNull(), we actually want to leave targetObject null.
+ // Not doing so breaks |make package|.
+ return ReportOnCallerUTF8(cx, ERROR_SCOPE_OBJ,
+ PromiseFlatCString(registryLocation).get());
+ }
+ } else {
+ nsresult rv = FindTargetObject(cx, &targetObject);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ Maybe<JSAutoCompartment> ac;
+ if (targetObject) {
+ ac.emplace(cx, targetObject);
+ }
+
+ RootedObject global(cx);
+ nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
+
+ if (global) {
+ if (!JS_WrapObject(cx, &global)) {
+ NS_ERROR("can't wrap return value");
+ return NS_ERROR_FAILURE;
+ }
+
+ retval.setObject(*global);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::ImportInto(const nsACString& aLocation,
+ JSObject* aTargetObj,
+ nsAXPCNativeCallContext* cc,
+ JSObject** _retval)
+{
+ JSContext* callercx;
+ nsresult rv = cc->GetJSContext(&callercx);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RootedObject targetObject(callercx, aTargetObj);
+ RootedObject global(callercx);
+ rv = ImportInto(aLocation, targetObject, callercx, &global);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *_retval = global;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::IsModuleLoaded(const nsACString& aLocation,
+ bool* retval)
+{
+ MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+ nsresult rv;
+ if (!mInitialized) {
+ rv = ReallyInit();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ ComponentLoaderInfo info(aLocation);
+ rv = info.EnsureKey();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *retval = !!mImports.Get(info.Key());
+ return NS_OK;
+}
+
+static JSObject*
+ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId id)
+{
+ if (JS_HasExtensibleLexicalEnvironment(aModObj)) {
+ RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj));
+ bool found;
+ if (!JS_HasOwnPropertyById(aCx, lexical, id, &found)) {
+ return nullptr;
+ }
+ if (found) {
+ return lexical;
+ }
+ }
+ return aModObj;
+}
+
+nsresult
+mozJSComponentLoader::ImportInto(const nsACString& aLocation,
+ HandleObject targetObj,
+ JSContext* callercx,
+ MutableHandleObject vp)
+{
+ vp.set(nullptr);
+
+ nsresult rv;
+ if (!mInitialized) {
+ rv = ReallyInit();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ ComponentLoaderInfo info(aLocation);
+ rv = info.EnsureResolvedURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the JAR if there is one
+ nsCOMPtr<nsIJARURI> jarURI;
+ jarURI = do_QueryInterface(info.ResolvedURI(), &rv);
+ nsCOMPtr<nsIFileURL> baseFileURL;
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIURI> baseURI;
+ while (jarURI) {
+ jarURI->GetJARFile(getter_AddRefs(baseURI));
+ jarURI = do_QueryInterface(baseURI, &rv);
+ }
+ baseFileURL = do_QueryInterface(baseURI, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ baseFileURL = do_QueryInterface(info.ResolvedURI(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIFile> sourceFile;
+ rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> sourceLocalFile;
+ sourceLocalFile = do_QueryInterface(sourceFile, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = info.EnsureKey();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ModuleEntry* mod;
+ nsAutoPtr<ModuleEntry> newEntry;
+ if (!mImports.Get(info.Key(), &mod) && !mInProgressImports.Get(info.Key(), &mod)) {
+ newEntry = new ModuleEntry(RootingContext::get(callercx));
+ if (!newEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+ mInProgressImports.Put(info.Key(), newEntry);
+
+ rv = info.EnsureURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+ RootedValue exception(callercx);
+ rv = ObjectForLocation(info, sourceLocalFile, &newEntry->obj,
+ &newEntry->thisObjectKey,
+ &newEntry->location, true, &exception);
+
+ mInProgressImports.Remove(info.Key());
+
+ if (NS_FAILED(rv)) {
+ if (!exception.isUndefined()) {
+ // An exception was thrown during compilation. Propagate it
+ // out to our caller so they can report it.
+ if (!JS_WrapValue(callercx, &exception))
+ return NS_ERROR_OUT_OF_MEMORY;
+ JS_SetPendingException(callercx, exception);
+ return NS_OK;
+ }
+
+ // Something failed, but we don't know what it is, guess.
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // Set the location information for the new global, so that tools like
+ // about:memory may use that information
+ if (!mReuseLoaderGlobal) {
+ xpc::SetLocationForGlobal(newEntry->obj, aLocation);
+ }
+
+ mod = newEntry;
+ }
+
+ MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
+ vp.set(mod->obj);
+
+ if (targetObj) {
+ // cxhelper must be created before jsapi, so that jsapi is detroyed and
+ // pops any context it has pushed before we report to the caller context.
+ JSCLContextHelper cxhelper(callercx);
+
+ // Even though we are calling JS_SetPropertyById on targetObj, we want
+ // to ensure that we never run script here, so we use an AutoJSAPI and
+ // not an AutoEntryScript.
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ JSAutoCompartment ac(cx, mod->obj);
+
+ RootedValue symbols(cx);
+ RootedObject exportedSymbolsHolder(cx, ResolveModuleObjectProperty(cx, mod->obj,
+ "EXPORTED_SYMBOLS"));
+ if (!exportedSymbolsHolder ||
+ !JS_GetProperty(cx, exportedSymbolsHolder,
+ "EXPORTED_SYMBOLS", &symbols)) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT,
+ location.get());
+ }
+
+ bool isArray;
+ if (!JS_IsArrayObject(cx, symbols, &isArray)) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!isArray) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY,
+ location.get());
+ }
+
+ RootedObject symbolsObj(cx, &symbols.toObject());
+
+ // Iterate over symbols array, installing symbols on targetObj:
+
+ uint32_t symbolCount = 0;
+ if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
+ location.get());
+ }
+
+#ifdef DEBUG
+ nsAutoCString logBuffer;
+#endif
+
+ RootedValue value(cx);
+ RootedId symbolId(cx);
+ RootedObject symbolHolder(cx);
+ for (uint32_t i = 0; i < symbolCount; ++i) {
+ if (!JS_GetElement(cx, symbolsObj, i, &value) ||
+ !value.isString() ||
+ !JS_ValueToId(cx, value, &symbolId)) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT,
+ location.get(), i);
+ }
+
+ symbolHolder = ResolveModuleObjectPropertyById(cx, mod->obj, symbolId);
+ if (!symbolHolder ||
+ !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
+ JSAutoByteString bytes;
+ RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
+ if (!bytes.encodeUtf8(cx, symbolStr))
+ return NS_ERROR_FAILURE;
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
+ location.get(), bytes.ptr());
+ }
+
+ JSAutoCompartment target_ac(cx, targetObj);
+
+ if (!JS_WrapValue(cx, &value) ||
+ !JS_SetPropertyById(cx, targetObj, symbolId, value)) {
+ JSAutoByteString bytes;
+ RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
+ if (!bytes.encodeUtf8(cx, symbolStr))
+ return NS_ERROR_FAILURE;
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_SETTING_SYMBOL,
+ location.get(), bytes.ptr());
+ }
+#ifdef DEBUG
+ if (i == 0) {
+ logBuffer.AssignLiteral("Installing symbols [ ");
+ }
+ JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
+ if (!!bytes)
+ logBuffer.Append(bytes.ptr());
+ logBuffer.Append(' ');
+ if (i == symbolCount - 1) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ LOG(("%s] from %s\n", logBuffer.get(), location.get()));
+ }
+#endif
+ }
+ }
+
+ // Cache this module for later
+ if (newEntry) {
+ mImports.Put(info.Key(), newEntry);
+ newEntry.forget();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::Unload(const nsACString & aLocation)
+{
+ nsresult rv;
+
+ if (!mInitialized) {
+ return NS_OK;
+ }
+
+ MOZ_RELEASE_ASSERT(!mReuseLoaderGlobal, "Module unloading not supported when "
+ "compartment sharing is enabled");
+
+ ComponentLoaderInfo info(aLocation);
+ rv = info.EnsureKey();
+ NS_ENSURE_SUCCESS(rv, rv);
+ ModuleEntry* mod;
+ if (mImports.Get(info.Key(), &mod)) {
+ mImports.Remove(info.Key());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data)
+{
+ if (!strcmp(topic, "xpcom-shutdown-loaders")) {
+ UnloadModules();
+ } else {
+ NS_ERROR("Unexpected observer topic.");
+ }
+
+ return NS_OK;
+}
+
+size_t
+mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t n = aMallocSizeOf(this);
+ n += aMallocSizeOf(location);
+
+ return n;
+}
+
+/* static */ already_AddRefed<nsIFactory>
+mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
+ const mozilla::Module::CIDEntry& entry)
+{
+ const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
+ MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
+
+ nsCOMPtr<nsIFactory> f;
+ nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ return f.forget();
+}
+
+//----------------------------------------------------------------------
+
+JSCLContextHelper::JSCLContextHelper(JSContext* aCx)
+ : mContext(aCx)
+ , mBuf(nullptr)
+{
+}
+
+JSCLContextHelper::~JSCLContextHelper()
+{
+ if (mBuf) {
+ JS_ReportErrorUTF8(mContext, "%s", mBuf);
+ JS_smprintf_free(mBuf);
+ }
+}
+
+void
+JSCLContextHelper::reportErrorAfterPop(char* buf)
+{
+ MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
+ mBuf = buf;
+}
diff --git a/js/xpconnect/loader/mozJSComponentLoader.h b/js/xpconnect/loader/mozJSComponentLoader.h
new file mode 100644
index 000000000..6ebe4a371
--- /dev/null
+++ b/js/xpconnect/loader/mozJSComponentLoader.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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 mozJSComponentLoader_h
+#define mozJSComponentLoader_h
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ModuleLoader.h"
+#include "nsAutoPtr.h"
+#include "nsISupports.h"
+#include "nsIObserver.h"
+#include "nsIURI.h"
+#include "xpcIJSModuleLoader.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "jsapi.h"
+
+#include "xpcIJSGetFactory.h"
+
+class nsIFile;
+class nsIPrincipal;
+class nsIXPConnectJSObjectHolder;
+class ComponentLoaderInfo;
+
+/* 6bd13476-1dd2-11b2-bbef-f0ccb5fa64b6 (thanks, mozbot) */
+
+#define MOZJSCOMPONENTLOADER_CID \
+ {0x6bd13476, 0x1dd2, 0x11b2, \
+ { 0xbb, 0xef, 0xf0, 0xcc, 0xb5, 0xfa, 0x64, 0xb6 }}
+#define MOZJSCOMPONENTLOADER_CONTRACTID "@mozilla.org/moz/jsloader;1"
+
+class mozJSComponentLoader : public mozilla::ModuleLoader,
+ public xpcIJSModuleLoader,
+ public nsIObserver
+{
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_XPCIJSMODULELOADER
+ NS_DECL_NSIOBSERVER
+
+ mozJSComponentLoader();
+
+ // ModuleLoader
+ const mozilla::Module* LoadModule(mozilla::FileLocation& aFile) override;
+
+ nsresult FindTargetObject(JSContext* aCx,
+ JS::MutableHandleObject aTargetObject);
+
+ static mozJSComponentLoader* Get() { return sSelf; }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ protected:
+ virtual ~mozJSComponentLoader();
+
+ static mozJSComponentLoader* sSelf;
+
+ nsresult ReallyInit();
+ void UnloadModules();
+
+ JSObject* PrepareObjectForLocation(JSContext* aCx,
+ nsIFile* aComponentFile,
+ nsIURI* aComponent,
+ bool aReuseLoaderGlobal,
+ bool* aRealFile);
+
+ nsresult ObjectForLocation(ComponentLoaderInfo& aInfo,
+ nsIFile* aComponentFile,
+ JS::MutableHandleObject aObject,
+ JS::MutableHandleScript aTableScript,
+ char** location,
+ bool aCatchException,
+ JS::MutableHandleValue aException);
+
+ nsresult ImportInto(const nsACString& aLocation,
+ JS::HandleObject targetObj,
+ JSContext* callercx,
+ JS::MutableHandleObject vp);
+
+ nsCOMPtr<nsIComponentManager> mCompMgr;
+ nsCOMPtr<nsIPrincipal> mSystemPrincipal;
+ nsCOMPtr<nsIXPConnectJSObjectHolder> mLoaderGlobal;
+
+ class ModuleEntry : public mozilla::Module
+ {
+ public:
+ explicit ModuleEntry(JS::RootingContext* aRootingCx)
+ : mozilla::Module(), obj(aRootingCx), thisObjectKey(aRootingCx)
+ {
+ mVersion = mozilla::Module::kVersion;
+ mCIDs = nullptr;
+ mContractIDs = nullptr;
+ mCategoryEntries = nullptr;
+ getFactoryProc = GetFactory;
+ loadProc = nullptr;
+ unloadProc = nullptr;
+
+ location = nullptr;
+ }
+
+ ~ModuleEntry() {
+ Clear();
+ }
+
+ void Clear() {
+ getfactoryobj = nullptr;
+
+ if (obj) {
+ mozilla::AutoJSContext cx;
+ JSAutoCompartment ac(cx, obj);
+
+ if (JS_HasExtensibleLexicalEnvironment(obj)) {
+ JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalEnvironment(obj));
+ }
+ JS_SetAllNonReservedSlotsToUndefined(cx, obj);
+ obj = nullptr;
+ thisObjectKey = nullptr;
+ }
+
+ if (location)
+ free(location);
+
+ obj = nullptr;
+ thisObjectKey = nullptr;
+ location = nullptr;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ static already_AddRefed<nsIFactory> GetFactory(const mozilla::Module& module,
+ const mozilla::Module::CIDEntry& entry);
+
+ nsCOMPtr<xpcIJSGetFactory> getfactoryobj;
+ JS::PersistentRootedObject obj;
+ JS::PersistentRootedScript thisObjectKey;
+ char* location;
+ };
+
+ friend class ModuleEntry;
+
+ static size_t DataEntrySizeOfExcludingThis(const nsACString& aKey, ModuleEntry* const& aData,
+ mozilla::MallocSizeOf aMallocSizeOf, void* arg);
+ static size_t ClassEntrySizeOfExcludingThis(const nsACString& aKey,
+ const nsAutoPtr<ModuleEntry>& aData,
+ mozilla::MallocSizeOf aMallocSizeOf, void* arg);
+
+ // Modules are intentionally leaked, but still cleared.
+ nsDataHashtable<nsCStringHashKey, ModuleEntry*> mModules;
+
+ nsClassHashtable<nsCStringHashKey, ModuleEntry> mImports;
+ nsDataHashtable<nsCStringHashKey, ModuleEntry*> mInProgressImports;
+
+ bool mInitialized;
+ bool mReuseLoaderGlobal;
+};
+
+#endif
diff --git a/js/xpconnect/loader/mozJSLoaderUtils.cpp b/js/xpconnect/loader/mozJSLoaderUtils.cpp
new file mode 100644
index 000000000..abc8aa3bb
--- /dev/null
+++ b/js/xpconnect/loader/mozJSLoaderUtils.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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/scache/StartupCache.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "nsJSPrincipals.h"
+
+using namespace JS;
+using namespace mozilla::scache;
+using mozilla::UniquePtr;
+
+// We only serialize scripts with system principals. So we don't serialize the
+// principals when writing a script. Instead, when reading it back, we set the
+// principals to the system principals.
+nsresult
+ReadCachedScript(StartupCache* cache, nsACString& uri, JSContext* cx,
+ nsIPrincipal* systemPrincipal, MutableHandleScript scriptp)
+{
+ UniquePtr<char[]> buf;
+ uint32_t len;
+ nsresult rv = cache->GetBuffer(PromiseFlatCString(uri).get(), &buf, &len);
+ if (NS_FAILED(rv))
+ return rv; // don't warn since NOT_AVAILABLE is an ok error
+
+ JS::TranscodeBuffer buffer;
+ buffer.replaceRawBuffer(reinterpret_cast<uint8_t*>(buf.release()), len);
+ JS::TranscodeResult code = JS::DecodeScript(cx, buffer, scriptp);
+ if (code == JS::TranscodeResult_Ok)
+ return NS_OK;
+
+ if ((code & JS::TranscodeResult_Failure) != 0)
+ return NS_ERROR_FAILURE;
+
+ MOZ_ASSERT((code & JS::TranscodeResult_Throw) != 0);
+ JS_ClearPendingException(cx);
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+ReadCachedFunction(StartupCache* cache, nsACString& uri, JSContext* cx,
+ nsIPrincipal* systemPrincipal, JSFunction** functionp)
+{
+ // This doesn't actually work ...
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+WriteCachedScript(StartupCache* cache, nsACString& uri, JSContext* cx,
+ nsIPrincipal* systemPrincipal, HandleScript script)
+{
+ MOZ_ASSERT(JS_GetScriptPrincipals(script) == nsJSPrincipals::get(systemPrincipal));
+
+ JS::TranscodeBuffer buffer;
+ JS::TranscodeResult code = JS::EncodeScript(cx, buffer, script);
+ if (code != JS::TranscodeResult_Ok) {
+ if ((code & JS::TranscodeResult_Failure) != 0)
+ return NS_ERROR_FAILURE;
+ MOZ_ASSERT((code & JS::TranscodeResult_Throw) != 0);
+ JS_ClearPendingException(cx);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ size_t size = buffer.length();
+ if (size > UINT32_MAX)
+ return NS_ERROR_FAILURE;
+ nsresult rv = cache->PutBuffer(PromiseFlatCString(uri).get(),
+ reinterpret_cast<char*>(buffer.begin()),
+ size);
+ return rv;
+}
+
+nsresult
+WriteCachedFunction(StartupCache* cache, nsACString& uri, JSContext* cx,
+ nsIPrincipal* systemPrincipal, JSFunction* function)
+{
+ // This doesn't actually work ...
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/js/xpconnect/loader/mozJSLoaderUtils.h b/js/xpconnect/loader/mozJSLoaderUtils.h
new file mode 100644
index 000000000..9adf82bf6
--- /dev/null
+++ b/js/xpconnect/loader/mozJSLoaderUtils.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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 mozJSLoaderUtils_h
+#define mozJSLoaderUtils_h
+
+#include "nsString.h"
+
+namespace mozilla {
+namespace scache {
+class StartupCache;
+} // namespace scache
+} // namespace mozilla
+
+nsresult
+ReadCachedScript(mozilla::scache::StartupCache* cache, nsACString& uri,
+ JSContext* cx, nsIPrincipal* systemPrincipal,
+ JS::MutableHandleScript scriptp);
+
+nsresult
+ReadCachedFunction(mozilla::scache::StartupCache* cache, nsACString& uri,
+ JSContext* cx, nsIPrincipal* systemPrincipal,
+ JSFunction** function);
+
+nsresult
+WriteCachedScript(mozilla::scache::StartupCache* cache, nsACString& uri,
+ JSContext* cx, nsIPrincipal* systemPrincipal,
+ JS::HandleScript script);
+nsresult
+WriteCachedFunction(mozilla::scache::StartupCache* cache, nsACString& uri,
+ JSContext* cx, nsIPrincipal* systemPrincipal,
+ JSFunction* function);
+
+#endif /* mozJSLoaderUtils_h */
diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
new file mode 100644
index 000000000..824e9ab9e
--- /dev/null
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -0,0 +1,929 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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 "mozJSSubScriptLoader.h"
+#include "mozJSComponentLoader.h"
+#include "mozJSLoaderUtils.h"
+
+#include "nsIURI.h"
+#include "nsIIOService.h"
+#include "nsIChannel.h"
+#include "nsIInputStream.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIFileURL.h"
+#include "nsScriptLoader.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsThreadUtils.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "nsJSPrincipals.h"
+#include "xpcprivate.h" // For xpc::OptionsBase
+#include "jswrapper.h"
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+#include "mozilla/Unused.h"
+#include "nsContentUtils.h"
+#include "nsStringGlue.h"
+#include "nsCycleCollectionParticipant.h"
+
+using namespace mozilla::scache;
+using namespace JS;
+using namespace xpc;
+using namespace mozilla;
+using namespace mozilla::dom;
+
+class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase {
+public:
+ explicit LoadSubScriptOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options)
+ , target(cx)
+ , charset(NullString())
+ , ignoreCache(false)
+ , async(false)
+ { }
+
+ virtual bool Parse() {
+ return ParseObject("target", &target) &&
+ ParseString("charset", charset) &&
+ ParseBoolean("ignoreCache", &ignoreCache) &&
+ ParseBoolean("async", &async);
+ }
+
+ RootedObject target;
+ nsString charset;
+ bool ignoreCache;
+ bool async;
+};
+
+
+/* load() error msgs, XXX localize? */
+#define LOAD_ERROR_NOSERVICE "Error creating IO Service."
+#define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
+#define LOAD_ERROR_NOSCHEME "Failed to get URI scheme. This is bad."
+#define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
+#define LOAD_ERROR_NOSTREAM "Error opening input stream (invalid filename?)"
+#define LOAD_ERROR_NOCONTENT "ContentLength not available (not a local URL?)"
+#define LOAD_ERROR_BADCHARSET "Error converting to specified charset"
+#define LOAD_ERROR_BADREAD "File Read Error."
+#define LOAD_ERROR_READUNDERFLOW "File Read Error (underflow.)"
+#define LOAD_ERROR_NOPRINCIPALS "Failed to get principals."
+#define LOAD_ERROR_NOSPEC "Failed to get URI spec. This is bad."
+#define LOAD_ERROR_CONTENTTOOBIG "ContentLength is too large"
+
+mozJSSubScriptLoader::mozJSSubScriptLoader() : mSystemPrincipal(nullptr)
+{
+}
+
+mozJSSubScriptLoader::~mozJSSubScriptLoader()
+{
+ /* empty */
+}
+
+NS_IMPL_ISUPPORTS(mozJSSubScriptLoader, mozIJSSubScriptLoader)
+
+static void
+ReportError(JSContext* cx, const char* msg)
+{
+ RootedValue exn(cx, JS::StringValue(JS_NewStringCopyZ(cx, msg)));
+ JS_SetPendingException(cx, exn);
+}
+
+static void
+ReportError(JSContext* cx, const char* origMsg, nsIURI* uri)
+{
+ if (!uri) {
+ ReportError(cx, origMsg);
+ return;
+ }
+
+ nsAutoCString spec;
+ nsresult rv = uri->GetSpec(spec);
+ if (NS_FAILED(rv))
+ spec.Assign("(unknown)");
+
+ nsAutoCString msg(origMsg);
+ msg.Append(": ");
+ msg.Append(spec);
+ ReportError(cx, msg.get());
+}
+
+bool
+PrepareScript(nsIURI* uri,
+ JSContext* cx,
+ RootedObject& targetObj,
+ const char* uriStr,
+ const nsAString& charset,
+ const char* buf,
+ int64_t len,
+ bool reuseGlobal,
+ MutableHandleScript script,
+ MutableHandleFunction function)
+{
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(uriStr, 1)
+ .setVersion(JSVERSION_LATEST);
+ if (!charset.IsVoid()) {
+ char16_t* scriptBuf = nullptr;
+ size_t scriptLength = 0;
+
+ nsresult rv =
+ nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf), len,
+ charset, nullptr, scriptBuf, scriptLength);
+
+ JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
+ JS::SourceBufferHolder::GiveOwnership);
+
+ if (NS_FAILED(rv)) {
+ ReportError(cx, LOAD_ERROR_BADCHARSET, uri);
+ return false;
+ }
+
+ if (!reuseGlobal) {
+ if (JS_IsGlobalObject(targetObj)) {
+ return JS::Compile(cx, options, srcBuf, script);
+ }
+ return JS::CompileForNonSyntacticScope(cx, options, srcBuf, script);
+ } else {
+ AutoObjectVector envChain(cx);
+ if (!JS_IsGlobalObject(targetObj) && !envChain.append(targetObj)) {
+ return false;
+ }
+ return JS::CompileFunction(cx, envChain, options, nullptr, 0, nullptr,
+ srcBuf, function);
+ }
+ } else {
+ // We only use lazy source when no special encoding is specified because
+ // the lazy source loader doesn't know the encoding.
+ if (!reuseGlobal) {
+ options.setSourceIsLazy(true);
+ if (JS_IsGlobalObject(targetObj)) {
+ return JS::Compile(cx, options, buf, len, script);
+ }
+ return JS::CompileForNonSyntacticScope(cx, options, buf, len, script);
+ } else {
+ AutoObjectVector envChain(cx);
+ if (!JS_IsGlobalObject(targetObj) && !envChain.append(targetObj)) {
+ return false;
+ }
+ return JS::CompileFunction(cx, envChain, options, nullptr, 0, nullptr,
+ buf, len, function);
+ }
+ }
+}
+
+bool
+EvalScript(JSContext* cx,
+ RootedObject& target_obj,
+ MutableHandleValue retval,
+ nsIURI* uri,
+ bool cache,
+ RootedScript& script,
+ RootedFunction& function)
+{
+ if (function) {
+ script = JS_GetFunctionScript(cx, function);
+ }
+
+ if (function) {
+ if (!JS_CallFunction(cx, target_obj, function, JS::HandleValueArray::empty(), retval)) {
+ return false;
+ }
+ } else {
+ if (JS_IsGlobalObject(target_obj)) {
+ if (!JS_ExecuteScript(cx, script, retval)) {
+ return false;
+ }
+ } else {
+ JS::AutoObjectVector envChain(cx);
+ if (!envChain.append(target_obj)) {
+ return false;
+ }
+ if (!JS_ExecuteScript(cx, envChain, script, retval)) {
+ return false;
+ }
+ }
+ }
+
+ JSAutoCompartment rac(cx, target_obj);
+ if (!JS_WrapValue(cx, retval)) {
+ return false;
+ }
+
+ if (cache && !!script) {
+ nsAutoCString cachePath;
+ JSVersion version = JS_GetVersion(cx);
+ cachePath.AppendPrintf("jssubloader/%d", version);
+ PathifyURI(uri, cachePath);
+
+ nsCOMPtr<nsIScriptSecurityManager> secman =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ if (!secman) {
+ return false;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal;
+ nsresult rv = secman->GetSystemPrincipal(getter_AddRefs(principal));
+ if (NS_FAILED(rv) || !principal) {
+ ReportError(cx, LOAD_ERROR_NOPRINCIPALS, uri);
+ return false;
+ }
+
+ WriteCachedScript(StartupCache::GetSingleton(),
+ cachePath, cx, principal, script);
+ }
+
+ return true;
+}
+
+class AsyncScriptLoader : public nsIIncrementalStreamLoaderObserver
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
+
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader)
+
+ AsyncScriptLoader(nsIChannel* aChannel, bool aReuseGlobal,
+ JSObject* aTargetObj, const nsAString& aCharset,
+ bool aCache, Promise* aPromise)
+ : mChannel(aChannel)
+ , mTargetObj(aTargetObj)
+ , mPromise(aPromise)
+ , mCharset(aCharset)
+ , mReuseGlobal(aReuseGlobal)
+ , mCache(aCache)
+ {
+ // Needed for the cycle collector to manage mTargetObj.
+ mozilla::HoldJSObjects(this);
+ }
+
+private:
+ virtual ~AsyncScriptLoader() {
+ mozilla::DropJSObjects(this);
+ }
+
+ RefPtr<nsIChannel> mChannel;
+ Heap<JSObject*> mTargetObj;
+ RefPtr<Promise> mPromise;
+ nsString mCharset;
+ bool mReuseGlobal;
+ bool mCache;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncScriptLoader)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncScriptLoader)
+ NS_INTERFACE_MAP_ENTRY(nsIIncrementalStreamLoaderObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncScriptLoader)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
+ tmp->mTargetObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncScriptLoader)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTargetObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncScriptLoader)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncScriptLoader)
+
+class MOZ_STACK_CLASS AutoRejectPromise
+{
+public:
+ AutoRejectPromise(AutoEntryScript& aAutoEntryScript,
+ Promise* aPromise,
+ nsIGlobalObject* aGlobalObject)
+ : mAutoEntryScript(aAutoEntryScript)
+ , mPromise(aPromise)
+ , mGlobalObject(aGlobalObject) {}
+
+ ~AutoRejectPromise() {
+ if (mPromise) {
+ JSContext* cx = mAutoEntryScript.cx();
+ RootedValue rejectionValue(cx, JS::UndefinedValue());
+ if (mAutoEntryScript.HasException()) {
+ Unused << mAutoEntryScript.PeekException(&rejectionValue);
+ }
+ mPromise->MaybeReject(cx, rejectionValue);
+ }
+ }
+
+ void ResolvePromise(HandleValue aResolveValue) {
+ mPromise->MaybeResolve(aResolveValue);
+ mPromise = nullptr;
+ }
+
+private:
+ AutoEntryScript& mAutoEntryScript;
+ RefPtr<Promise> mPromise;
+ nsCOMPtr<nsIGlobalObject> mGlobalObject;
+};
+
+NS_IMETHODIMP
+AsyncScriptLoader::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
+ nsISupports* aContext,
+ uint32_t aDataLength,
+ const uint8_t* aData,
+ uint32_t *aConsumedData)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
+ nsISupports* aContext,
+ nsresult aStatus,
+ uint32_t aLength,
+ const uint8_t* aBuf)
+{
+ nsCOMPtr<nsIURI> uri;
+ mChannel->GetURI(getter_AddRefs(uri));
+
+ nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(mTargetObj);
+ AutoEntryScript aes(globalObject, "async loadSubScript");
+ AutoRejectPromise autoPromise(aes, mPromise, globalObject);
+ JSContext* cx = aes.cx();
+
+ if (NS_FAILED(aStatus)) {
+ ReportError(cx, "Unable to load script.", uri);
+ }
+ // Just notify that we are done with this load.
+ NS_ENSURE_SUCCESS(aStatus, NS_OK);
+
+ if (aLength == 0) {
+ ReportError(cx, LOAD_ERROR_NOCONTENT, uri);
+ return NS_OK;
+ }
+
+ if (aLength > INT32_MAX) {
+ ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri);
+ return NS_OK;
+ }
+
+ RootedFunction function(cx);
+ RootedScript script(cx);
+ nsAutoCString spec;
+ nsresult rv = uri->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RootedObject target_obj(cx, mTargetObj);
+
+ if (!PrepareScript(uri, cx, target_obj, spec.get(), mCharset,
+ reinterpret_cast<const char*>(aBuf), aLength,
+ mReuseGlobal, &script, &function))
+ {
+ return NS_OK;
+ }
+
+ JS::Rooted<JS::Value> retval(cx);
+ if (EvalScript(cx, target_obj, &retval, uri, mCache, script, function)) {
+ autoPromise.ResolvePromise(retval);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri, JSObject* targetObjArg,
+ const nsAString& charset,
+ nsIIOService* serv, bool reuseGlobal,
+ bool cache, MutableHandleValue retval)
+{
+ RootedObject target_obj(RootingCx(), targetObjArg);
+
+ nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(target_obj);
+ ErrorResult result;
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(globalObject))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<Promise> promise = Promise::Create(globalObject, result);
+ if (result.Failed()) {
+ return result.StealNSResult();
+ }
+
+ DebugOnly<bool> asJS = ToJSValue(jsapi.cx(), promise, retval);
+ MOZ_ASSERT(asJS, "Should not fail to convert the promise to a JS value");
+
+ // We create a channel and call SetContentType, to avoid expensive MIME type
+ // lookups (bug 632490).
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ uri,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL,
+ serv);
+
+ if (!NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+
+ channel->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
+
+ RefPtr<AsyncScriptLoader> loadObserver =
+ new AsyncScriptLoader(channel,
+ reuseGlobal,
+ target_obj,
+ charset,
+ cache,
+ promise);
+
+ nsCOMPtr<nsIIncrementalStreamLoader> loader;
+ rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStreamListener> listener = loader.get();
+ return channel->AsyncOpen2(listener);
+}
+
+bool
+mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObjArg,
+ const nsAString& charset, const char* uriStr,
+ nsIIOService* serv, nsIPrincipal* principal,
+ bool reuseGlobal, JS::MutableHandleScript script,
+ JS::MutableHandleFunction function)
+{
+ script.set(nullptr);
+ function.set(nullptr);
+
+ RootedObject target_obj(cx, targetObjArg);
+
+ // We create a channel and call SetContentType, to avoid expensive MIME type
+ // lookups (bug 632490).
+ nsCOMPtr<nsIChannel> chan;
+ nsCOMPtr<nsIInputStream> instream;
+ nsresult rv;
+ rv = NS_NewChannel(getter_AddRefs(chan),
+ uri,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL,
+ serv);
+
+ if (NS_SUCCEEDED(rv)) {
+ chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
+ rv = chan->Open2(getter_AddRefs(instream));
+ }
+
+ if (NS_FAILED(rv)) {
+ ReportError(cx, LOAD_ERROR_NOSTREAM, uri);
+ return false;
+ }
+
+ int64_t len = -1;
+
+ rv = chan->GetContentLength(&len);
+ if (NS_FAILED(rv) || len == -1) {
+ ReportError(cx, LOAD_ERROR_NOCONTENT, uri);
+ return false;
+ }
+
+ if (len > INT32_MAX) {
+ ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri);
+ return false;
+ }
+
+ nsCString buf;
+ rv = NS_ReadInputStreamToString(instream, buf, len);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return PrepareScript(uri, cx, target_obj, uriStr, charset,
+ buf.get(), len,
+ reuseGlobal,
+ script, function);
+}
+
+NS_IMETHODIMP
+mozJSSubScriptLoader::LoadSubScript(const nsAString& url,
+ HandleValue target,
+ const nsAString& charset,
+ JSContext* cx,
+ MutableHandleValue retval)
+{
+ /*
+ * Loads a local url and evals it into the current cx
+ * Synchronous (an async version would be cool too.)
+ * url: The url to load. Must be local so that it can be loaded
+ * synchronously.
+ * target_obj: Optional object to eval the script onto (defaults to context
+ * global)
+ * charset: Optional character set to use for reading
+ * returns: Whatever jsval the script pointed to by the url returns.
+ * Should ONLY (O N L Y !) be called from JavaScript code.
+ */
+ LoadSubScriptOptions options(cx);
+ options.charset = charset;
+ options.target = target.isObject() ? &target.toObject() : nullptr;
+ return DoLoadSubScriptWithOptions(url, options, cx, retval);
+}
+
+
+NS_IMETHODIMP
+mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString& url,
+ HandleValue optionsVal,
+ JSContext* cx,
+ MutableHandleValue retval)
+{
+ if (!optionsVal.isObject())
+ return NS_ERROR_INVALID_ARG;
+ LoadSubScriptOptions options(cx, &optionsVal.toObject());
+ if (!options.Parse())
+ return NS_ERROR_INVALID_ARG;
+ return DoLoadSubScriptWithOptions(url, options, cx, retval);
+}
+
+nsresult
+mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
+ LoadSubScriptOptions& options,
+ JSContext* cx,
+ MutableHandleValue retval)
+{
+ nsresult rv = NS_OK;
+
+ /* set the system principal if it's not here already */
+ if (!mSystemPrincipal) {
+ nsCOMPtr<nsIScriptSecurityManager> secman =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ if (!secman)
+ return NS_OK;
+
+ rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
+ if (NS_FAILED(rv) || !mSystemPrincipal)
+ return rv;
+ }
+
+ RootedObject targetObj(cx);
+ mozJSComponentLoader* loader = mozJSComponentLoader::Get();
+ rv = loader->FindTargetObject(cx, &targetObj);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We base reusingGlobal off of what the loader told us, but we may not
+ // actually be using that object.
+ bool reusingGlobal = !JS_IsGlobalObject(targetObj);
+
+ if (options.target)
+ targetObj = options.target;
+
+ // Remember an object out of the calling compartment so that we
+ // can properly wrap the result later.
+ nsCOMPtr<nsIPrincipal> principal = mSystemPrincipal;
+ RootedObject result_obj(cx, targetObj);
+ targetObj = JS_FindCompilationScope(cx, targetObj);
+ if (!targetObj)
+ return NS_ERROR_FAILURE;
+
+ if (targetObj != result_obj)
+ principal = GetObjectPrincipal(targetObj);
+
+ /* load up the url. From here on, failures are reflected as ``custom''
+ * js exceptions */
+ nsCOMPtr<nsIURI> uri;
+ nsAutoCString uriStr;
+ nsAutoCString scheme;
+
+ // Figure out who's calling us
+ JS::AutoFilename filename;
+ if (!JS::DescribeScriptedCaller(cx, &filename)) {
+ // No scripted frame means we don't know who's calling, bail.
+ return NS_ERROR_FAILURE;
+ }
+
+ JSAutoCompartment ac(cx, targetObj);
+
+ // Suppress caching if we're compiling as content.
+ StartupCache* cache = (principal == mSystemPrincipal)
+ ? StartupCache::GetSingleton()
+ : nullptr;
+ nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
+ if (!serv) {
+ ReportError(cx, LOAD_ERROR_NOSERVICE);
+ return NS_OK;
+ }
+
+ // Make sure to explicitly create the URI, since we'll need the
+ // canonicalized spec.
+ rv = NS_NewURI(getter_AddRefs(uri), NS_LossyConvertUTF16toASCII(url).get(), nullptr, serv);
+ if (NS_FAILED(rv)) {
+ ReportError(cx, LOAD_ERROR_NOURI);
+ return NS_OK;
+ }
+
+ rv = uri->GetSpec(uriStr);
+ if (NS_FAILED(rv)) {
+ ReportError(cx, LOAD_ERROR_NOSPEC);
+ return NS_OK;
+ }
+
+ rv = uri->GetScheme(scheme);
+ if (NS_FAILED(rv)) {
+ ReportError(cx, LOAD_ERROR_NOSCHEME, uri);
+ return NS_OK;
+ }
+
+ if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("app")) {
+ // This might be a URI to a local file, though!
+ nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(innerURI);
+ if (!fileURL) {
+ ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL, uri);
+ return NS_OK;
+ }
+
+ // For file URIs prepend the filename with the filename of the
+ // calling script, and " -> ". See bug 418356.
+ nsAutoCString tmp(filename.get());
+ tmp.AppendLiteral(" -> ");
+ tmp.Append(uriStr);
+
+ uriStr = tmp;
+ }
+
+ JSVersion version = JS_GetVersion(cx);
+ nsAutoCString cachePath;
+ cachePath.AppendPrintf("jssubloader/%d", version);
+ PathifyURI(uri, cachePath);
+
+ RootedFunction function(cx);
+ RootedScript script(cx);
+ if (cache && !options.ignoreCache) {
+ rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
+ if (NS_FAILED(rv)) {
+ // ReadCachedScript may have set a pending exception.
+ JS_ClearPendingException(cx);
+ }
+ }
+
+ // If we are doing an async load, trigger it and bail out.
+ if (!script && options.async) {
+ return ReadScriptAsync(uri, targetObj, options.charset, serv,
+ reusingGlobal, !!cache, retval);
+ }
+
+ if (!script) {
+ if (!ReadScript(uri, cx, targetObj, options.charset,
+ static_cast<const char*>(uriStr.get()), serv,
+ principal, reusingGlobal, &script, &function))
+ {
+ return NS_OK;
+ }
+ } else {
+ cache = nullptr;
+ }
+
+ Unused << EvalScript(cx, targetObj, retval, uri, !!cache, script, function);
+ return NS_OK;
+}
+
+/**
+ * Let us compile scripts from a URI off the main thread.
+ */
+
+class ScriptPrecompiler : public nsIIncrementalStreamLoaderObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
+
+ ScriptPrecompiler(nsIObserver* aObserver,
+ nsIPrincipal* aPrincipal,
+ nsIChannel* aChannel)
+ : mObserver(aObserver)
+ , mPrincipal(aPrincipal)
+ , mChannel(aChannel)
+ , mScriptBuf(nullptr)
+ , mScriptLength(0)
+ {}
+
+ static void OffThreadCallback(void* aToken, void* aData);
+
+ /* Sends the "done" notification back. Main thread only. */
+ void SendObserverNotification();
+
+private:
+ virtual ~ScriptPrecompiler()
+ {
+ if (mScriptBuf) {
+ js_free(mScriptBuf);
+ }
+ }
+
+ RefPtr<nsIObserver> mObserver;
+ RefPtr<nsIPrincipal> mPrincipal;
+ RefPtr<nsIChannel> mChannel;
+ char16_t* mScriptBuf;
+ size_t mScriptLength;
+};
+
+NS_IMPL_ISUPPORTS(ScriptPrecompiler, nsIIncrementalStreamLoaderObserver);
+
+class NotifyPrecompilationCompleteRunnable : public Runnable
+{
+public:
+ NS_DECL_NSIRUNNABLE
+
+ explicit NotifyPrecompilationCompleteRunnable(ScriptPrecompiler* aPrecompiler)
+ : mPrecompiler(aPrecompiler)
+ , mToken(nullptr)
+ {}
+
+ void SetToken(void* aToken) {
+ MOZ_ASSERT(aToken && !mToken);
+ mToken = aToken;
+ }
+
+protected:
+ RefPtr<ScriptPrecompiler> mPrecompiler;
+ void* mToken;
+};
+
+/* RAII helper class to send observer notifications */
+class AutoSendObserverNotification {
+public:
+ explicit AutoSendObserverNotification(ScriptPrecompiler* aPrecompiler)
+ : mPrecompiler(aPrecompiler)
+ {}
+
+ ~AutoSendObserverNotification() {
+ if (mPrecompiler) {
+ mPrecompiler->SendObserverNotification();
+ }
+ }
+
+ void Disarm() {
+ mPrecompiler = nullptr;
+ }
+
+private:
+ ScriptPrecompiler* mPrecompiler;
+};
+
+NS_IMETHODIMP
+NotifyPrecompilationCompleteRunnable::Run(void)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mPrecompiler);
+
+ AutoSendObserverNotification notifier(mPrecompiler);
+
+ if (mToken) {
+ JSContext* cx = XPCJSContext::Get()->Context();
+ NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+ JS::CancelOffThreadScript(cx, mToken);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptPrecompiler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
+ nsISupports* aContext,
+ uint32_t aDataLength,
+ const uint8_t* aData,
+ uint32_t *aConsumedData)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptPrecompiler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
+ nsISupports* aContext,
+ nsresult aStatus,
+ uint32_t aLength,
+ const uint8_t* aString)
+{
+ AutoSendObserverNotification notifier(this);
+
+ // Just notify that we are done with this load.
+ NS_ENSURE_SUCCESS(aStatus, NS_OK);
+
+ // Convert data to char16_t* and prepare to call CompileOffThread.
+ nsAutoString hintCharset;
+ nsresult rv =
+ nsScriptLoader::ConvertToUTF16(mChannel, aString, aLength,
+ hintCharset, nullptr,
+ mScriptBuf, mScriptLength);
+
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ // Our goal is to cache persistently the compiled script and to avoid quota
+ // checks. Since the caching mechanism decide the persistence type based on
+ // the principal, we create a new global with the app's principal.
+ // We then enter its compartment to compile with its principal.
+ AutoSafeJSContext cx;
+ RootedValue v(cx);
+ SandboxOptions sandboxOptions;
+ sandboxOptions.sandboxName.AssignASCII("asm.js precompilation");
+ sandboxOptions.invisibleToDebugger = true;
+ sandboxOptions.discardSource = true;
+ rv = CreateSandboxObject(cx, &v, mPrincipal, sandboxOptions);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ JSAutoCompartment ac(cx, js::UncheckedUnwrap(&v.toObject()));
+
+ JS::CompileOptions options(cx, JSVERSION_DEFAULT);
+ options.forceAsync = true;
+ options.installedFile = true;
+
+ nsCOMPtr<nsIURI> uri;
+ mChannel->GetURI(getter_AddRefs(uri));
+ nsAutoCString spec;
+ uri->GetSpec(spec);
+ options.setFile(spec.get());
+
+ if (!JS::CanCompileOffThread(cx, options, mScriptLength)) {
+ NS_WARNING("Can't compile script off thread!");
+ return NS_OK;
+ }
+
+ RefPtr<NotifyPrecompilationCompleteRunnable> runnable =
+ new NotifyPrecompilationCompleteRunnable(this);
+
+ if (!JS::CompileOffThread(cx, options,
+ mScriptBuf, mScriptLength,
+ OffThreadCallback,
+ static_cast<void*>(runnable))) {
+ NS_WARNING("Failed to compile script off thread!");
+ return NS_OK;
+ }
+
+ Unused << runnable.forget();
+ notifier.Disarm();
+
+ return NS_OK;
+}
+
+/* static */
+void
+ScriptPrecompiler::OffThreadCallback(void* aToken, void* aData)
+{
+ RefPtr<NotifyPrecompilationCompleteRunnable> runnable =
+ dont_AddRef(static_cast<NotifyPrecompilationCompleteRunnable*>(aData));
+ runnable->SetToken(aToken);
+
+ NS_DispatchToMainThread(runnable);
+}
+
+void
+ScriptPrecompiler::SendObserverNotification()
+{
+ MOZ_ASSERT(mChannel && mObserver);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIURI> uri;
+ mChannel->GetURI(getter_AddRefs(uri));
+ mObserver->Observe(uri, "script-precompiled", nullptr);
+}
+
+NS_IMETHODIMP
+mozJSSubScriptLoader::PrecompileScript(nsIURI* aURI,
+ nsIPrincipal* aPrincipal,
+ nsIObserver* aObserver)
+{
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = NS_NewChannel(getter_AddRefs(channel),
+ aURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<ScriptPrecompiler> loadObserver =
+ new ScriptPrecompiler(aObserver, aPrincipal, channel);
+
+ nsCOMPtr<nsIIncrementalStreamLoader> loader;
+ rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStreamListener> listener = loader.get();
+ rv = channel->AsyncOpen2(listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.h b/js/xpconnect/loader/mozJSSubScriptLoader.h
new file mode 100644
index 000000000..15ed5a14c
--- /dev/null
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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 "nsCOMPtr.h"
+#include "mozIJSSubScriptLoader.h"
+
+class nsIPrincipal;
+class nsIURI;
+class LoadSubScriptOptions;
+
+#define MOZ_JSSUBSCRIPTLOADER_CID \
+{ /* 829814d6-1dd2-11b2-8e08-82fa0a339b00 */ \
+ 0x929814d6, \
+ 0x1dd2, \
+ 0x11b2, \
+ {0x8e, 0x08, 0x82, 0xfa, 0x0a, 0x33, 0x9b, 0x00} \
+}
+
+class nsIIOService;
+
+class mozJSSubScriptLoader : public mozIJSSubScriptLoader
+{
+public:
+ mozJSSubScriptLoader();
+
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZIJSSUBSCRIPTLOADER
+
+private:
+ virtual ~mozJSSubScriptLoader();
+
+ bool ReadScript(nsIURI* uri, JSContext* cx, JSObject* target_obj,
+ const nsAString& charset, const char* uriStr,
+ nsIIOService* serv, nsIPrincipal* principal,
+ bool reuseGlobal, JS::MutableHandleScript script,
+ JS::MutableHandleFunction function);
+
+ nsresult ReadScriptAsync(nsIURI* uri, JSObject* target_obj,
+ const nsAString& charset,
+ nsIIOService* serv, bool reuseGlobal,
+ bool cache, JS::MutableHandleValue retval);
+
+ nsresult DoLoadSubScriptWithOptions(const nsAString& url,
+ LoadSubScriptOptions& options,
+ JSContext* cx,
+ JS::MutableHandle<JS::Value> retval);
+
+ nsCOMPtr<nsIPrincipal> mSystemPrincipal;
+};