Bug 1954685 - Unship onafterscriptexecute and onbeforescriptexecute events on Nightly; r=dom-core,webidl,saschanaz,devtools-reviewers,ochameau,hsivonen,jandem

Differential Revision: https://phabricator.services.mozilla.com/D241940
This commit is contained in:
Edgar Chen
2025-03-31 19:10:02 +00:00
parent 23e69eb981
commit 82d780e44e
10 changed files with 101 additions and 64 deletions

View File

@@ -924,8 +924,7 @@ async function doEagerEvalDOMGetters(commands) {
[`typeof document.lastModified`, "string"],
[`typeof document.readyState`, "string"],
[`typeof document.designMode`, "string"],
[`typeof document.onbeforescriptexecute`, "object"],
[`typeof document.onafterscriptexecute`, "object"],
[`typeof document.onabort`, "object"],
// Element
[`typeof document.documentElement.scrollTop`, "number"],

View File

@@ -5137,12 +5137,13 @@ static already_AddRefed<Event> GetEventWithTarget(
nsresult nsContentUtils::DispatchTrustedEvent(
Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
bool* aDefaultAction) {
bool* aDefaultAction, SystemGroupOnly aSystemGroupOnly) {
MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
!aEventName.EqualsLiteral("beforeinput"),
"Use DispatchInputEvent() instead");
return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
aComposed, Trusted::eYes, aDefaultAction);
aComposed, Trusted::eYes, aDefaultAction,
ChromeOnlyDispatch::eNo, aSystemGroupOnly);
}
// static
@@ -5154,13 +5155,11 @@ nsresult nsContentUtils::DispatchUntrustedEvent(
}
// static
nsresult nsContentUtils::DispatchEvent(Document* aDoc, EventTarget* aTarget,
const nsAString& aEventName,
CanBubble aCanBubble,
Cancelable aCancelable,
Composed aComposed, Trusted aTrusted,
bool* aDefaultAction,
ChromeOnlyDispatch aOnlyChromeDispatch) {
nsresult nsContentUtils::DispatchEvent(
Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
Trusted aTrusted, bool* aDefaultAction,
ChromeOnlyDispatch aOnlyChromeDispatch, SystemGroupOnly aSystemGroupOnly) {
if (!aDoc || !aTarget) {
return NS_ERROR_INVALID_ARG;
}
@@ -5174,6 +5173,8 @@ nsresult nsContentUtils::DispatchEvent(Document* aDoc, EventTarget* aTarget,
}
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatch =
aSystemGroupOnly == SystemGroupOnly::eYes;
bool doDefault = aTarget->DispatchEvent(*event, CallerType::System, err);
if (aDefaultAction) {

View File

@@ -266,6 +266,7 @@ class nsContentUtils {
using Trusted = mozilla::Trusted;
using JSONBehavior = mozilla::dom::JSONBehavior;
using RFPTarget = mozilla::RFPTarget;
using SystemGroupOnly = mozilla::SystemGroupOnly;
public:
static nsresult Init();
@@ -1556,24 +1557,22 @@ class nsContentUtils {
*/
// TODO: annotate with `MOZ_CAN_RUN_SCRIPT`
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1625902).
static nsresult DispatchTrustedEvent(Document* aDoc,
mozilla::dom::EventTarget* aTarget,
const nsAString& aEventName, CanBubble,
Cancelable,
Composed aComposed = Composed::eDefault,
bool* aDefaultAction = nullptr);
static nsresult DispatchTrustedEvent(
Document* aDoc, mozilla::dom::EventTarget* aTarget,
const nsAString& aEventName, CanBubble, Cancelable,
Composed aComposed = Composed::eDefault, bool* aDefaultAction = nullptr,
SystemGroupOnly aSystemGroupOnly = SystemGroupOnly::eNo);
// TODO: annotate with `MOZ_CAN_RUN_SCRIPT`
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1625902).
static nsresult DispatchTrustedEvent(Document* aDoc,
mozilla::dom::EventTarget* aTarget,
const nsAString& aEventName,
CanBubble aCanBubble,
Cancelable aCancelable,
bool* aDefaultAction) {
static nsresult DispatchTrustedEvent(
Document* aDoc, mozilla::dom::EventTarget* aTarget,
const nsAString& aEventName, CanBubble aCanBubble, Cancelable aCancelable,
bool* aDefaultAction,
SystemGroupOnly aSystemGroupOnly = SystemGroupOnly::eNo) {
return DispatchTrustedEvent(aDoc, aTarget, aEventName, aCanBubble,
aCancelable, Composed::eDefault,
aDefaultAction);
aCancelable, Composed::eDefault, aDefaultAction,
aSystemGroupOnly);
}
/**
@@ -3577,7 +3576,8 @@ class nsContentUtils {
Document* aDoc, mozilla::dom::EventTarget* aTarget,
const nsAString& aEventName, CanBubble, Cancelable, Composed, Trusted,
bool* aDefaultAction = nullptr,
ChromeOnlyDispatch = ChromeOnlyDispatch::eNo);
ChromeOnlyDispatch = ChromeOnlyDispatch::eNo,
SystemGroupOnly = SystemGroupOnly::eNo);
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DispatchEvent(

View File

@@ -12,15 +12,24 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=587931
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=587931">Mozilla Bug 587931</a>
<pre id="test">
<script type="application/javascript">
var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
/** Test for Bug 587931 **/
SimpleTest.waitForExplicitFinish();
var afterCount = 0;
var lastBeforeExecute = null;
var afterCountForSystemGroup = 0;
// For system group
var afterCountForSystemGroup = 0;
var lastBeforeExecuteForSystemGroup = null;
var expectedCurrentScriptInAfterScriptExecute = null;
function verifyScript(n) {
var curr = document.currentScript;
is(curr, document.getElementById(n), "correct script (" + n + ")");
is(lastBeforeExecute, curr, "correct beforescript (" + n + ")");
is(lastBeforeExecute, AppConstants.NIGHTLY_BUILD ? null : curr, "correct beforescript (" + n + ")");
document.addEventListener("afterscriptexecute", function(event) {
afterCount++;
lastBeforeExecute = null;
@@ -29,13 +38,26 @@ function verifyScript(n) {
"document.currentScript in afterscriptexecute(" + n + ")");
document.removeEventListener("afterscriptexecute", arguments.callee);
});
// Test system group
SpecialPowers.wrap(document).addEventListener("afterscriptexecute", function(event) {
afterCountForSystemGroup++;
lastBeforeExecuteForSystemGroup = null;
is(event.target, curr, "correct afterscript (" + n + ") for system group");
is(document.currentScript, expectedCurrentScriptInAfterScriptExecute,
"document.currentScript in afterscriptexecute(" + n + ") for system group");
}, { mozSystemGroup: true, once: true });
}
document.onbeforescriptexecute = function(event) {
lastBeforeExecute = event.target;
};
// Test system group
SpecialPowers.wrap(document).addEventListener("beforescriptexecute", function(event) {
lastBeforeExecuteForSystemGroup = event.target;
}, { mozSystemGroup: true });
window.addEventListener("load", function() {
is(afterCount, 4, "correct number of afterscriptexecute");
is(afterCount, AppConstants.NIGHTLY_BUILD ? 0 : 4, "correct number of afterscriptexecute");
is(afterCountForSystemGroup, 4, "correct number of afterscriptexecute for system group");
SimpleTest.finish();
});
</script>
@@ -64,17 +86,17 @@ document.body.appendChild(s);
<!-- Test cancel using beforescriptexecute -->
<script onbeforescriptexecute="return false;"
onafterescriptexecute="window.firedAfterScriptExecuteForCancel = true;">
ok(false, "should have been canceled");
ok(AppConstants.NIGHTLY_BUILD, "should have been canceled");
</script>
<script>
isnot(window.firedAfterScriptExecuteForCancel, true, "onafterscriptexecute executed");
</script>
<!-- Test cancel using beforescriptexecute for external -->
<script onbeforescriptexecute="return false;"
<script onbeforescriptexecute="window.extFiredBeforeScriptExecuteForCancel = true; return false;"
onafterescriptexecute="window.extFiredAfterScriptExecuteForCancel = true;"
onload="window.extFiredLoadForCancel = true;"
src="data:text/plain,ok(false, 'should have been canceled');">
src="data:text/plain,ok(!window.extFiredBeforeScriptExecuteForCancel, 'should have been canceled');">
</script>
<script>
isnot(window.extFiredAfterScriptExecuteForCancel, true, "onafterscriptexecute executed");
@@ -86,17 +108,17 @@ is(extFiredLoadForCancel, true, "onload executed");
onafterscriptexecute="window.afterDidExecute = true;"
onload="window.loadDidExecute = true"
onerror="window.errorDidExecute = true"
src="data:text/plain,
is(window.beforeDidExecute, true, 'onbeforescriptexecute executed');
isnot(window.afterDidExecute, true, 'onafterscriptexecute executed');
isnot(window.loadDidExecute, true, 'onload executed');
isnot(window.errorDidExecute, true, 'onerror executed');
">
src="data:text/plain,window.didExecute=true">
is(window.beforeDidExecute, AppConstants.NIGHTLY_BUILD ? undefined : true, 'onbeforescriptexecute executed');
is(window.afterDidExecute, undefined, 'onafterscriptexecute executed');
is(window.didExecute, true, 'script executed');
is(window.loadDidExecute, undefined, 'onload executed');
is(window.errorDidExecute, undefined, 'onerror executed');
</script>
<script>
is(afterDidExecute, true, "onafterscriptexecute executed");
is(loadDidExecute, true, "onload executed");
isnot(window.errorDidExecute, true, "onerror executed");
is(window.afterDidExecute, AppConstants.NIGHTLY_BUILD ? undefined : true, "onafterscriptexecute executed");
is(window.loadDidExecute, true, "onload executed");
is(window.errorDidExecute, undefined, "onerror executed");
</script>
</body>
</html>

View File

@@ -2435,7 +2435,10 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
if (runScript) {
nsContentUtils::DispatchTrustedEvent(
scriptElem->OwnerDoc(), scriptElem, u"beforescriptexecute"_ns,
CanBubble::eYes, Cancelable::eYes, &runScript);
CanBubble::eYes, Cancelable::eYes, &runScript,
StaticPrefs::dom_events_script_execute_enabled()
? SystemGroupOnly::eNo
: SystemGroupOnly::eYes);
}
// Inner window could have gone away after firing beforescriptexecute
@@ -2454,9 +2457,12 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
doc->DecrementIgnoreDestructiveWritesCounter();
}
nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(), scriptElem,
u"afterscriptexecute"_ns,
CanBubble::eYes, Cancelable::eNo);
nsContentUtils::DispatchTrustedEvent(
scriptElem->OwnerDoc(), scriptElem, u"afterscriptexecute"_ns,
CanBubble::eYes, Cancelable::eNo, nullptr,
StaticPrefs::dom_events_script_execute_enabled()
? SystemGroupOnly::eNo
: SystemGroupOnly::eYes);
}
FireScriptEvaluated(rv, aRequest);

