Bug 1646560: Part 2 - Move allowJavascript and friends from DocShell to BrowsingContext and WindowContext. r=jdescottes,nika,geckoview-reviewers,devtools-backward-compat-reviewers,agi

This is slightly complicated by the fact that the editor code wants to be able
to set this from the content process, so we really need separate
BrowsingContext and WindowContext flags, the latter of which can be set by the
owning process.

Differential Revision: https://phabricator.services.mozilla.com/D114899
This commit is contained in:
Kris Maglione 2021-06-15 04:40:11 +00:00
parent b3c34d273e
commit ae436f55ec
36 changed files with 705 additions and 313 deletions

View file

@ -11,7 +11,7 @@
add_task(async function docshell_capabilities() { add_task(async function docshell_capabilities() {
let tab = await createTab(); let tab = await createTab();
let browser = tab.linkedBrowser; let browser = tab.linkedBrowser;
let docShell = browser.docShell; let { browsingContext, docShell } = browser;
// Get the list of capabilities for docShells. // Get the list of capabilities for docShells.
let flags = Object.keys(docShell).filter(k => k.startsWith("allow")); let flags = Object.keys(docShell).filter(k => k.startsWith("allow"));
@ -27,7 +27,7 @@ add_task(async function docshell_capabilities() {
// Flip a couple of allow* flags. // Flip a couple of allow* flags.
docShell.allowImages = false; docShell.allowImages = false;
docShell.allowMetaRedirects = false; docShell.allowMetaRedirects = false;
docShell.allowJavascript = false; browsingContext.allowJavascript = false;
// Now reload the document to ensure that these capabilities // Now reload the document to ensure that these capabilities
// are taken into account. // are taken into account.
@ -68,7 +68,7 @@ add_task(async function docshell_capabilities() {
ok(!docShell.allowMetaRedirects, "meta redirects not allowed"); ok(!docShell.allowMetaRedirects, "meta redirects not allowed");
// Check that docShell allowJavascript flag is not set. // Check that docShell allowJavascript flag is not set.
ok(docShell.allowJavascript, "Javascript still allowed"); ok(browsingContext.allowJavascript, "Javascript still allowed");
// Check that we correctly restored features as disabled. // Check that we correctly restored features as disabled.
state = JSON.parse(ss.getTabState(tab)); state = JSON.parse(ss.getTabState(tab));

View file

@ -85,8 +85,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=840488
is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")"); is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")");
} }
function setScriptEnabledForDocShell(win, enabled) { function setScriptEnabled(win, enabled) {
win.docShell.allowJavascript = enabled; win.browsingContext.allowJavascript = enabled;
} }
function testList(expectEnabled, win, list, idx) { function testList(expectEnabled, win, list, idx) {
@ -138,16 +138,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=840488
// Test simple docshell enable/disable. // Test simple docshell enable/disable.
checkScriptEnabled(rootWin, true); checkScriptEnabled(rootWin, true);
setScriptEnabledForDocShell(rootWin, false); setScriptEnabled(rootWin, false);
checkScriptEnabled(rootWin, false); checkScriptEnabled(rootWin, false);
setScriptEnabledForDocShell(rootWin, true); setScriptEnabled(rootWin, true);
checkScriptEnabled(rootWin, true); checkScriptEnabled(rootWin, true);
// Privileged frames are immune to docshell flags. // Privileged frames are immune to docshell flags.
ok(chromeWin.document.nodePrincipal.isSystemPrincipal, "Sanity check for System Principal"); ok(chromeWin.document.nodePrincipal.isSystemPrincipal, "Sanity check for System Principal");
setScriptEnabledForDocShell(chromeWin, false); setScriptEnabled(chromeWin, false);
checkScriptEnabled(chromeWin, true); checkScriptEnabled(chromeWin, true);
setScriptEnabledForDocShell(chromeWin, true); setScriptEnabled(chromeWin, true);
// Play around with the docshell tree and make sure everything works as // Play around with the docshell tree and make sure everything works as
// we expect. // we expect.
@ -156,32 +156,32 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=840488
return addFrame(rootWin[0], 'childA', true); return addFrame(rootWin[0], 'childA', true);
}).then(function() { }).then(function() {
checkScriptEnabled(rootWin[0][0], true); checkScriptEnabled(rootWin[0][0], true);
setScriptEnabledForDocShell(rootWin[0], false); setScriptEnabled(rootWin[0], false);
checkScriptEnabled(rootWin, true); checkScriptEnabled(rootWin, true);
checkScriptEnabled(rootWin[0], false); checkScriptEnabled(rootWin[0], false);
checkScriptEnabled(rootWin[0][0], false); checkScriptEnabled(rootWin[0][0], false);
return addFrame(rootWin[0], 'childB', false); return addFrame(rootWin[0], 'childB', false);
}).then(function() { }).then(function() {
checkScriptEnabled(rootWin[0][1], false); checkScriptEnabled(rootWin[0][1], false);
setScriptEnabledForDocShell(rootWin[0][0], false); setScriptEnabled(rootWin[0][0], false);
setScriptEnabledForDocShell(rootWin[0], true); setScriptEnabled(rootWin[0], true);
checkScriptEnabled(rootWin[0], true); checkScriptEnabled(rootWin[0], true);
checkScriptEnabled(rootWin[0][0], false); checkScriptEnabled(rootWin[0][0], false);
setScriptEnabledForDocShell(rootWin[0][0], true); setScriptEnabled(rootWin[0][0], true);
// Flags are inherited from the parent docshell at attach time. Note that // Flags are inherited from the parent docshell at attach time. Note that
// the flag itself is inherited, regardless of whether or not scripts are // the flag itself is inherited, regardless of whether or not scripts are
// currently allowed on the parent (which could depend on the parent's // currently allowed on the parent (which could depend on the parent's
// parent). Check that. // parent). Check that.
checkScriptEnabled(rootWin[0][1], false); checkScriptEnabled(rootWin[0][1], false);
setScriptEnabledForDocShell(rootWin[0], false); setScriptEnabled(rootWin[0], false);
setScriptEnabledForDocShell(rootWin[0][1], true); setScriptEnabled(rootWin[0][1], true);
return addFrame(rootWin[0][1], 'grandchild', false); return addFrame(rootWin[0][1], 'grandchild', false);
}).then(function() { }).then(function() {
checkScriptEnabled(rootWin[0], false); checkScriptEnabled(rootWin[0], false);
checkScriptEnabled(rootWin[0][1], false); checkScriptEnabled(rootWin[0][1], false);
checkScriptEnabled(rootWin[0][1][0], false); checkScriptEnabled(rootWin[0][1][0], false);
setScriptEnabledForDocShell(rootWin[0], true); setScriptEnabled(rootWin[0], true);
checkScriptEnabled(rootWin[0], true); checkScriptEnabled(rootWin[0], true);
checkScriptEnabled(rootWin[0][1], true); checkScriptEnabled(rootWin[0][1], true);
checkScriptEnabled(rootWin[0][1][0], true); checkScriptEnabled(rootWin[0][1][0], true);
@ -195,8 +195,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=840488
.then(function() { .then(function() {
checkScriptEnabled(rootWin[0][0], true); checkScriptEnabled(rootWin[0][0], true);
checkScriptEnabled(rootWin[0][1][0], true); checkScriptEnabled(rootWin[0][1][0], true);
setScriptEnabledForDocShell(rootWin[0][0], false); setScriptEnabled(rootWin[0][0], false);
setScriptEnabledForDocShell(rootWin[0][1], false); setScriptEnabled(rootWin[0][1], false);
checkScriptEnabled(rootWin[0][0], false); checkScriptEnabled(rootWin[0][0], false);
checkScriptEnabled(rootWin[0][1][0], false); checkScriptEnabled(rootWin[0][1][0], false);
return navigateBack(rootWin[0][0].frameElement); return navigateBack(rootWin[0][0].frameElement);

View file

@ -34,8 +34,8 @@ add_task(async function() {
async function testJSEnabled() { async function testJSEnabled() {
info("Testing that JS is enabled"); info("Testing that JS is enabled");
// We use waitForTick here because switching docShell.allowJavascript to true // We use waitForTick here because switching browsingContext.allowJavascript
// takes a while to become live. // to true takes a while to become live.
await waitForTick(); await waitForTick();
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() { await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {

View file

@ -605,10 +605,10 @@ OptionsPanel.prototype = {
/** /**
* Disables JavaScript for the currently loaded tab. We force a page refresh * Disables JavaScript for the currently loaded tab. We force a page refresh
* here because setting docShell.allowJavascript to true fails to block JS * here because setting browsingContext.allowJavascript to true fails to block
* execution from event listeners added using addEventListener(), AJAX calls * JS execution from event listeners added using addEventListener(), AJAX
* and timers. The page refresh prevents these things from being added in the * calls and timers. The page refresh prevents these things from being added
* first place. * in the first place.
* *
* @param {Event} event * @param {Event} event
* The event sent by checking / unchecking the disable JS checkbox. * The event sent by checking / unchecking the disable JS checkbox.

View file

@ -2115,14 +2115,18 @@ Toolbox.prototype = {
}, },
/** /**
* Read the initial javascriptEnabled configuration from the current target * If we have an older version of the server which handles `javascriptEnabled`
* and forward it to the configuration actor. * in the browsing-context target, read the initial javascriptEnabled
* configuration from the current target and forward it to the configuration
* actor.
*/ */
_applyJavascriptEnabledSettings: function() { _applyJavascriptEnabledSettings: function() {
const javascriptEnabled = this.target._javascriptEnabled; if (this.target.traits.javascriptEnabled) {
this.commands.targetConfigurationCommand.updateConfiguration({ const javascriptEnabled = this.target._javascriptEnabled;
javascriptEnabled, this.commands.targetConfigurationCommand.updateConfiguration({
}); javascriptEnabled,
});
}
}, },
/** /**

View file

@ -20,9 +20,8 @@ class BrowsingContextTargetFront extends TargetMixin(
// For targets which support the Watcher and configuration actor, the status // For targets which support the Watcher and configuration actor, the status
// for the `javascriptEnabled` setting will be available on the configuration // for the `javascriptEnabled` setting will be available on the configuration
// front, and the target will only be used to read the initial value. // front, and the target will only be used to read the initial value from older
// For other targets, _javascriptEnabled will be updated everytime // servers.
// `reconfigure` is called.
// Note: this property is marked as private but is accessed by the // Note: this property is marked as private but is accessed by the
// TargetCommand to provide the "isJavascriptEnabled" wrapper. It should NOT be // TargetCommand to provide the "isJavascriptEnabled" wrapper. It should NOT be
// used anywhere else. // used anywhere else.
@ -121,9 +120,13 @@ class BrowsingContextTargetFront extends TargetMixin(
const response = await super.attach(); const response = await super.attach();
this.targetForm.threadActor = response.threadActor; this.targetForm.threadActor = response.threadActor;
this._javascriptEnabled = response.javascriptEnabled;
this.traits = response.traits || {}; this.traits = response.traits || {};
if (response.javascriptEnabled != null) {
this.traits.javascriptEnabled = true;
this._javascriptEnabled = response.javascriptEnabled;
}
// xpcshell tests from devtools/server/tests/xpcshell/ are implementing // xpcshell tests from devtools/server/tests/xpcshell/ are implementing
// fake BrowsingContextTargetActor which do not expose any console actor. // fake BrowsingContextTargetActor which do not expose any console actor.
if (this.targetForm.consoleActor) { if (this.targetForm.consoleActor) {
@ -133,16 +136,6 @@ class BrowsingContextTargetFront extends TargetMixin(
return this._attach; return this._attach;
} }
async reconfigure({ options }) {
const response = await super.reconfigure({ options });
if (typeof options.javascriptEnabled != "undefined") {
this._javascriptEnabled = options.javascriptEnabled;
}
return response;
}
async detach() { async detach() {
// When calling this.destroy() at the end of this method, // When calling this.destroy() at the end of this method,
// we will end up calling detach again from TargetMixin.destroy. // we will end up calling detach again from TargetMixin.destroy.

View file

@ -193,6 +193,17 @@ const TargetConfigurationActor = ActorClassWithSpec(targetConfigurationSpec, {
case "customUserAgent": case "customUserAgent":
this._setCustomUserAgent(value); this._setCustomUserAgent(value);
break; break;
case "javascriptEnabled":
if (value !== undefined) {
const reload = value != this.isJavascriptEnabled();
this._setJavascriptEnabled(value);
// This flag requires a reload in order to take full effect,
// so reload if it has changed.
if (reload) {
this._browsingContext.reload(0);
}
}
break;
case "overrideDPPX": case "overrideDPPX":
this._setDPPXOverride(value); this._setDPPXOverride(value);
break; break;
@ -243,6 +254,10 @@ const TargetConfigurationActor = ActorClassWithSpec(targetConfigurationSpec, {
this._setDPPXOverride(this._initialDPPXOverride); this._setDPPXOverride(this._initialDPPXOverride);
} }
if (this._initialJavascriptEnabled !== undefined) {
this._setJavascriptEnabled(this._initialJavascriptEnabled);
}
if (this._initialTouchEventsOverride !== undefined) { if (this._initialTouchEventsOverride !== undefined) {
this._setTouchEventsOverride(this._initialTouchEventsOverride); this._setTouchEventsOverride(this._initialTouchEventsOverride);
} }
@ -297,6 +312,18 @@ const TargetConfigurationActor = ActorClassWithSpec(targetConfigurationSpec, {
this._browsingContext.customUserAgent = userAgent; this._browsingContext.customUserAgent = userAgent;
}, },
isJavascriptEnabled() {
return this._browsingContext.allowJavascript;
},
_setJavascriptEnabled(allow) {
if (this._initialJavascriptEnabled === undefined) {
this._initialJavascriptEnabled = this._browsingContext.allowJavascript;
}
if (allow !== undefined) {
this._browsingContext.allowJavascript = allow;
}
},
/* DPPX override */ /* DPPX override */
_setDPPXOverride(dppx) { _setDPPXOverride(dppx) {
if (this._browsingContext.overrideDPPX === dppx) { if (this._browsingContext.overrideDPPX === dppx) {

View file

@ -1090,7 +1090,6 @@ const browsingContextTargetPrototype = {
return { return {
threadActor: this.threadActor.actorID, threadActor: this.threadActor.actorID,
cacheDisabled: this._getCacheDisabled(), cacheDisabled: this._getCacheDisabled(),
javascriptEnabled: this._getJavascriptEnabled(),
traits: this.traits, traits: this.traits,
}; };
}, },
@ -1278,14 +1277,6 @@ const browsingContextTargetPrototype = {
// propagated through the browsing context tree via the platform. // propagated through the browsing context tree via the platform.
return; return;
} }
if (
typeof options.javascriptEnabled !== "undefined" &&
options.javascriptEnabled !== this._getJavascriptEnabled()
) {
this._setJavascriptEnabled(options.javascriptEnabled);
reload = true;
}
if ( if (
typeof options.cacheDisabled !== "undefined" && typeof options.cacheDisabled !== "undefined" &&
options.cacheDisabled !== this._getCacheDisabled() options.cacheDisabled !== this._getCacheDisabled()
@ -1320,7 +1311,6 @@ const browsingContextTargetPrototype = {
* state when closing the toolbox. * state when closing the toolbox.
*/ */
_restoreTargetConfiguration() { _restoreTargetConfiguration() {
this._restoreJavascript();
this._setCacheDisabled(false); this._setCacheDisabled(false);
this._setPaintFlashingEnabled(false); this._setPaintFlashingEnabled(false);
@ -1339,39 +1329,6 @@ const browsingContextTargetPrototype = {
this.docShell.defaultLoadFlags = disabled ? disable : enable; this.docShell.defaultLoadFlags = disabled ? disable : enable;
}, },
/**
* Disable or enable JS via docShell.
*/
_wasJavascriptEnabled: null,
_setJavascriptEnabled(allow) {
if (this._wasJavascriptEnabled === null) {
this._wasJavascriptEnabled = this.docShell.allowJavascript;
}
this.docShell.allowJavascript = allow;
},
/**
* Restore JS state, before the actor modified it.
*/
_restoreJavascript() {
if (this._wasJavascriptEnabled !== null) {
this._setJavascriptEnabled(this._wasJavascriptEnabled);
this._wasJavascriptEnabled = null;
}
},
/**
* Return JS allowed status.
*/
_getJavascriptEnabled() {
if (!this.docShell) {
// The browsing context is already closed.
return null;
}
return this.docShell.allowJavascript;
},
/** /**
* Disable or enable the paint flashing on the target. * Disable or enable the paint flashing on the target.
*/ */

View file

@ -61,20 +61,23 @@ class TargetConfigurationCommand {
} }
async isJavascriptEnabled() { async isJavascriptEnabled() {
if ( if (this._hasTargetWatcherSupport()) {
this._hasTargetWatcherSupport() && const front = await this.getFront();
// `javascriptEnabled` is first read by the target and then forwarded by return front.isJavascriptEnabled();
// the toolbox to the TargetConfigurationCommand, so it might be undefined at this
// point.
typeof this.configuration.javascriptEnabled !== "undefined"
) {
return this.configuration.javascriptEnabled;
} }
// If the TargetConfigurationActor does not know the value yet, or if the target don't // If the TargetConfigurationActor does not know the value yet, or if the target don't
// support the Watcher + configuration actor, fallback on the initial value cached by // support the Watcher + configuration actor, and we have an old version of the server
// the target front. // which handles `javascriptEnabled` in the target, fallback on the initial value
return this._commands.targetCommand.targetFront._javascriptEnabled; // cached by the target front.
const { targetFront } = this._commands.targetCommand;
if (targetFront.traits.javascriptEnabled) {
return targetFront._javascriptEnabled;
}
// If we don't have target watcher support, we can't get this value, so just fall back
// to the default.
return true;
} }
/** /**

View file

@ -38,6 +38,12 @@ const targetConfigurationSpec = generateActorSpec({
configuration: RetVal("target-configuration.configuration"), configuration: RetVal("target-configuration.configuration"),
}, },
}, },
isJavascriptEnabled: {
request: {},
response: {
javascriptEnabled: RetVal("boolean"),
},
},
}, },
}); });

View file

@ -14,7 +14,7 @@ const {
types.addDictType("browsingContextTarget.attach", { types.addDictType("browsingContextTarget.attach", {
threadActor: "number", threadActor: "number",
cacheDisabled: "boolean", cacheDisabled: "boolean",
javascriptEnabled: "boolean", javascriptEnabled: "nullable:boolean",
traits: "json", traits: "json",
}); });
@ -46,7 +46,6 @@ types.addDictType("browsingContextTarget.reload", {
types.addDictType("browsingContextTarget.reconfigure", { types.addDictType("browsingContextTarget.reconfigure", {
cacheDisabled: "nullable:boolean", cacheDisabled: "nullable:boolean",
colorSchemeSimulation: "nullable:string", colorSchemeSimulation: "nullable:string",
javascriptEnabled: "nullable:boolean",
paintFlashing: "nullable:boolean", paintFlashing: "nullable:boolean",
printSimulationEnabled: "nullable:boolean", printSimulationEnabled: "nullable:boolean",
restoreFocus: "nullable:boolean", restoreFocus: "nullable:boolean",

View file

@ -406,6 +406,8 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
fields.mTouchEventsOverrideInternal = TouchEventsOverride::None; fields.mTouchEventsOverrideInternal = TouchEventsOverride::None;
fields.mAllowJavascript = inherit ? inherit->GetAllowJavascript() : true;
RefPtr<BrowsingContext> context; RefPtr<BrowsingContext> context;
if (XRE_IsParentProcess()) { if (XRE_IsParentProcess()) {
context = new CanonicalBrowsingContext(parentWC, group, id, context = new CanonicalBrowsingContext(parentWC, group, id,
@ -538,6 +540,7 @@ BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
mUseRemoteSubframes(false), mUseRemoteSubframes(false),
mCreatedDynamically(false), mCreatedDynamically(false),
mIsInBFCache(false), mIsInBFCache(false),
mCanExecuteScripts(true),
mChildOffset(0) { mChildOffset(0) {
MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup); MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup);
MOZ_RELEASE_ASSERT(mBrowsingContextId != 0); MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
@ -742,6 +745,7 @@ void BrowsingContext::Attach(bool aFromIPC, ContentParent* aOriginProcess) {
mChildOffset = mChildOffset =
mCreatedDynamically ? -1 : mParentWindow->Children().Length(); mCreatedDynamically ? -1 : mParentWindow->Children().Length();
mParentWindow->AppendChildBrowsingContext(this); mParentWindow->AppendChildBrowsingContext(this);
RecomputeCanExecuteScripts();
} else { } else {
mGroup->Toplevels().AppendElement(this); mGroup->Toplevels().AppendElement(this);
} }
@ -2628,6 +2632,43 @@ void BrowsingContext::DidSet(FieldIndex<IDX_HasMainMediaController>,
Group()->UpdateToplevelsSuspendedIfNeeded(); Group()->UpdateToplevelsSuspendedIfNeeded();
} }
auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
ContentParent* aSource) -> CanSetResult {
if (mozilla::SessionHistoryInParent()) {
return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow : CanSetResult::Deny;
}
// Without Session History in Parent, session restore code still needs to set
// this from content processes.
return LegacyRevertIfNotOwningOrParentProcess(aSource);
}
void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
RecomputeCanExecuteScripts();
}
void BrowsingContext::RecomputeCanExecuteScripts() {
const bool old = mCanExecuteScripts;
if (!AllowJavascript()) {
// Scripting has been explicitly disabled on our BrowsingContext.
mCanExecuteScripts = false;
} else if (GetParentWindowContext()) {
// Otherwise, inherit parent.
mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts();
} else {
// Otherwise, we're the root of the tree, and we haven't explicitly disabled
// script. Allow.
mCanExecuteScripts = true;
}
if (old != mCanExecuteScripts) {
for (WindowContext* wc : GetWindowContexts()) {
wc->RecomputeCanExecuteScripts();
}
}
}
bool BrowsingContext::InactiveForSuspend() const { bool BrowsingContext::InactiveForSuspend() const {
if (!StaticPrefs::dom_suspend_inactive_enabled()) { if (!StaticPrefs::dom_suspend_inactive_enabled()) {
return false; return false;

View file

@ -200,7 +200,10 @@ enum class ExplicitActiveStatus : uint8_t {
/* Don't use the getter of the field, but IsInBFCache() method */ \ /* Don't use the getter of the field, but IsInBFCache() method */ \
FIELD(IsInBFCache, bool) \ FIELD(IsInBFCache, bool) \
FIELD(HasRestoreData, bool) \ FIELD(HasRestoreData, bool) \
FIELD(SessionStoreEpoch, uint32_t) FIELD(SessionStoreEpoch, uint32_t) \
/* Whether we can execute scripts in this BrowsingContext. Has no effect \
* unless scripts are also allowed in the parent WindowContext. */ \
FIELD(AllowJavascript, bool)
// BrowsingContext, in this context, is the cross process replicated // BrowsingContext, in this context, is the cross process replicated
// environment in which information about documents is stored. In // environment in which information about documents is stored. In
@ -851,6 +854,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
bool IsInBFCache() const { return mIsInBFCache; } bool IsInBFCache() const { return mIsInBFCache; }
bool AllowJavascript() const { return GetAllowJavascript(); }
bool CanExecuteScripts() const { return mCanExecuteScripts; }
protected: protected:
virtual ~BrowsingContext(); virtual ~BrowsingContext();
BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup, BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup,
@ -865,6 +871,12 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
private: private:
void Attach(bool aFromIPC, ContentParent* aOriginProcess); void Attach(bool aFromIPC, ContentParent* aOriginProcess);
// Recomputes whether we can execute scripts in this BrowsingContext based on
// the value of AllowJavascript() and whether scripts are allowed in the
// parent WindowContext. Called whenever the AllowJavascript() flag or the
// parent WC changes.
void RecomputeCanExecuteScripts();
// Find the special browsing context if aName is '_self', '_parent', // Find the special browsing context if aName is '_self', '_parent',
// '_top', but not '_blank'. The latter is handled in FindWithName // '_top', but not '_blank'. The latter is handled in FindWithName
BrowsingContext* FindWithSpecialName(const nsAString& aName, BrowsingContext* FindWithSpecialName(const nsAString& aName,
@ -1069,6 +1081,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
ContentParent* aSource); ContentParent* aSource);
void DidSet(FieldIndex<IDX_HasMainMediaController>, bool aOldValue); void DidSet(FieldIndex<IDX_HasMainMediaController>, bool aOldValue);
CanSetResult CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
ContentParent* aSource);
void DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue);
bool CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue, bool CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
ContentParent* aSource); ContentParent* aSource);
@ -1185,6 +1201,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
// before dispatching pageshow. // before dispatching pageshow.
bool mIsInBFCache : 1; bool mIsInBFCache : 1;
// Determines if we can execute scripts in this BrowsingContext. True if
// AllowJavascript() is true and script execution is allowed in the parent
// WindowContext.
bool mCanExecuteScripts : 1;
// The original offset of this context in its container. This property is -1 // The original offset of this context in its container. This property is -1
// if this BrowsingContext is for a frame that was added dynamically. // if this BrowsingContext is for a frame that was added dynamically.
int32_t mChildOffset; int32_t mChildOffset;

View file

@ -262,6 +262,44 @@ bool WindowContext::CanSet(FieldIndex<IDX_HadLazyLoadImage>, const bool& aValue,
return IsTop(); return IsTop();
} }
bool WindowContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
ContentParent* aSource) {
return (XRE_IsParentProcess() && !aSource) || CheckOnlyOwningProcessCanSet(aSource);
}
void WindowContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
RecomputeCanExecuteScripts();
}
void WindowContext::RecomputeCanExecuteScripts(bool aApplyChanges) {
const bool old = mCanExecuteScripts;
if (!AllowJavascript()) {
// Scripting has been explicitly disabled on our WindowContext.
mCanExecuteScripts = false;
} else {
// Otherwise, inherit.
mCanExecuteScripts = mBrowsingContext->CanExecuteScripts();
}
if (aApplyChanges && old != mCanExecuteScripts) {
// Inform our active DOM window.
if (nsGlobalWindowInner* window = GetInnerWindow()) {
// Only update scriptability if the window is current. Windows will have
// scriptability disabled when entering the bfcache and updated when
// coming out.
if (window->IsCurrentInnerWindow()) {
auto& scriptability = xpc::Scriptability::Get(
window->GetGlobalJSObject());
scriptability.SetWindowAllowsScript(mCanExecuteScripts);
}
}
for (const RefPtr<BrowsingContext>& child : Children()) {
child->RecomputeCanExecuteScripts();
}
}
}
void WindowContext::DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>, void WindowContext::DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>,
bool aOldValue) { bool aOldValue) {
MOZ_ASSERT( MOZ_ASSERT(
@ -473,6 +511,7 @@ WindowContext::WindowContext(BrowsingContext* aBrowsingContext,
MOZ_ASSERT(mBrowsingContext); MOZ_ASSERT(mBrowsingContext);
MOZ_ASSERT(mInnerWindowId); MOZ_ASSERT(mInnerWindowId);
MOZ_ASSERT(mOuterWindowId); MOZ_ASSERT(mOuterWindowId);
RecomputeCanExecuteScripts(/* aApplyChanges */ false);
} }
WindowContext::~WindowContext() { WindowContext::~WindowContext() {

View file

@ -31,62 +31,65 @@ class WindowGlobalInit;
class BrowsingContext; class BrowsingContext;
class BrowsingContextGroup; class BrowsingContextGroup;
#define MOZ_EACH_WC_FIELD(FIELD) \ #define MOZ_EACH_WC_FIELD(FIELD) \
/* Whether the SHEntry associated with the current top-level \ /* Whether the SHEntry associated with the current top-level \
* window has already seen user interaction. \ * window has already seen user interaction. \
* As such, this will be reset to false when a new SHEntry is \ * As such, this will be reset to false when a new SHEntry is \
* created without changing the WC (e.g. when using pushState or \ * created without changing the WC (e.g. when using pushState or \
* sub-frame navigation) \ * sub-frame navigation) \
* This flag is set for optimization purposes, to avoid \ * This flag is set for optimization purposes, to avoid \
* having to get the top SHEntry and update it on every \ * having to get the top SHEntry and update it on every \
* user interaction. \ * user interaction. \
* This is only meaningful on the top-level WC. */ \ * This is only meaningful on the top-level WC. */ \
FIELD(SHEntryHasUserInteraction, bool) \ FIELD(SHEntryHasUserInteraction, bool) \
FIELD(CookieBehavior, Maybe<uint32_t>) \ FIELD(CookieBehavior, Maybe<uint32_t>) \
FIELD(IsOnContentBlockingAllowList, bool) \ FIELD(IsOnContentBlockingAllowList, bool) \
/* Whether the given window hierarchy is third party. See \ /* Whether the given window hierarchy is third party. See \
* ThirdPartyUtil::IsThirdPartyWindow for details */ \ * ThirdPartyUtil::IsThirdPartyWindow for details */ \
FIELD(IsThirdPartyWindow, bool) \ FIELD(IsThirdPartyWindow, bool) \
/* Whether this window's channel has been marked as a third-party \ /* Whether this window's channel has been marked as a third-party \
* tracking resource */ \ * tracking resource */ \
FIELD(IsThirdPartyTrackingResourceWindow, bool) \ FIELD(IsThirdPartyTrackingResourceWindow, bool) \
FIELD(IsSecureContext, bool) \ FIELD(IsSecureContext, bool) \
FIELD(IsOriginalFrameSource, bool) \ FIELD(IsOriginalFrameSource, bool) \
/* Mixed-Content: If the corresponding documentURI is https, \ /* Mixed-Content: If the corresponding documentURI is https, \
* then this flag is true. */ \ * then this flag is true. */ \
FIELD(IsSecure, bool) \ FIELD(IsSecure, bool) \
/* Whether the user has overriden the mixed content blocker to allow \ /* Whether the user has overriden the mixed content blocker to allow \
* mixed content loads to happen */ \ * mixed content loads to happen */ \
FIELD(AllowMixedContent, bool) \ FIELD(AllowMixedContent, bool) \
/* Whether this window has registered a "beforeunload" event \ /* Whether this window has registered a "beforeunload" event \
* handler */ \ * handler */ \
FIELD(HasBeforeUnload, bool) \ FIELD(HasBeforeUnload, bool) \
/* Controls whether the WindowContext is currently considered to be \ /* Controls whether the WindowContext is currently considered to be \
* activated by a gesture */ \ * activated by a gesture */ \
FIELD(UserActivationState, UserActivation::State) \ FIELD(UserActivationState, UserActivation::State) \
FIELD(EmbedderPolicy, nsILoadInfo::CrossOriginEmbedderPolicy) \ FIELD(EmbedderPolicy, nsILoadInfo::CrossOriginEmbedderPolicy) \
/* True if this document tree contained at least a HTMLMediaElement. \ /* True if this document tree contained at least a HTMLMediaElement. \
* This should only be set on top level context. */ \ * This should only be set on top level context. */ \
FIELD(DocTreeHadMedia, bool) \ FIELD(DocTreeHadMedia, bool) \
FIELD(AutoplayPermission, uint32_t) \ FIELD(AutoplayPermission, uint32_t) \
FIELD(ShortcutsPermission, uint32_t) \ FIELD(ShortcutsPermission, uint32_t) \
/* Store the Id of the browsing context where active media session \ /* Store the Id of the browsing context where active media session \
* exists on the top level window context */ \ * exists on the top level window context */ \
FIELD(ActiveMediaSessionContextId, Maybe<uint64_t>) \ FIELD(ActiveMediaSessionContextId, Maybe<uint64_t>) \
/* ALLOW_ACTION if it is allowed to open popups for the sub-tree \ /* ALLOW_ACTION if it is allowed to open popups for the sub-tree \
* starting and including the current WindowContext */ \ * starting and including the current WindowContext */ \
FIELD(PopupPermission, uint32_t) \ FIELD(PopupPermission, uint32_t) \
FIELD(DelegatedPermissions, \ FIELD(DelegatedPermissions, \
PermissionDelegateHandler::DelegatedPermissionList) \ PermissionDelegateHandler::DelegatedPermissionList) \
FIELD(DelegatedExactHostMatchPermissions, \ FIELD(DelegatedExactHostMatchPermissions, \
PermissionDelegateHandler::DelegatedPermissionList) \ PermissionDelegateHandler::DelegatedPermissionList) \
FIELD(HasReportedShadowDOMUsage, bool) \ FIELD(HasReportedShadowDOMUsage, bool) \
/* Whether the principal of this window is for a local \ /* Whether the principal of this window is for a local \
* IP address */ \ * IP address */ \
FIELD(IsLocalIP, bool) \ FIELD(IsLocalIP, bool) \
/* Whether the corresponding document has `loading='lazy'` \ /* Whether the corresponding document has `loading='lazy'` \
* images; It won't become false if the image becomes non-lazy */ \ * images; It won't become false if the image becomes non-lazy */ \
FIELD(HadLazyLoadImage, bool) FIELD(HadLazyLoadImage, bool) \
/* Whether we can execute scripts in this WindowContext. Has no effect \
* unless scripts are also allowed in the BrowsingContext. */ \
FIELD(AllowJavascript, bool)
class WindowContext : public nsISupports, public nsWrapperCache { class WindowContext : public nsISupports, public nsWrapperCache {
MOZ_DECL_SYNCED_CONTEXT(WindowContext, MOZ_EACH_WC_FIELD) MOZ_DECL_SYNCED_CONTEXT(WindowContext, MOZ_EACH_WC_FIELD)
@ -182,6 +185,9 @@ class WindowContext : public nsISupports, public nsWrapperCache {
bool HadLazyLoadImage() const { return GetHadLazyLoadImage(); } bool HadLazyLoadImage() const { return GetHadLazyLoadImage(); }
bool AllowJavascript() const { return GetAllowJavascript(); }
bool CanExecuteScripts() const { return mCanExecuteScripts; }
protected: protected:
WindowContext(BrowsingContext* aBrowsingContext, uint64_t aInnerWindowId, WindowContext(BrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
uint64_t aOuterWindowId, FieldValues&& aFields); uint64_t aOuterWindowId, FieldValues&& aFields);
@ -271,6 +277,10 @@ class WindowContext : public nsISupports, public nsWrapperCache {
bool CanSet(FieldIndex<IDX_HadLazyLoadImage>, const bool& aValue, bool CanSet(FieldIndex<IDX_HadLazyLoadImage>, const bool& aValue,
ContentParent* aSource); ContentParent* aSource);
bool CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
ContentParent* aSource);
void DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue);
void DidSet(FieldIndex<IDX_HasReportedShadowDOMUsage>, bool aOldValue); void DidSet(FieldIndex<IDX_HasReportedShadowDOMUsage>, bool aOldValue);
void DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>, bool aOldValue); void DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>, bool aOldValue);
@ -284,6 +294,11 @@ class WindowContext : public nsISupports, public nsWrapperCache {
void DidSet(FieldIndex<I>, T&& aOldValue) {} void DidSet(FieldIndex<I>, T&& aOldValue) {}
void DidSet(FieldIndex<IDX_UserActivationState>); void DidSet(FieldIndex<IDX_UserActivationState>);
// Recomputes whether we can execute scripts in this WindowContext based on
// the value of AllowJavascript() and whether scripts are allowed in the
// BrowsingContext.
void RecomputeCanExecuteScripts(bool aApplyChanges = true);
const uint64_t mInnerWindowId; const uint64_t mInnerWindowId;
const uint64_t mOuterWindowId; const uint64_t mOuterWindowId;
RefPtr<BrowsingContext> mBrowsingContext; RefPtr<BrowsingContext> mBrowsingContext;
@ -298,6 +313,11 @@ class WindowContext : public nsISupports, public nsWrapperCache {
bool mIsDiscarded = false; bool mIsDiscarded = false;
bool mIsInProcess = false; bool mIsInProcess = false;
// Determines if we can execute scripts in this WindowContext. True if
// AllowJavascript() is true and script execution is allowed in the
// BrowsingContext.
bool mCanExecuteScripts = true;
// The start time of user gesture, this is only available if the window // The start time of user gesture, this is only available if the window
// context is in process. // context is in process.
TimeStamp mUserGestureStart; TimeStamp mUserGestureStart;

View file

@ -393,7 +393,6 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
#endif #endif
mInitialized(false), mInitialized(false),
mAllowSubframes(true), mAllowSubframes(true),
mAllowJavascript(true),
mAllowMetaRedirects(true), mAllowMetaRedirects(true),
mAllowImages(true), mAllowImages(true),
mAllowMedia(true), mAllowMedia(true),
@ -407,7 +406,6 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mDeviceSizeIsPageSize(false), mDeviceSizeIsPageSize(false),
mWindowDraggingAllowed(false), mWindowDraggingAllowed(false),
mInFrameSwap(false), mInFrameSwap(false),
mCanExecuteScripts(false),
mFiredUnloadEvent(false), mFiredUnloadEvent(false),
mEODForCurrentDocument(false), mEODForCurrentDocument(false),
mURIResultedInDocument(false), mURIResultedInDocument(false),
@ -1749,14 +1747,6 @@ nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
return mBrowsingContext->SetAllowPlugins(aAllowPlugins); return mBrowsingContext->SetAllowPlugins(aAllowPlugins);
} }
NS_IMETHODIMP
nsDocShell::GetAllowJavascript(bool* aAllowJavascript) {
NS_ENSURE_ARG_POINTER(aAllowJavascript);
*aAllowJavascript = mAllowJavascript;
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) { nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
MOZ_ASSERT(aEnabled); MOZ_ASSERT(aEnabled);
@ -1770,13 +1760,6 @@ nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsDocShell::SetAllowJavascript(bool aAllowJavascript) {
mAllowJavascript = aAllowJavascript;
RecomputeCanExecuteScripts();
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) { nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing); NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
@ -2653,50 +2636,6 @@ Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
return innerWindow->GetClientInfo(); return innerWindow->GetClientInfo();
} }
void nsDocShell::RecomputeCanExecuteScripts() {
bool old = mCanExecuteScripts;
RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
// If we have no tree owner, that means that we've been detached from the
// docshell tree (this is distinct from having no parent docshell, which
// is the case for root docshells). It would be nice to simply disallow
// script in detached docshells, but bug 986542 demonstrates that this
// behavior breaks at least one website.
//
// So instead, we use our previous value, unless mAllowJavascript has been
// explicitly set to false.
if (!mTreeOwner) {
mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
// If scripting has been explicitly disabled on our docshell, we're done.
} else if (!mAllowJavascript) {
mCanExecuteScripts = false;
// If we have a parent, inherit.
} else if (parent) {
mCanExecuteScripts = parent->mCanExecuteScripts;
// Otherwise, we're the root of the tree, and we haven't explicitly disabled
// script. Allow.
} else {
mCanExecuteScripts = true;
}
// Inform our active DOM window.
//
// This will pass the outer, which will be in the scope of the active inner.
if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
xpc::Scriptability& scriptability =
xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
}
// If our value has changed, our children might be affected. Recompute their
// value as well.
if (old != mCanExecuteScripts) {
for (auto* child : mChildList.ForwardRange()) {
static_cast<nsDocShell*>(child)->RecomputeCanExecuteScripts();
}
}
}
nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) { nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
bool wasFrame = IsFrame(); bool wasFrame = IsFrame();
@ -2717,10 +2656,6 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent)); nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
if (parentAsDocShell) { if (parentAsDocShell) {
if (mAllowJavascript &&
NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
SetAllowJavascript(value);
}
if (mAllowMetaRedirects && if (mAllowMetaRedirects &&
NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) { NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
SetAllowMetaRedirects(value); SetAllowMetaRedirects(value);
@ -2755,9 +2690,6 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
mContentListener->SetParentContentListener(parentURIListener); mContentListener->SetParentContentListener(parentURIListener);
} }
// Our parent has changed. Recompute scriptability.
RecomputeCanExecuteScripts();
// Inform windows when they're being removed from their parent. // Inform windows when they're being removed from their parent.
if (!aParent) { if (!aParent) {
MaybeClearStorageAccessFlag(); MaybeClearStorageAccessFlag();
@ -2982,16 +2914,6 @@ nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
} }
} }
// Our tree owner has changed. Recompute scriptability.
//
// Note that this is near-redundant with the recomputation in
// SetDocLoaderParent(), but not so for the root DocShell, where the call to
// SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
// and we never set another parent. Given that this is neither expensive nor
// performance-critical, let's be safe and unconditionally recompute this
// state whenever dependent state changes.
RecomputeCanExecuteScripts();
return NS_OK; return NS_OK;
} }
@ -7658,9 +7580,6 @@ nsresult nsDocShell::RestoreFromHistory() {
// Make sure to not clobber the state of the child. Since AddChild // Make sure to not clobber the state of the child. Since AddChild
// always clobbers it, save it off first. // always clobbers it, save it off first.
bool allowJavascript;
childShell->GetAllowJavascript(&allowJavascript);
bool allowRedirects; bool allowRedirects;
childShell->GetAllowMetaRedirects(&allowRedirects); childShell->GetAllowMetaRedirects(&allowRedirects);
@ -7684,7 +7603,6 @@ nsresult nsDocShell::RestoreFromHistory() {
// child inherits our mPrivateBrowsingId, which is what we want. // child inherits our mPrivateBrowsingId, which is what we want.
AddChild(childItem); AddChild(childItem);
childShell->SetAllowJavascript(allowJavascript);
childShell->SetAllowMetaRedirects(allowRedirects); childShell->SetAllowMetaRedirects(allowRedirects);
childShell->SetAllowSubframes(allowSubframes); childShell->SetAllowSubframes(allowSubframes);
childShell->SetAllowImages(allowImages); childShell->SetAllowImages(allowImages);
@ -13091,12 +13009,6 @@ NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
#endif #endif
} }
NS_IMETHODIMP
nsDocShell::GetCanExecuteScripts(bool* aResult) {
*aResult = mCanExecuteScripts;
return NS_OK;
}
/* [infallible] */ /* [infallible] */
NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell( NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
bool* aIsTopLevelContentDocShell) { bool* aIsTopLevelContentDocShell) {

View file

@ -952,7 +952,6 @@ class nsDocShell final : public nsDocLoader,
void SetupReferrerInfoFromChannel(nsIChannel* aChannel); void SetupReferrerInfoFromChannel(nsIChannel* aChannel);
void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo); void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo);
void ReattachEditorToWindow(nsISHEntry* aSHEntry); void ReattachEditorToWindow(nsISHEntry* aSHEntry);
void RecomputeCanExecuteScripts();
void ClearFrameHistory(nsISHEntry* aEntry); void ClearFrameHistory(nsISHEntry* aEntry);
// Determine if this type of load should update history. // Determine if this type of load should update history.
static bool ShouldUpdateGlobalHistory(uint32_t aLoadType); static bool ShouldUpdateGlobalHistory(uint32_t aLoadType);
@ -1245,7 +1244,6 @@ class nsDocShell final : public nsDocLoader,
bool mInitialized : 1; bool mInitialized : 1;
bool mAllowSubframes : 1; bool mAllowSubframes : 1;
bool mAllowJavascript : 1;
bool mAllowMetaRedirects : 1; bool mAllowMetaRedirects : 1;
bool mAllowImages : 1; bool mAllowImages : 1;
bool mAllowMedia : 1; bool mAllowMedia : 1;
@ -1260,11 +1258,6 @@ class nsDocShell final : public nsDocLoader,
bool mWindowDraggingAllowed : 1; bool mWindowDraggingAllowed : 1;
bool mInFrameSwap : 1; bool mInFrameSwap : 1;
// Because scriptability depends on the mAllowJavascript values of our
// ancestors, we cache the effective scriptability and recompute it when
// it might have changed;
bool mCanExecuteScripts : 1;
// This boolean is set to true right before we fire pagehide and generally // This boolean is set to true right before we fire pagehide and generally
// unset when we embed a new content viewer. While it's true no navigation // unset when we embed a new content viewer. While it's true no navigation
// is allowed in this docshell. // is allowed in this docshell.

View file

@ -192,11 +192,6 @@ interface nsIDocShell : nsIDocShellTreeItem
*/ */
attribute boolean allowPlugins; attribute boolean allowPlugins;
/**
* Whether to allow Javascript execution
*/
attribute boolean allowJavascript;
/** /**
* Attribute stating if refresh based redirects can be allowed * Attribute stating if refresh based redirects can be allowed
*/ */
@ -468,13 +463,6 @@ interface nsIDocShell : nsIDocShellTreeItem
*/ */
void exitPrintPreview(); void exitPrintPreview();
/**
* Whether this docshell can execute scripts based on its hierarchy.
* The rule of thumb here is that we disable js if this docshell or any
* of its parents disallow scripting.
*/
[infallible] readonly attribute boolean canExecuteScripts;
/** /**
* The ID of the docshell in the session history. * The ID of the docshell in the session history.
*/ */

View file

@ -0,0 +1,44 @@
"use strict";
var EXPORTED_SYMBOLS = ["AllowJavascriptChild"];
class AllowJavascriptChild extends JSWindowActorChild {
async receiveMessage(msg) {
switch (msg.name) {
case "CheckScriptsAllowed":
return this.checkScriptsAllowed();
case "CheckFiredLoadEvent":
return this.contentWindow.wrappedJSObject.gFiredOnload;
case "CreateIframe":
return this.createIframe(msg.data.url);
}
return null;
}
handleEvent(event) {
if (event.type === "load") {
this.sendAsyncMessage("LoadFired");
}
}
checkScriptsAllowed() {
let win = this.contentWindow;
win.wrappedJSObject.gFiredOnclick = false;
win.document.body.click();
return win.wrappedJSObject.gFiredOnclick;
}
async createIframe(url) {
let doc = this.contentWindow.document;
let iframe = doc.createElement("iframe");
iframe.src = url;
doc.body.appendChild(iframe);
await new Promise(resolve => {
iframe.addEventListener("load", resolve, { once: true });
});
return iframe.browsingContext;
}
}

View file

@ -0,0 +1,31 @@
"use strict";
var EXPORTED_SYMBOLS = ["AllowJavascriptParent"];
let loadPromises = new WeakMap();
class AllowJavascriptParent extends JSWindowActorParent {
async receiveMessage(msg) {
switch (msg.name) {
case "LoadFired":
let bc = this.browsingContext;
let deferred = loadPromises.get(bc);
if (deferred) {
loadPromises.delete(bc);
deferred.resolve(this);
}
break;
}
}
static promiseLoad(bc) {
let deferred = loadPromises.get(bc);
if (!deferred) {
deferred = {};
deferred.promise = new Promise(resolve => {
deferred.resolve = resolve;
});
loadPromises.set(bc, deferred);
}
return deferred.promise;
}
}

View file

@ -0,0 +1,269 @@
"use strict";
const { XPCShellContentUtils } = ChromeUtils.import(
"resource://testing-common/XPCShellContentUtils.jsm"
);
XPCShellContentUtils.init(this);
const ACTOR = "AllowJavascript";
const HTML = String.raw`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script type="application/javascript">
"use strict";
var gFiredOnload = false;
var gFiredOnclick = false;
</script>
</head>
<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;">
</body>
</html>`;
const server = XPCShellContentUtils.createHttpServer({
hosts: ["example.com", "example.org"],
});
server.registerPathHandler("/", (request, response) => {
response.setHeader("Content-Type", "text/html");
response.write(HTML);
});
function getResourceURI(file) {
return Services.io.newFileURI(do_get_file(file)).spec;
}
const { AllowJavascriptParent } = ChromeUtils.import(
getResourceURI("AllowJavascriptParent.jsm")
);
async function assertScriptsAllowed(bc, expectAllowed, desc) {
let actor = bc.currentWindowGlobal.getActor(ACTOR);
let allowed = await actor.sendQuery("CheckScriptsAllowed");
equal(
allowed,
expectAllowed,
`Scripts should be ${expectAllowed ? "" : "dis"}allowed for ${desc}`
);
}
async function assertLoadFired(bc, expectFired, desc) {
let actor = bc.currentWindowGlobal.getActor(ACTOR);
let fired = await actor.sendQuery("CheckFiredLoadEvent");
equal(
fired,
expectFired,
`Should ${expectFired ? "" : "not "}have fired load for ${desc}`
);
}
function createSubframe(bc, url) {
let actor = bc.currentWindowGlobal.getActor(ACTOR);
return actor.sendQuery("CreateIframe", { url });
}
add_task(async function() {
ChromeUtils.registerWindowActor(ACTOR, {
allFrames: true,
child: {
moduleURI: getResourceURI("AllowJavascriptChild.jsm"),
events: { load: { capture: true } },
},
parent: {
moduleURI: getResourceURI("AllowJavascriptParent.jsm"),
},
});
let page = await XPCShellContentUtils.loadContentPage("http://example.com/", {
remote: true,
remoteSubframes: true,
});
let bc = page.browsingContext;
{
let oopFrame1 = await createSubframe(bc, "http://example.org/");
let inprocFrame1 = await createSubframe(bc, "http://example.com/");
let oopFrame1OopSub = await createSubframe(
oopFrame1,
"http://example.com/"
);
let inprocFrame1OopSub = await createSubframe(
inprocFrame1,
"http://example.org/"
);
equal(
oopFrame1.allowJavascript,
true,
"OOP BC should inherit allowJavascript from parent"
);
equal(
inprocFrame1.allowJavascript,
true,
"In-process BC should inherit allowJavascript from parent"
);
equal(
oopFrame1OopSub.allowJavascript,
true,
"OOP BC child should inherit allowJavascript from parent"
);
equal(
inprocFrame1OopSub.allowJavascript,
true,
"In-process child BC should inherit allowJavascript from parent"
);
await assertLoadFired(bc, true, "top BC");
await assertScriptsAllowed(bc, true, "top BC");
await assertLoadFired(oopFrame1, true, "OOP frame 1");
await assertScriptsAllowed(oopFrame1, true, "OOP frame 1");
await assertLoadFired(inprocFrame1, true, "In-process frame 1");
await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1");
await assertLoadFired(oopFrame1OopSub, true, "OOP frame 1 subframe");
await assertScriptsAllowed(oopFrame1OopSub, true, "OOP frame 1 subframe");
await assertLoadFired(
inprocFrame1OopSub,
true,
"In-process frame 1 subframe"
);
await assertScriptsAllowed(
inprocFrame1OopSub,
true,
"In-process frame 1 subframe"
);
bc.allowJavascript = false;
await assertScriptsAllowed(bc, false, "top BC with scripts disallowed");
await assertScriptsAllowed(
oopFrame1,
false,
"OOP frame 1 with top BC with scripts disallowed"
);
await assertScriptsAllowed(
inprocFrame1,
false,
"In-process frame 1 with top BC with scripts disallowed"
);
await assertScriptsAllowed(
oopFrame1OopSub,
false,
"OOP frame 1 subframe with top BC with scripts disallowed"
);
await assertScriptsAllowed(
inprocFrame1OopSub,
false,
"In-process frame 1 subframe with top BC with scripts disallowed"
);
let oopFrame2 = await createSubframe(bc, "http://example.org/");
let inprocFrame2 = await createSubframe(bc, "http://example.com/");
equal(
oopFrame2.allowJavascript,
false,
"OOP BC 2 should inherit allowJavascript from parent"
);
equal(
inprocFrame2.allowJavascript,
false,
"In-process BC 2 should inherit allowJavascript from parent"
);
await assertLoadFired(
oopFrame2,
undefined,
"OOP frame 2 with top BC with scripts disallowed"
);
await assertScriptsAllowed(
oopFrame2,
false,
"OOP frame 2 with top BC with scripts disallowed"
);
await assertLoadFired(
inprocFrame2,
undefined,
"In-process frame 2 with top BC with scripts disallowed"
);
await assertScriptsAllowed(
inprocFrame2,
false,
"In-process frame 2 with top BC with scripts disallowed"
);
bc.allowJavascript = true;
await assertScriptsAllowed(bc, true, "top BC");
await assertScriptsAllowed(oopFrame1, true, "OOP frame 1");
await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1");
await assertScriptsAllowed(oopFrame1OopSub, true, "OOP frame 1 subframe");
await assertScriptsAllowed(
inprocFrame1OopSub,
true,
"In-process frame 1 subframe"
);
await assertScriptsAllowed(oopFrame2, false, "OOP frame 2");
await assertScriptsAllowed(inprocFrame2, false, "In-process frame 2");
oopFrame1.currentWindowGlobal.allowJavascript = false;
inprocFrame1.currentWindowGlobal.allowJavascript = false;
await assertScriptsAllowed(
oopFrame1,
false,
"OOP frame 1 with second level WC scripts disallowed"
);
await assertScriptsAllowed(
inprocFrame1,
false,
"In-process frame 1 with second level WC scripts disallowed"
);
await assertScriptsAllowed(
oopFrame1OopSub,
false,
"OOP frame 1 subframe second level WC scripts disallowed"
);
await assertScriptsAllowed(
inprocFrame1OopSub,
false,
"In-process frame 1 subframe with second level WC scripts disallowed"
);
oopFrame1.reload(0);
inprocFrame1.reload(0);
await AllowJavascriptParent.promiseLoad(oopFrame1);
await AllowJavascriptParent.promiseLoad(inprocFrame1);
equal(
oopFrame1.currentWindowGlobal.allowJavascript,
true,
"WindowContext.allowJavascript does not persist after navigation for OOP frame 1"
);
equal(
inprocFrame1.currentWindowGlobal.allowJavascript,
true,
"WindowContext.allowJavascript does not persist after navigation for in-process frame 1"
);
await assertScriptsAllowed(oopFrame1, true, "OOP frame 1");
await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1");
}
bc.allowJavascript = false;
bc.reload(0);
await AllowJavascriptParent.promiseLoad(bc);
await assertLoadFired(bc, undefined, "top BC with scripts disabled");
await assertScriptsAllowed(bc, false, "top BC with scripts disabled");
await page.close();
});

View file

@ -1,6 +1,11 @@
[DEFAULT] [DEFAULT]
head = head_docshell.js head = head_docshell.js
[test_allowJavascript.js]
skip-if = os == 'android'
support-files =
AllowJavascriptChild.jsm
AllowJavascriptParent.jsm
[test_bug442584.js] [test_bug442584.js]
[test_browsing_context_structured_clone.js] [test_browsing_context_structured_clone.js]
[test_URIFixup.js] [test_URIFixup.js]

View file

@ -1847,7 +1847,7 @@ WindowStateHolder::WindowStateHolder(nsGlobalWindowInner* aWindow)
aWindow->Suspend(); aWindow->Suspend();
// When a global goes into the bfcache, we disable script. // When a global goes into the bfcache, we disable script.
xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false); xpc::Scriptability::Get(mInnerWindowReflector).SetWindowAllowsScript(false);
} }
WindowStateHolder::~WindowStateHolder() { WindowStateHolder::~WindowStateHolder() {
@ -2331,10 +2331,11 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
mBrowsingContext->SetWindowProxy(outer); mBrowsingContext->SetWindowProxy(outer);
} }
// Set scriptability based on the state of the docshell. // Set scriptability based on the state of the WindowContext.
bool allow = GetDocShell()->GetCanExecuteScripts(); WindowContext* wc = mInnerWindow->GetWindowContext();
bool allow = wc ? wc->CanExecuteScripts() : mBrowsingContext->CanExecuteScripts();
xpc::Scriptability::Get(GetWrapperPreserveColor()) xpc::Scriptability::Get(GetWrapperPreserveColor())
.SetDocShellAllowsScript(allow); .SetWindowAllowsScript(allow);
if (!aState) { if (!aState) {
// Get the "window" property once so it will be cached on our inner. We // Get the "window" property once so it will be cached on our inner. We

View file

@ -193,6 +193,16 @@ interface BrowsingContext {
*/ */
readonly attribute TouchEventsOverride touchEventsOverride; readonly attribute TouchEventsOverride touchEventsOverride;
/**
* Partially determines whether script execution is allowed in this
* BrowsingContext. Script execution will be permitted only if this
* attribute is true and script execution is allowed in the parent
* WindowContext.
*
* May only be set in the parent process.
*/
[SetterThrows] attribute boolean allowJavascript;
/** /**
* The nsID of the browsing context in the session history. * The nsID of the browsing context in the session history.
*/ */

View file

@ -29,6 +29,16 @@ interface WindowContext {
// True if the corresponding document has `loading='lazy'` images; // True if the corresponding document has `loading='lazy'` images;
// It won't become false if the image becomes non-lazy. // It won't become false if the image becomes non-lazy.
readonly attribute boolean hadLazyLoadImage; readonly attribute boolean hadLazyLoadImage;
/**
* Partially determines whether script execution is allowed in this
* BrowsingContext. Script execution will be permitted only if this
* attribute is true and script execution is allowed in the owner
* BrowsingContext.
*
* May only be set in the context's owning process.
*/
[SetterThrows] attribute boolean allowJavascript;
}; };
// Keep this in sync with nsIContentViewer::PermitUnloadAction. // Keep this in sync with nsIContentViewer::PermitUnloadAction.

View file

@ -56,6 +56,7 @@ WindowGlobalInit WindowGlobalActor::BaseInitializer(
auto& fields = ctx.mFields; auto& fields = ctx.mFields;
fields.mEmbedderPolicy = InheritedPolicy(aBrowsingContext); fields.mEmbedderPolicy = InheritedPolicy(aBrowsingContext);
fields.mAutoplayPermission = nsIPermissionManager::UNKNOWN_ACTION; fields.mAutoplayPermission = nsIPermissionManager::UNKNOWN_ACTION;
fields.mAllowJavascript = true;
return init; return init;
} }

View file

@ -42,6 +42,7 @@
#include "nsStringFwd.h" // for nsString #include "nsStringFwd.h" // for nsString
#include "mozilla/dom/BrowsingContext.h" // for BrowsingContext #include "mozilla/dom/BrowsingContext.h" // for BrowsingContext
#include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges, etc #include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges, etc
#include "mozilla/dom/WindowContext.h" // for WindowContext
#include "nsFrameSelection.h" // for nsFrameSelection #include "nsFrameSelection.h" // for nsFrameSelection
#include "nsBaseCommandController.h" // for nsBaseCommandController #include "nsBaseCommandController.h" // for nsBaseCommandController
#include "mozilla/dom/LoadURIOptionsBinding.h" #include "mozilla/dom/LoadURIOptionsBinding.h"
@ -120,7 +121,7 @@ nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow,
nsresult rv; nsresult rv;
if (!mInteractive) { if (!mInteractive) {
rv = DisableJSAndPlugins(*docShell); rv = DisableJSAndPlugins(window->GetCurrentInnerWindow());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
@ -172,28 +173,25 @@ nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow,
return rv; return rv;
} }
nsresult nsEditingSession::DisableJSAndPlugins(nsIDocShell& aDocShell) { nsresult nsEditingSession::DisableJSAndPlugins(nsPIDOMWindowInner* aWindow) {
bool tmp; WindowContext* wc = aWindow->GetWindowContext();
nsresult rv = aDocShell.GetAllowJavascript(&tmp); BrowsingContext* bc = wc->GetBrowsingContext();
NS_ENSURE_SUCCESS(rv, rv);
mScriptsEnabled = tmp; mScriptsEnabled = wc->GetAllowJavascript();
rv = aDocShell.SetAllowJavascript(false); MOZ_TRY(wc->SetAllowJavascript(false));
NS_ENSURE_SUCCESS(rv, rv);
// Disable plugins in this document: // Disable plugins in this document:
mPluginsEnabled = aDocShell.PluginsAllowedInCurrentDoc(); mPluginsEnabled = bc->GetAllowPlugins();
rv = aDocShell.GetBrowsingContext()->SetAllowPlugins(false); MOZ_TRY(bc->SetAllowPlugins(false));
NS_ENSURE_SUCCESS(rv, rv);
mDisabledJSAndPlugins = true; mDisabledJSAndPlugins = true;
return NS_OK; return NS_OK;
} }
nsresult nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowOuter* aWindow) { nsresult nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowInner* aWindow) {
if (!mDisabledJSAndPlugins) { if (!mDisabledJSAndPlugins) {
return NS_OK; return NS_OK;
} }
@ -204,17 +202,15 @@ nsresult nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowOuter* aWindow) {
// DetachFromWindow may call this method with nullptr. // DetachFromWindow may call this method with nullptr.
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsIDocShell* docShell = aWindow->GetDocShell();
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled); WindowContext* wc = aWindow->GetWindowContext();
NS_ENSURE_SUCCESS(rv, rv); BrowsingContext* bc = wc->GetBrowsingContext();
MOZ_TRY(wc->SetAllowJavascript(mScriptsEnabled));
// Disable plugins in this document: // Disable plugins in this document:
auto* browsingContext = aWindow->GetBrowsingContext();
NS_ENSURE_TRUE(browsingContext, NS_ERROR_FAILURE);
return browsingContext->SetAllowPlugins(mPluginsEnabled); return bc->SetAllowPlugins(mPluginsEnabled);
} }
/*--------------------------------------------------------------------------- /*---------------------------------------------------------------------------
@ -510,7 +506,7 @@ nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy* aWindow) {
if (stopEditing) { if (stopEditing) {
// Make things the way they were before we started editing. // Make things the way they were before we started editing.
RestoreJSAndPlugins(window); RestoreJSAndPlugins(window->GetCurrentInnerWindow());
RestoreAnimationMode(window); RestoreAnimationMode(window);
if (mMakeWholeDocumentEditable) { if (mMakeWholeDocumentEditable) {
@ -1192,7 +1188,7 @@ nsresult nsEditingSession::DetachFromWindow(nsPIDOMWindowOuter* aWindow) {
// make things the way they were before we started editing. // make things the way they were before we started editing.
RemoveEditorControllers(aWindow); RemoveEditorControllers(aWindow);
RemoveWebProgressListener(aWindow); RemoveWebProgressListener(aWindow);
RestoreJSAndPlugins(aWindow); RestoreJSAndPlugins(aWindow->GetCurrentInnerWindow());
RestoreAnimationMode(aWindow); RestoreAnimationMode(aWindow);
// Kill our weak reference to our original window, in case // Kill our weak reference to our original window, in case
@ -1219,7 +1215,7 @@ nsresult nsEditingSession::ReattachToWindow(nsPIDOMWindowOuter* aWindow) {
// Disable plugins. // Disable plugins.
if (!mInteractive) { if (!mInteractive) {
rv = DisableJSAndPlugins(*docShell); rv = DisableJSAndPlugins(aWindow->GetCurrentInnerWindow());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }

View file

@ -31,6 +31,8 @@ class nsIChannel;
class nsIControllers; class nsIControllers;
class nsIDocShell; class nsIDocShell;
class nsIWebProgress; class nsIWebProgress;
class nsIPIDOMWindowOuter;
class nsIPIDOMWindowInner;
namespace mozilla { namespace mozilla {
class ComposerCommandsUpdater; class ComposerCommandsUpdater;
@ -113,13 +115,13 @@ class nsEditingSession final : public nsIEditingSession,
/** /**
* Disable scripts and plugins in aDocShell. * Disable scripts and plugins in aDocShell.
*/ */
nsresult DisableJSAndPlugins(nsIDocShell& aDocShell); nsresult DisableJSAndPlugins(nsPIDOMWindowInner* aWindow);
/** /**
* Restore JS and plugins (enable/disable them) according to the state they * Restore JS and plugins (enable/disable them) according to the state they
* were before the last call to disableJSAndPlugins. * were before the last call to disableJSAndPlugins.
*/ */
nsresult RestoreJSAndPlugins(nsPIDOMWindowOuter* aWindow); nsresult RestoreJSAndPlugins(nsPIDOMWindowInner* aWindow);
protected: protected:
bool mDoneSetup; // have we prepared for editing yet? bool mDoneSetup; // have we prepared for editing yet?

View file

@ -22,7 +22,7 @@ var iframe = document.getElementById("load-frame");
function enableJS() { allowJS(true, iframe); } function enableJS() { allowJS(true, iframe); }
function disableJS() { allowJS(false, iframe); } function disableJS() { allowJS(false, iframe); }
function allowJS(allow, frame) { function allowJS(allow, frame) {
SpecialPowers.wrap(frame.contentWindow).docShell.allowJavascript = allow; SpecialPowers.wrap(frame.contentWindow).windowGlobalChild.windowContext.allowJavascript = allow;
} }
function expectJSAllowed(allowed, testCondition, callback) { function expectJSAllowed(allowed, testCondition, callback) {

View file

@ -456,7 +456,7 @@ void NukeJSStackFrames(JS::Realm* aRealm) {
Scriptability::Scriptability(JS::Realm* realm) Scriptability::Scriptability(JS::Realm* realm)
: mScriptBlocks(0), : mScriptBlocks(0),
mDocShellAllowsScript(true), mWindowAllowsScript(true),
mScriptBlockedByPolicy(false) { mScriptBlockedByPolicy(false) {
nsIPrincipal* prin = nsJSPrincipals::get(JS::GetRealmPrincipals(realm)); nsIPrincipal* prin = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
@ -477,7 +477,7 @@ Scriptability::Scriptability(JS::Realm* realm)
} }
bool Scriptability::Allowed() { bool Scriptability::Allowed() {
return mDocShellAllowsScript && !mScriptBlockedByPolicy && mScriptBlocks == 0; return mWindowAllowsScript && !mScriptBlockedByPolicy && mScriptBlocks == 0;
} }
bool Scriptability::IsImmuneToScriptPolicy() { return mImmuneToScriptPolicy; } bool Scriptability::IsImmuneToScriptPolicy() { return mImmuneToScriptPolicy; }
@ -489,8 +489,8 @@ void Scriptability::Unblock() {
--mScriptBlocks; --mScriptBlocks;
} }
void Scriptability::SetDocShellAllowsScript(bool aAllowed) { void Scriptability::SetWindowAllowsScript(bool aAllowed) {
mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy; mWindowAllowsScript = aAllowed || mImmuneToScriptPolicy;
} }
/* static */ /* static */

View file

@ -81,7 +81,7 @@ class Scriptability {
void Block(); void Block();
void Unblock(); void Unblock();
void SetDocShellAllowsScript(bool aAllowed); void SetWindowAllowsScript(bool aAllowed);
static Scriptability& Get(JSObject* aScope); static Scriptability& Get(JSObject* aScope);
@ -92,9 +92,9 @@ class Scriptability {
// Script may not run if this value is non-zero. // Script may not run if this value is non-zero.
uint32_t mScriptBlocks; uint32_t mScriptBlocks;
// Whether the docshell allows javascript in this scope. If this scope // Whether the DOM window allows javascript in this scope. If this scope
// doesn't have a docshell, this value is always true. // doesn't have a window, this value is always true.
bool mDocShellAllowsScript; bool mWindowAllowsScript;
// Whether this scope is immune to user-defined or addon-defined script // Whether this scope is immune to user-defined or addon-defined script
// policy. // policy.

View file

@ -28,7 +28,6 @@ class GeckoViewSettingsChild extends GeckoViewActorChild {
case "SettingsUpdate": { case "SettingsUpdate": {
const settings = message.data; const settings = message.data;
this.allowJavascript = settings.allowJavascript;
this.viewportMode = settings.viewportMode; this.viewportMode = settings.viewportMode;
if (settings.isPopup) { if (settings.isPopup) {
// Allow web extensions to close their own action popups (bz1612363) // Allow web extensions to close their own action popups (bz1612363)
@ -38,14 +37,6 @@ class GeckoViewSettingsChild extends GeckoViewActorChild {
} }
} }
get allowJavascript() {
return this.docShell.allowJavascript;
}
set allowJavascript(aAllowJavascript) {
this.docShell.allowJavascript = aAllowJavascript;
}
set viewportMode(aMode) { set viewportMode(aMode) {
const { windowUtils } = this.contentWindow; const { windowUtils } = this.contentWindow;
if (aMode === windowUtils.desktopModeViewport) { if (aMode === windowUtils.desktopModeViewport) {

View file

@ -88,6 +88,14 @@ class GeckoViewSettings extends GeckoViewModule {
); );
} }
get allowJavascript() {
return this.browsingContext.allowJavascript;
}
set allowJavascript(aAllowJavascript) {
this.browsingContext.allowJavascript = aAllowJavascript;
}
get customUserAgent() { get customUserAgent() {
if (this.userAgentOverride !== null) { if (this.userAgentOverride !== null) {
return this.userAgentOverride; return this.userAgentOverride;

View file

@ -111,7 +111,7 @@ async function starttest(){
// Play with the window object. // Play with the window object.
var docShell = SpecialPowers.wrap(window).docShell; var docShell = SpecialPowers.wrap(window).docShell;
ok(docShell.allowJavascript, "Able to pull properties off of docshell!"); ok(docShell.browsingContext, "Able to pull properties off of docshell!");
// Make sure Xray-wrapped functions work. // Make sure Xray-wrapped functions work.
try { try {

View file

@ -230,6 +230,10 @@ class ContentPage {
return browser; return browser;
} }
get browsingContext() {
return this.browser.browsingContext;
}
sendMessage(msg, data) { sendMessage(msg, data) {
return MessageChannel.sendMessage(this.browser.messageManager, msg, data); return MessageChannel.sendMessage(this.browser.messageManager, msg, data);
} }

View file

@ -38,6 +38,7 @@
#include "nsIFormControl.h" #include "nsIFormControl.h"
#include "nsIScrollableFrame.h" #include "nsIScrollableFrame.h"
#include "nsISHistory.h" #include "nsISHistory.h"
#include "nsIXULRuntime.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
@ -222,7 +223,6 @@ void SessionStoreUtils::CollectDocShellCapabilities(const GlobalObject& aGlobal,
void SessionStoreUtils::RestoreDocShellCapabilities( void SessionStoreUtils::RestoreDocShellCapabilities(
nsIDocShell* aDocShell, const nsCString& aDisallowCapabilities) { nsIDocShell* aDocShell, const nsCString& aDisallowCapabilities) {
aDocShell->SetAllowPlugins(true); aDocShell->SetAllowPlugins(true);
aDocShell->SetAllowJavascript(true);
aDocShell->SetAllowMetaRedirects(true); aDocShell->SetAllowMetaRedirects(true);
aDocShell->SetAllowSubframes(true); aDocShell->SetAllowSubframes(true);
aDocShell->SetAllowImages(true); aDocShell->SetAllowImages(true);
@ -232,12 +232,13 @@ void SessionStoreUtils::RestoreDocShellCapabilities(
aDocShell->SetAllowContentRetargeting(true); aDocShell->SetAllowContentRetargeting(true);
aDocShell->SetAllowContentRetargetingOnChildren(true); aDocShell->SetAllowContentRetargetingOnChildren(true);
bool allowJavascript = true;
for (const nsACString& token : for (const nsACString& token :
nsCCharSeparatedTokenizer(aDisallowCapabilities, ',').ToRange()) { nsCCharSeparatedTokenizer(aDisallowCapabilities, ',').ToRange()) {
if (token.EqualsLiteral("Plugins")) { if (token.EqualsLiteral("Plugins")) {
aDocShell->SetAllowPlugins(false); aDocShell->SetAllowPlugins(false);
} else if (token.EqualsLiteral("Javascript")) { } else if (token.EqualsLiteral("Javascript")) {
aDocShell->SetAllowJavascript(false); allowJavascript = false;
} else if (token.EqualsLiteral("MetaRedirects")) { } else if (token.EqualsLiteral("MetaRedirects")) {
aDocShell->SetAllowMetaRedirects(false); aDocShell->SetAllowMetaRedirects(false);
} else if (token.EqualsLiteral("Subframes")) { } else if (token.EqualsLiteral("Subframes")) {
@ -261,6 +262,12 @@ void SessionStoreUtils::RestoreDocShellCapabilities(
aDocShell->SetAllowContentRetargetingOnChildren(false); aDocShell->SetAllowContentRetargetingOnChildren(false);
} }
} }
if (!mozilla::SessionHistoryInParent()) {
// With SessionHistoryInParent, this is set from the parent process.
BrowsingContext* bc = aDocShell->GetBrowsingContext();
Unused << bc->SetAllowJavascript(allowJavascript);
}
} }
static void CollectCurrentScrollPosition(JSContext* aCx, Document& aDocument, static void CollectCurrentScrollPosition(JSContext* aCx, Document& aDocument,
@ -1463,6 +1470,16 @@ already_AddRefed<Promise> SessionStoreUtils::RestoreDocShellState(
} }
} }
bool allowJavascript = true;
for (const nsACString& token :
nsCCharSeparatedTokenizer(aDocShellCaps, ',').ToRange()) {
if (token.EqualsLiteral("Javascript")) {
allowJavascript = false;
}
}
Unused << aContext.SetAllowJavascript(allowJavascript);
DocShellRestoreState state = {uri, aDocShellCaps}; DocShellRestoreState state = {uri, aDocShellCaps};
// TODO (anny): Investigate removing this roundtrip. // TODO (anny): Investigate removing this roundtrip.