--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -112,16 +112,17 @@ const char* mozilla::dom::ContentPrefs::
"javascript.options.strict.debug",
"javascript.options.throw_on_asmjs_validation_failure",
"javascript.options.throw_on_debuggee_would_run",
"javascript.options.wasm",
"javascript.options.wasm_baselinejit",
"javascript.options.wasm_ionjit",
"javascript.options.werror",
"javascript.use_us_english_locale",
+ "jsloader.shareGlobal",
"layout.idle_period.required_quiescent_frames",
"layout.idle_period.time_limit",
"layout.interruptible-reflow.enabled",
"mathml.disabled",
"media.apple.forcevda",
"media.clearkey.persistent-license.enabled",
"media.cubeb.backend",
"media.cubeb.sandbox",
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -196,17 +196,19 @@ ReportOnCallerUTF8(JSCLContextHelper& he
return NS_OK;
}
mozJSComponentLoader::mozJSComponentLoader()
: mModules(16),
mImports(16),
mInProgressImports(16),
mLocations(16),
- mInitialized(false)
+ mInitialized(false),
+ mShareLoaderGlobal(false),
+ mLoaderGlobal(dom::RootingCx())
{
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__));
@@ -294,16 +296,18 @@ mozJSComponentLoader::sSelf;
NS_IMPL_ISUPPORTS(mozJSComponentLoader,
mozilla::ModuleLoader,
xpcIJSModuleLoader,
nsIObserver)
nsresult
mozJSComponentLoader::ReallyInit()
{
+ mShareLoaderGlobal = Preferences::GetBool("jsloader.shareGlobal");
+
nsresult rv;
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);
@@ -423,17 +427,23 @@ mozJSComponentLoader::LoadModule(FileLoc
// The hash owns the ModuleEntry now, forget about it
return entry.forget();
}
void
mozJSComponentLoader::FindTargetObject(JSContext* aCx,
MutableHandleObject aTargetObject)
{
- aTargetObject.set(CurrentGlobalOrNull(aCx));
+ aTargetObject.set(js::GetJSMEnvironmentOfScriptedCaller(aCx));
+
+ // The above could fail if the scripted caller is not a
+ // component/JSM (it could be a DOM scope, for instance).
+ if (!aTargetObject) {
+ aTargetObject.set(CurrentGlobalOrNull(aCx));
+ }
}
// 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)
{
@@ -503,38 +513,98 @@ mozJSComponentLoader::CreateLoaderGlobal
// Set the location information for the new global, so that tools like
// about:memory may use that information
xpc::SetLocationForGlobal(global, aLocation);
aGlobal.set(global);
}
+bool
+mozJSComponentLoader::ReuseGlobal(bool aIsAddon, nsIURI* aURI)
+{
+ if (aIsAddon || !mShareLoaderGlobal)
+ return false;
+
+ nsCString spec;
+ NS_ENSURE_SUCCESS(aURI->GetSpec(spec), false);
+
+ // The loader calls Object.freeze on global properties, which
+ // causes problems if the global is shared with other code.
+ if (spec.EqualsASCII("resource://gre/modules/commonjs/toolkit/loader.js")) {
+ return false;
+ }
+
+ // Various tests call addDebuggerToGlobal on the result of
+ // importing this JSM, which would be annoying to fix.
+ if (spec.EqualsASCII("resource://gre/modules/jsdebugger.jsm")) {
+ return false;
+ }
+
+ // Some SpecialPowers jsms call Cu.forcePermissiveCOWs(),
+ // which sets a per-compartment flag that disables certain
+ // security wrappers, so don't use the shared global for them
+ // to avoid breaking tests.
+ if (FindInReadable(NS_LITERAL_CSTRING("chrome://specialpowers/"), spec)) {
+ return false;
+ }
+
+ return true;
+}
+
JSObject*
mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
nsIFile* aComponentFile,
nsIURI* aURI,
+ bool* aReuseGlobal,
bool* aRealFile)
{
nsAutoCString nativePath;
NS_ENSURE_SUCCESS(aURI->GetSpec(nativePath), nullptr);
+ JSAddonId* addonId = MapURIToAddonID(aURI);
+ bool reuseGlobal = ReuseGlobal(!!addonId, aURI);
+
+ *aReuseGlobal = reuseGlobal;
+
+ bool createdNewGlobal = false;
RootedObject globalObj(aCx);
+ if (reuseGlobal)
+ globalObj = mLoaderGlobal;
- CreateLoaderGlobal(aCx, nativePath, MapURIToAddonID(aURI), &globalObj);
+ if (!globalObj) {
+ nsAutoCString globalLocation;
+ if (reuseGlobal) {
+ globalLocation.AssignLiteral("shared JSM global");
+ } else {
+ globalLocation.Assign(nativePath);
+ }
+
+ CreateLoaderGlobal(aCx, globalLocation,
+ addonId, &globalObj);
+ NS_ENSURE_TRUE(globalObj, nullptr);
+
+ if (reuseGlobal) {
+ mLoaderGlobal = globalObj;
+ }
+
+ createdNewGlobal = true;
+ }
// |thisObj| is the object we set properties on for a particular .jsm.
- // XXX Right now, thisObj is always globalObj, but if we start
- // sharing globals between jsms, they won't be the same.
- // See bug 1186409.
RootedObject thisObj(aCx, globalObj);
NS_ENSURE_TRUE(thisObj, nullptr);
JSAutoCompartment ac(aCx, thisObj);
+ if (reuseGlobal) {
+ thisObj = js::NewJSMEnvironment(aCx);
+ NS_ENSURE_TRUE(thisObj, 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
nsresult rv = NS_OK;
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
nsCOMPtr<nsIFile> testFile;
@@ -562,17 +632,17 @@ mozJSComponentLoader::PrepareObjectForLo
// 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, thisObj, "__URI__", exposedUri, 0))
return nullptr;
- {
+ if (createdNewGlobal) {
// AutoEntryScript required to invoke debugger hook, which is a
// Gecko-specific concept at present.
dom::AutoEntryScript aes(globalObj,
"component loader report global");
JS_FireOnNewGlobalObject(aes.cx(), globalObj);
}
return thisObj;
@@ -591,20 +661,21 @@ mozJSComponentLoader::ObjectForLocation(
dom::AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
bool realFile = false;
nsresult rv = aInfo.EnsureURI();
NS_ENSURE_SUCCESS(rv, rv);
+ bool reuseGlobal = false;
RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aInfo.URI(),
- &realFile));
+ &reuseGlobal, &realFile));
NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
- MOZ_ASSERT(JS_IsGlobalObject(obj));
+ MOZ_ASSERT(JS_IsGlobalObject(obj) == !reuseGlobal);
JSAutoCompartment ac(cx, obj);
RootedScript script(cx);
nsAutoCString nativePath;
rv = aInfo.URI()->GetSpec(nativePath);
NS_ENSURE_SUCCESS(rv, rv);
@@ -657,17 +728,20 @@ mozJSComponentLoader::ObjectForLocation(
if (realFile) {
AutoMemMap map;
MOZ_TRY(map.init(aComponentFile));
// Note: exceptions will get handled further down;
// don't early return for them here.
auto buf = map.get<char>();
- Compile(cx, options, buf.get(), map.size(), &script);
+ if (reuseGlobal)
+ CompileForNonSyntacticScope(cx, options, buf.get(), map.size(), &script);
+ else
+ Compile(cx, options, buf.get(), map.size(), &script);
} 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);
@@ -688,17 +762,20 @@ mozJSComponentLoader::ObjectForLocation(
/* read the file in one swoop */
rv = scriptStream->Read(buf.get(), len, &bytesRead);
if (bytesRead != len)
return NS_BASE_STREAM_OSERROR;
buf[len] = '\0';
- Compile(cx, options, buf.get(), bytesRead, &script);
+ if (reuseGlobal)
+ CompileForNonSyntacticScope(cx, options, buf.get(), bytesRead, &script);
+ else
+ Compile(cx, options, buf.get(), bytesRead, &script);
}
// Propagate the exception, if one exists. Also, don't leave the stale
// exception on this context.
if (!script && aPropagateExceptions && jsapi.HasException()) {
if (!jsapi.StealException(aException))
return NS_ERROR_OUT_OF_MEMORY;
}
}
@@ -731,18 +808,26 @@ mozJSComponentLoader::ObjectForLocation(
{ // Scope for AutoEntryScript
// We're going to run script via JS_ExecuteScript, 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();
- JS::RootedValue rval(cx);
- if (!JS::CloneAndExecuteScript(aescx, script, &rval)) {
+
+ bool executeOk = false;
+ if (JS_IsGlobalObject(obj)) {
+ JS::RootedValue rval(cx);
+ executeOk = JS::CloneAndExecuteScript(aescx, script, &rval);
+ } else {
+ executeOk = js::ExecuteInJSMEnvironment(aescx, script, obj);
+ }
+
+ if (!executeOk) {
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;
@@ -760,16 +845,28 @@ mozJSComponentLoader::ObjectForLocation(
return NS_OK;
}
void
mozJSComponentLoader::UnloadModules()
{
mInitialized = false;
+ if (mLoaderGlobal) {
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ RootedObject global(cx, mLoaderGlobal);
+ JSAutoCompartment ac(cx, global);
+ MOZ_ASSERT(JS_HasExtensibleLexicalEnvironment(global));
+ JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalEnvironment(global));
+ JS_SetAllNonReservedSlotsToUndefined(cx, global);
+ mLoaderGlobal = nullptr;
+ }
+
mInProgressImports.Clear();
mImports.Clear();
mLocations.Clear();
for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
iter.Data()->Clear();
iter.Remove();
}
@@ -1125,16 +1222,19 @@ mozJSComponentLoader::Unload(const nsACS
rv = info.EnsureKey();
NS_ENSURE_SUCCESS(rv, rv);
ModuleEntry* mod;
if (mImports.Get(info.Key(), &mod)) {
mLocations.Remove(mod->resolvedURL);
mImports.Remove(info.Key());
}
+ // If this is the last module to be unloaded, we will leak mLoaderGlobal
+ // until UnloadModules is called. So be it.
+
return NS_OK;
}
NS_IMETHODIMP
mozJSComponentLoader::Observe(nsISupports* subject, const char* topic,
const char16_t* data)
{
if (!strcmp(topic, "xpcom-shutdown-loaders")) {
--- a/js/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/xpconnect/loader/mozJSComponentLoader.h
@@ -67,19 +67,22 @@ class mozJSComponentLoader : public mozi
nsresult ReallyInit();
void UnloadModules();
void CreateLoaderGlobal(JSContext* aCx,
nsACString& aLocation,
JSAddonId* aAddonID,
JS::MutableHandleObject aGlobal);
+ bool ReuseGlobal(bool aIsAddon, nsIURI* aComponent);
+
JSObject* PrepareObjectForLocation(JSContext* aCx,
nsIFile* aComponentFile,
nsIURI* aComponent,
+ bool* aReuseGlobal,
bool* aRealFile);
nsresult ObjectForLocation(ComponentLoaderInfo& aInfo,
nsIFile* aComponentFile,
JS::MutableHandleObject aObject,
JS::MutableHandleScript aTableScript,
char** location,
bool aCatchException,
@@ -163,11 +166,13 @@ class mozJSComponentLoader : public mozi
nsDataHashtable<nsCStringHashKey, ModuleEntry*> mInProgressImports;
// A map of on-disk file locations which are loaded as modules to the
// pre-resolved URIs they were loaded from. Used to prevent the same file
// from being loaded separately, from multiple URLs.
nsClassHashtable<nsCStringHashKey, nsCString> mLocations;
bool mInitialized;
+ bool mShareLoaderGlobal;
+ JS::PersistentRooted<JSObject*> mLoaderGlobal;
};
#endif
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -162,31 +162,43 @@ PrepareScript(nsIURI* uri,
return JS::Compile(cx, options, buf, len, script);
}
return JS::CompileForNonSyntacticScope(cx, options, buf, len, script);
}
static bool
EvalScript(JSContext* cx,
HandleObject targetObj,
+ HandleObject loadScope,
MutableHandleValue retval,
nsIURI* uri,
bool startupCache,
bool preloadCache,
MutableHandleScript script)
{
if (JS_IsGlobalObject(targetObj)) {
if (!JS::CloneAndExecuteScript(cx, script, retval)) {
return false;
}
} else {
JS::AutoObjectVector envChain(cx);
if (!envChain.append(targetObj)) {
return false;
}
+ if (loadScope != targetObj &&
+ loadScope &&
+ !JS_IsGlobalObject(loadScope))
+ {
+ MOZ_DIAGNOSTIC_ASSERT(js::GetObjectCompartment(loadScope) ==
+ js::GetObjectCompartment(targetObj));
+
+ if (!envChain.append(loadScope)) {
+ return false;
+ }
+ }
if (!JS::CloneAndExecuteScript(cx, envChain, script, retval)) {
return false;
}
}
JSAutoCompartment rac(cx, targetObj);
if (!JS_WrapValue(cx, retval)) {
return false;
@@ -236,59 +248,64 @@ class AsyncScriptLoader : public nsIIncr
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader)
AsyncScriptLoader(nsIChannel* aChannel, bool aWantReturnValue,
- JSObject* aTargetObj, const nsAString& aCharset,
- bool aCache, Promise* aPromise)
+ JSObject* aTargetObj, JSObject* aLoadScope,
+ const nsAString& aCharset, bool aCache,
+ Promise* aPromise)
: mChannel(aChannel)
, mTargetObj(aTargetObj)
+ , mLoadScope(aLoadScope)
, mPromise(aPromise)
, mCharset(aCharset)
, mWantReturnValue(aWantReturnValue)
, 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;
+ Heap<JSObject*> mLoadScope;
RefPtr<Promise> mPromise;
nsString mCharset;
bool mWantReturnValue;
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;
+ tmp->mLoadScope = 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_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTargetObj)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLoadScope)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncScriptLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncScriptLoader)
class MOZ_STACK_CLASS AutoRejectPromise
{
public:
@@ -363,37 +380,39 @@ AsyncScriptLoader::OnStreamComplete(nsII
}
RootedScript script(cx);
nsAutoCString spec;
nsresult rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
RootedObject targetObj(cx, mTargetObj);
+ RootedObject loadScope(cx, mLoadScope);
if (!PrepareScript(uri, cx, targetObj, spec.get(), mCharset,
reinterpret_cast<const char*>(aBuf), aLength,
mWantReturnValue, &script))
{
return NS_OK;
}
JS::Rooted<JS::Value> retval(cx);
- if (EvalScript(cx, targetObj, &retval, uri, mCache,
+ if (EvalScript(cx, targetObj, loadScope, &retval, uri, mCache,
mCache && !mWantReturnValue,
&script)) {
autoPromise.ResolvePromise(retval);
}
return NS_OK;
}
nsresult
mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri,
HandleObject targetObj,
+ HandleObject loadScope,
const nsAString& charset,
nsIIOService* serv,
bool wantReturnValue,
bool cache,
MutableHandleValue retval)
{
nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(targetObj);
ErrorResult result;
@@ -430,16 +449,17 @@ mozJSSubScriptLoader::ReadScriptAsync(ns
}
channel->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
RefPtr<AsyncScriptLoader> loadObserver =
new AsyncScriptLoader(channel,
wantReturnValue,
targetObj,
+ loadScope,
charset,
cache,
promise);
nsCOMPtr<nsIIncrementalStreamLoader> loader;
rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver);
NS_ENSURE_SUCCESS(rv, rv);
@@ -548,28 +568,32 @@ mozJSSubScriptLoader::LoadSubScriptWithO
nsresult
mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
LoadSubScriptOptions& options,
JSContext* cx,
MutableHandleValue retval)
{
nsresult rv = NS_OK;
RootedObject targetObj(cx);
- if (options.target) {
+ RootedObject loadScope(cx);
+ mozJSComponentLoader* loader = mozJSComponentLoader::Get();
+ loader->FindTargetObject(cx, &loadScope);
+
+ if (options.target)
targetObj = options.target;
- } else {
- mozJSComponentLoader* loader = mozJSComponentLoader::Get();
- loader->FindTargetObject(cx, &targetObj);
- MOZ_ASSERT(JS_IsGlobalObject(targetObj));
- }
+ else
+ targetObj = loadScope;
targetObj = JS_FindCompilationScope(cx, targetObj);
- if (!targetObj)
+ if (!targetObj || !loadScope)
return NS_ERROR_FAILURE;
+ if (js::GetObjectCompartment(loadScope) != js::GetObjectCompartment(targetObj))
+ loadScope = nullptr;
+
/* 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;
@@ -651,27 +675,27 @@ mozJSSubScriptLoader::DoLoadSubScriptWit
if (NS_FAILED(rv) || !script) {
// 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,
+ return ReadScriptAsync(uri, targetObj, loadScope, options.charset, serv,
options.wantReturnValue, !!cache, retval);
}
if (script) {
// |script| came from the cache, so don't bother writing it
// |back there.
cache = nullptr;
} else if (!ReadScript(uri, cx, targetObj, options.charset,
static_cast<const char*>(uriStr.get()), serv,
options.wantReturnValue, &script)) {
return NS_OK;
}
- Unused << EvalScript(cx, targetObj, retval, uri, !!cache,
+ Unused << EvalScript(cx, targetObj, loadScope, retval, uri, !!cache,
!ignoreCache && !options.wantReturnValue,
&script);
return NS_OK;
}
--- a/js/xpconnect/loader/mozJSSubScriptLoader.h
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.h
@@ -36,16 +36,17 @@ private:
bool ReadScript(nsIURI* uri, JSContext* cx, JS::HandleObject targetObj,
const nsAString& charset, const char* uriStr,
nsIIOService* serv,
bool wantReturnValue,
JS::MutableHandleScript script);
nsresult ReadScriptAsync(nsIURI* uri,
JS::HandleObject targetObj,
+ JS::HandleObject loadScope,
const nsAString& charset,
nsIIOService* serv,
bool wantReturnValue,
bool cache,
JS::MutableHandleValue retval);
nsresult DoLoadSubScriptWithOptions(const nsAString& url,
LoadSubScriptOptions& options,
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5237,16 +5237,22 @@ pref("social.toast-notifications.enabled
pref("dom.idle-observers-api.fuzz_time.disabled", true);
// Minimum delay in milliseconds between network activity notifications (0 means
// no notifications). The delay is the same for both download and upload, though
// they are handled separately. This pref is only read once at startup:
// a restart is required to enable a new value.
pref("network.activity.blipIntervalMilliseconds", 0);
+// If true, reuse the same global for (almost) everything loaded by the component
+// loader (JS components, JSMs, etc). This saves memory, but makes it possible
+// for the scripts to interfere with each other. A restart is required for this
+// to take effect.
+pref("jsloader.shareGlobal", false);
+
// When we're asked to take a screenshot, don't wait more than 2000ms for the
// event loop to become idle before actually taking the screenshot.
pref("dom.browserElement.maxScreenshotDelayMS", 2000);
// Whether we should show the placeholder when the element is focused but empty.
pref("dom.placeholder.show_on_focus", true);
// WebVR is enabled by default in beta and release for Windows and for all