View File

@@ -373,7 +373,9 @@ partial interface Document {
// Mozilla extensions of various sorts
partial interface Document {
// @deprecated We are going to remove these (bug 1584269).
[Pref="dom.events.script_execute.enabled"]
attribute EventHandler onbeforescriptexecute;
[Pref="dom.events.script_execute.enabled"]
attribute EventHandler onafterscriptexecute;
// Creates a new XUL element regardless of the document's default type.

View File

@@ -137,25 +137,16 @@
// Temporarily install a new onerror handler to catch script errors.
var hasUncaughtError = false;
var uncaughtError;
var eventOptions = {__proto__: null, once: true};
ReflectApply(EventTargetPrototypeAddEventListener, script, [
"beforescriptexecute", function() {
setGlobalOnError(function(messageOrEvent, source, lineno, colno, error) {
hasUncaughtError = true;
uncaughtError = error;
return true;
});
}, eventOptions
]);
ReflectApply(EventTargetPrototypeAddEventListener, script, [
"afterscriptexecute", function() {
restoreGlobalOnError();
}, eventOptions
]);
setGlobalOnError(function(messageOrEvent, source, lineno, colno, error) {
hasUncaughtError = true;
uncaughtError = error;
return true;
});
ReflectApply(HTMLScriptElementTextSetter, script, [code]);
AppendChild(documentDocumentElement, script);
RemoveChild(documentDocumentElement, script);
restoreGlobalOnError();
if (hasUncaughtError)
throw uncaughtError;
@@ -554,7 +545,7 @@
let nextScriptIndex = i + 1;
if (nextScriptIndex < scripts.length) {
var callNextAppend = () => appendScript(nextScriptIndex);
script.addEventListener("afterscriptexecute", callNextAppend, {once: true});
SpecialPowers.wrap(script).addEventListener("afterscriptexecute", callNextAppend, {mozSystemGroup: true, once: true});
// Module scripts don't fire the "afterscriptexecute" event when there
// was an error, instead the "error" event is emitted. So listen for

View File

@@ -2914,6 +2914,12 @@
value: true
mirror: always
# Whether to dispatch `beforescriptexecute` and `afterscriptexecute` event.
- name: dom.events.script_execute.enabled
type: bool
value: @IS_NOT_NIGHTLY_BUILD@
mirror: always
# Whether to expose test interfaces of various sorts
- name: dom.expose_test_interfaces
type: bool

View File

@@ -2,13 +2,21 @@
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[onbeforescriptexecute content attribute should not be supported]
expected: FAIL
expected:
if nightly_build: PASS
FAIL
[onafterscriptexecute content attribute should not be supported]
expected: FAIL
expected:
if nightly_build: PASS
FAIL
[beforescriptexecute event should not be supported]
expected: FAIL
expected:
if nightly_build: PASS
FAIL
[afterscriptexecute event should not be supported]
expected: FAIL
expected:
if nightly_build: PASS
FAIL

View File

@@ -48,6 +48,8 @@ enum class Trusted { eYes, eNo };
enum class Composed { eYes, eNo, eDefault };
enum class SystemGroupOnly { eYes, eNo };
/**
* Event messages
*/