script: Remove some proprietary Servo-only testing methods from Window (#42728)

These three methods date back from the very early history of Servo and
are no longer necessary:

1. `Window.debug`: `console.log` is a better replacement for this method
   now. A manual test that tests the very basics of JavaScript used
   this. This test is removed as well.
2. `Window.gc`: This can be replaced with `TestUtils.gc`, which is part
    of a W3C specification.
3. `Window.js_backtrace`: This method is moved to `ServoTestUtils`.

Testing: Tests are updated to reflect these changes.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson
2026-02-20 18:32:34 +01:00
committed by GitHub
parent f69ee078d2
commit e7ee1171d8
15 changed files with 41 additions and 82 deletions

View File

@@ -4,6 +4,7 @@
// check-tidy: no specs after this line
use backtrace::Backtrace;
use dom_struct::dom_struct;
use layout_api::ReflowPhasesRun;
use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
@@ -57,6 +58,12 @@ impl ServoTestUtilsMethods<crate::DomTypeHolder> for ServoTestUtils {
LayoutResult::new(global, phases, can_gc)
}
fn Js_backtrace(_: &GlobalScope) {
println!("Current JS stack:");
let rust_stack = Backtrace::new();
println!("Current Rust stack:\n{:?}", rust_stack);
}
fn Panic(_: &GlobalScope) {
panic!("explicit panic from script")
}

View File

@@ -17,7 +17,6 @@ use js::realm::CurrentRealm;
use js::rust::{CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleValue};
use js::typedarray::{self, HeapUint8ClampedArray};
use script_bindings::cformat;
use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
use script_bindings::interfaces::TestBindingHelpers;
use script_bindings::record::Record;
use servo_config::prefs;
@@ -569,7 +568,7 @@ impl TestBindingMethods<crate::DomTypeHolder> for TestBinding {
&self,
_dictionary: RootedTraceableBox<TestDictionaryWithTypedArray>,
) {
self.global().as_window().Gc();
self.global().as_window().gc();
}
fn ReceiveTestDictionaryWithSuccessOnKeyword(&self) -> RootedTraceableBox<TestDictionary> {
RootedTraceableBox::new(TestDictionary {

View File

@@ -15,7 +15,6 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use app_units::Au;
use backtrace::Backtrace;
use base::cross_process_instant::CrossProcessInstant;
use base::generic_channel::{self, GenericCallback, GenericSender};
use base::id::{BrowsingContextId, PipelineId, WebViewId};
@@ -942,6 +941,13 @@ impl Window {
}),
}
}
#[expect(unsafe_code)]
pub(crate) fn gc(&self) {
unsafe {
JS_GC(*self.get_cx(), GCReason::API);
}
}
}
#[derive(Debug)]
@@ -1771,27 +1777,6 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
}
// check-tidy: no specs after this line
fn Debug(&self, message: DOMString) {
debug!("{}", message);
}
#[expect(unsafe_code)]
fn Gc(&self) {
unsafe {
JS_GC(*self.get_cx(), GCReason::API);
}
}
#[expect(unsafe_code)]
fn Js_backtrace(&self) {
unsafe {
println!("Current JS stack:");
dump_js_stack(*self.get_cx());
let rust_stack = Backtrace::new();
println!("Current Rust stack:\n{:?}", rust_stack);
}
}
fn WebdriverCallback(&self, realm: &mut CurrentRealm, value: HandleValue) {
let webdriver_script_sender = self.webdriver_script_chan.borrow_mut().take();
if let Some(webdriver_script_sender) = webdriver_script_sender {
@@ -3358,7 +3343,7 @@ impl Window {
// GC any unreachable objects generated by user script,
// or unattached DOM nodes. Attached DOM nodes can't be GCd yet,
// as the document might be reactivated later.
self.Gc();
self.gc();
}
pub(crate) fn resume(&self, can_gc: CanGc) {

View File

@@ -17,6 +17,8 @@ namespace ServoTestUtils {
[Exposed=Window]
LayoutResult forceLayout();
undefined js_backtrace();
undefined panic();
};

View File

@@ -135,16 +135,6 @@ partial interface Window {
[Replaceable] readonly attribute double devicePixelRatio;
};
// Proprietary extensions.
partial interface Window {
[Pref="dom_servo_helpers_enabled"]
undefined debug(DOMString arg);
[Pref="dom_servo_helpers_enabled"]
undefined gc();
[Pref="dom_servo_helpers_enabled"]
undefined js_backtrace();
};
// WebDriver extensions
partial interface Window {
// Shouldn't be public, but just to make things work for now

View File

@@ -29,7 +29,7 @@ fn test_multiprocess_preference_observer() {
.delegate(delegate.clone())
.build();
let result = evaluate_javascript(&servo_test, webview.clone(), "window.gc");
let result = evaluate_javascript(&servo_test, webview.clone(), "window.ServoTestUtils");
assert_eq!(result, Ok(JSValue::Undefined));
let mut prefs = prefs::get().clone();
@@ -40,7 +40,7 @@ fn test_multiprocess_preference_observer() {
webview.reload();
servo_test.spin(move || !delegate.load_status_changed.get());
let result = evaluate_javascript(&servo_test, webview.clone(), "window.gc");
let result = evaluate_javascript(&servo_test, webview.clone(), "window.ServoTestUtils");
assert!(matches!(result, Ok(JSValue::Object(..))));
}

View File

@@ -1,25 +0,0 @@
<div>
<img></img>
<div>
<img></img><img></img><img></img>
</div>
</div>
<script>
debug("hi");
var elem = document.documentElement;
debug("document.documentElement: " + elem);
debug("Document: " + Document);
debug("Node: " + Node);
debug("Document instanceof Node: " + (Document instanceof Node));
debug("elem instanceof Node: " + (elem instanceof Node));
debug("elem instanceof Document: " + (elem instanceof Document));
debug("document instanceof Document: " + (document instanceof Document));
debug("document instanceof Node: " + (document instanceof Node));
debug("elem.tagName: " + elem.tagName);
debug("elem.firstChild: " + elem.firstChild);
debug("elem.firstChild.tagName: " + elem.firstChild.tagName);
debug("elem.firstChild.nextSibling: " + elem.firstChild.nextSibling);
debug("elem.firstChild.nextSibling.tagName: " + elem.firstChild.nextSibling.tagName);
debug("elem.nextSibling: " + elem.nextSibling);
debug("elem.nextSibling.tagName: " + elem.nextSibling.tagName);
</script>

View File

@@ -1 +0,0 @@
debug("Hello, world!");

View File

@@ -14226,7 +14226,7 @@
]
],
"media_query_list_gc.html": [
"36c13b5305e79f216375c384594374f2606797ea",
"c24500e1674b802883e4a33bdfa7823a218c81b9",
[
null,
{}
@@ -14510,14 +14510,14 @@
]
],
"promise.html": [
"5925047287175f291c07ee88075359ed92a86e6a",
"c4ce6b3ae93bbe78749f7304a2a966e5888ddd71",
[
null,
{}
]
],
"prototypes.html": [
"478b89a6fb876477711c19e392d7e4d190bff7a0",
"1d145bdd0f8540a68015fead0c93189edee157ad",
[
null,
{}
@@ -14735,7 +14735,7 @@
]
],
"trace_null.html": [
"bb4f8c1fc52af604a16bf01b198e5ba2857fdbe9",
"6a1eae5b741b13efff8bbced5a36b9f52558979d",
[
null,
{}
@@ -14749,7 +14749,7 @@
]
],
"transitionend_safety.html": [
"b72766c357af9553f1f7411b8d27c404f1e3fcde",
"c993fbc61cd4d604b5dde050d308884bd409ff79",
[
null,
{}
@@ -14793,7 +14793,7 @@
]
],
"weakref.html": [
"4deccbe1e26a3f921eea85a4395394a55cc88be4",
"091ff3e67e2999c54fc6e9ee95f7fd331f94501a",
[
null,
{}

View File

@@ -4,10 +4,10 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(function() {
promise_test(async function() {
var mql = matchMedia("(min-width: 1000px)");
mql = null;
gc();
await TestUtils.gc();
window.resizeBy(1,1);
})
</script>

View File

@@ -52,13 +52,15 @@
});
}, 'Native reject callback gets argument');
promise_test(function(test) {
promise_test(async function(test) {
var t = new TestBinding;
var resolved;
var p = new Promise(function() {});
var start = Date.now();
t.resolvePromiseDelayed(p, 'success', 100);
test.step_timeout(function() { window.gc() }, 0);
await TestUtils.gc();
return p.then(function(v) {
var end = Date.now();
assert_greater_than_equal(end - start, 100);

View File

@@ -7,9 +7,9 @@
<body>
<fooá>foo</fooá>
<script>
test(function() {
promise_test(async function() {
assert_true(window.document instanceof Node, "Should be Node");
gc(); // ensure that our document rooting works; subsequent accesses should be valid.
await TestUtils.gc(); // ensure that our document rooting works; subsequent accesses should be valid.
assert_true(window.document instanceof Node, "Should be Node");
assert_equals(window.document.nodeType, Node.DOCUMENT_NODE);
assert_true(window.document.documentElement instanceof Node, "Should be Node");

View File

@@ -2,8 +2,8 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(function() {
promise_test(async function() {
new CustomEvent("foo", { detail: null });
gc();
await TestUtils.gc();
});
</script>

View File

@@ -15,10 +15,10 @@
t.step_timeout(function() {
elem.style.color = 'red';
t.step_timeout(function() {
t.step_timeout(async function() {
document.body.removeChild(elem);
elem = null;
window.gc();
await TestUtils.gc();
t.step_timeout(t.step_func_done(), 100);
}, 0);
}, 0);

View File

@@ -4,7 +4,7 @@
<script src="/resources/testharnessreport.js"></script>
</head>
<script>
test(function() {
promise_test(async function() {
// We don't use assert_equals() etc here because it somehow
// keeps hold of its passed arguments, and thus the weak
// reference is never freed before the end of the test.
@@ -16,11 +16,11 @@ test(function() {
t.interfaceAttributeWeak = url;
assert_true(t.interfaceAttributeWeak !== null);
gc();
await TestUtils.gc();
assert_true(t.interfaceAttributeWeak !== null);
url = null;
gc();
await TestUtils.gc();
assert_true(t.interfaceAttributeWeak === null);
}, "Weak references work");