script: Extract ErrorInfo from pending exception stack (#43632)

When reporting an exception attempt to extract `ErrorInfo` from the
stack of the exception.

Testing: Covered by existing tests, expectations updated.

Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com>
This commit is contained in:
Gae24
2026-04-25 11:06:46 +02:00
committed by GitHub
parent ac9f747509
commit e0d7542d37
10 changed files with 40 additions and 69 deletions

8
Cargo.lock generated
View File

@@ -5068,9 +5068,9 @@ dependencies = [
[[package]]
name = "mozjs"
version = "0.15.9"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "475b6fbb284d1229f4108c5cf3842da47ee319b45c9a6c24358f41a0640a64f5"
checksum = "2231fbdbc1ee3eaae2d2859792f5f1a6ca065fdebecccfe9447e531292883126"
dependencies = [
"bindgen",
"cc",
@@ -5083,9 +5083,9 @@ dependencies = [
[[package]]
name = "mozjs_sys"
version = "0.140.8-4"
version = "0.140.10-1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59e4cb2d3bf4a10d2f0005331419fb12b21ff260711ded47b9590ed432fad41e"
checksum = "4d2a078fe5215b5afe55502b2a5c40b2119d81492eafc246373dc9cfda332129"
dependencies = [
"bindgen",
"cc",

View File

@@ -111,7 +111,7 @@ indexmap = { version = "2.14.0", features = ["std"] }
inventory = { version = "0.3.24" }
ipc-channel = "0.21"
itertools = "0.14"
js = { package = "mozjs", version = "=0.15.9", default-features = false, features = ["libz-sys", "intl"] }
js = { package = "mozjs", version = "=0.15.11", default-features = false, features = ["libz-sys", "intl"] }
keyboard-types = { version = "0.8.3", features = ["serde", "webdriver"] }
kurbo = { version = "0.12", features = ["euclid"] }
libc = "0.2"

View File

@@ -14,6 +14,7 @@ use embedder_traits::JavaScriptErrorInfo;
use js::context::JSContext;
use js::conversions::jsstr_to_string;
use js::error::{throw_range_error, throw_type_error};
use js::gc::{HandleObject, HandleValue, MutableHandleValue};
#[cfg(feature = "js_backtrace")]
use js::jsapi::StackFormat as JSStackFormat;
use js::jsapi::{ExceptionStackBehavior, JS_ClearPendingException, JS_IsExceptionPending};
@@ -21,7 +22,7 @@ use js::jsval::UndefinedValue;
use js::realm::CurrentRealm;
use js::rust::wrappers::{JS_ErrorFromException, JS_GetPendingException, JS_SetPendingException};
use js::rust::wrappers2::JS_GetProperty;
use js::rust::{HandleObject, HandleValue, MutableHandleValue, describe_scripted_caller};
use js::rust::{describe_scripted_caller, error_info_from_exception_stack};
use libc::c_uint;
use script_bindings::conversions::SafeToJSValConvertible;
pub(crate) use script_bindings::error::*;
@@ -348,28 +349,34 @@ impl ErrorInfo {
/// Report a pending exception, thereby clearing it.
pub(crate) fn report_pending_exception(cx: SafeJSContext, realm: InRealm, can_gc: CanGc) {
rooted!(in(*cx) let mut value = UndefinedValue());
if take_pending_exception(cx, value.handle_mut()) {
GlobalScope::from_safe_context(cx, realm).report_an_exception(cx, value.handle(), can_gc);
if let Some(error_info) = error_info_from_pending_exception(cx, value.handle_mut(), can_gc) {
GlobalScope::from_safe_context(cx, realm).report_an_error(
error_info,
value.handle(),
can_gc,
);
}
}
fn take_pending_exception(cx: SafeJSContext, value: MutableHandleValue) -> bool {
fn error_info_from_pending_exception(
cx: SafeJSContext,
value: MutableHandleValue,
_can_gc: CanGc,
) -> Option<ErrorInfo> {
unsafe {
if !JS_IsExceptionPending(*cx) {
return false;
}
}
unsafe {
if !JS_GetPendingException(*cx, value) {
JS_ClearPendingException(*cx);
error!("Uncaught exception: JS_GetPendingException failed");
return false;
return None;
}
JS_ClearPendingException(*cx);
let error_info = error_info_from_exception_stack(*cx, value.into())?;
Some(ErrorInfo {
message: error_info.message,
filename: error_info.filename,
lineno: error_info.line,
column: error_info.col,
})
}
true
}
pub(crate) fn javascript_error_info_from_error_info(
@@ -417,11 +424,9 @@ pub(crate) fn take_and_report_pending_exception_for_api(
let in_realm = InRealm::Already(&in_realm_proof);
rooted!(&in(cx) let mut value = UndefinedValue());
if !take_pending_exception(cx.into(), value.handle_mut()) {
return None;
}
let error_info =
error_info_from_pending_exception(cx.into(), value.handle_mut(), CanGc::from_cx(cx))?;
let error_info = ErrorInfo::from_value(value.handle(), cx.into(), CanGc::from_cx(cx));
let return_value = javascript_error_info_from_error_info(cx, &error_info, value.handle());
GlobalScope::from_safe_context(cx.into(), in_realm).report_an_error(
error_info,

View File

@@ -13,8 +13,8 @@ use js::jsapi::{ExceptionStackBehavior, Heap, JSScript, SetScriptPrivate};
use js::jsval::{PrivateValue, UndefinedValue};
use js::panic::maybe_resume_unwind;
use js::rust::wrappers2::{
Compile1, JS_ClearPendingException, JS_ExecuteScript, JS_GetPendingException,
JS_GetScriptPrivate, JS_SetPendingException,
Compile1, JS_ClearPendingException, JS_ExecuteScript, JS_GetScriptPrivate,
JS_IsExceptionPending, JS_SetPendingException,
};
use js::rust::{CompileOptionsWrapper, MutableHandleValue, transform_str_to_source_text};
use script_bindings::cformat;
@@ -24,11 +24,12 @@ use servo_url::ServoUrl;
use crate::DomTypeHolder;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::error::{Error, ErrorResult, report_pending_exception};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::realms::{InRealm, enter_auto_realm};
use crate::script_module::{
ModuleScript, ModuleSource, ModuleTree, RethrowError, ScriptFetchOptions,
};
@@ -157,11 +158,13 @@ impl GlobalScope {
// TODO Step 3. Record classic script execution start time given script.
let mut realm = enter_auto_realm(cx, self);
let cx = &mut realm.current_realm();
// Step 4. Prepare to run script given settings.
// Once dropped this will run "Step 9. Clean up after running script" steps
run_a_script::<DomTypeHolder, _>(self, || {
// Step 5. Let evaluationStatus be null.
rooted!(&in(cx) let mut evaluation_status = UndefinedValue());
let mut result = false;
match script.record {
@@ -188,10 +191,8 @@ impl GlobalScope {
},
}
unsafe { JS_GetPendingException(cx, evaluation_status.handle_mut()) };
// Step 8. If evaluationStatus is an abrupt completion, then:
if !evaluation_status.is_undefined() {
if unsafe { JS_IsExceptionPending(cx) } {
warn!("Error evaluating script");
match (rethrow_errors, script.muted_errors) {
@@ -208,13 +209,10 @@ impl GlobalScope {
},
// Step 8.3. Otherwise, rethrow errors is false. Perform the following steps:
_ => {
unsafe { JS_ClearPendingException(cx) };
let in_realm_proof = cx.into();
let in_realm = InRealm::Already(&in_realm_proof);
// Report an exception given by evaluationStatus.[[Value]] for script's settings object's global object.
self.report_an_exception(
cx.into(),
evaluation_status.handle(),
CanGc::from_cx(cx),
);
report_pending_exception(cx.into(), in_realm, CanGc::from_cx(cx));
// Return evaluationStatus.
return Err(Error::JSFailed);

View File

@@ -1,3 +0,0 @@
[data-url.html]
[Test data URL and scripts errors]
expected: FAIL

View File

@@ -1,7 +0,0 @@
[window-onerror-runtime-error-throw.html]
[correct line number passed to window.onerror]
expected: FAIL
[correct url passed to window.onerror]
expected: FAIL

View File

@@ -2,8 +2,3 @@
expected: ERROR
[report-error-cross-origin.sub.any.worker.html]
[WorkerGlobalScope error event: lineno]
expected: FAIL
[WorkerGlobalScope error event: filename]
expected: FAIL

View File

@@ -2,8 +2,3 @@
expected: ERROR
[report-error-redirect-to-cross-origin.sub.any.worker.html]
[WorkerGlobalScope error event: lineno]
expected: FAIL
[WorkerGlobalScope error event: filename]
expected: FAIL

View File

@@ -1,10 +1,4 @@
[report-error-setTimeout-cross-origin.sub.any.worker.html]
[WorkerGlobalScope error event: lineno]
expected: FAIL
[WorkerGlobalScope error event: filename]
expected: FAIL
[report-error-setTimeout-cross-origin.sub.any.sharedworker.html]
expected: ERROR

View File

@@ -1,10 +1,4 @@
[report-error-setTimeout-redirect-to-cross-origin.sub.any.worker.html]
[WorkerGlobalScope error event: lineno]
expected: FAIL
[WorkerGlobalScope error event: filename]
expected: FAIL
[report-error-setTimeout-redirect-to-cross-origin.sub.any.sharedworker.html]
expected: ERROR