Implement console.trace (#34629)

* Include unimplemented console methods in idl file

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Fix console.assert signature

The condition is optional and there can be multiple messages.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Implement console.trace

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* ./mach fmt

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Log stack trace when calling console.trace

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update wpt expectations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Include line/column info in console.trace logs

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Move option out of constant

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update mozjs

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2024-12-19 00:45:06 +01:00 committed by GitHub
parent ba56494eec
commit 28e330c9b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 143 additions and 45 deletions

14
Cargo.lock generated
View file

@ -429,7 +429,7 @@ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
"itertools 0.13.0", "itertools 0.10.5",
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
@ -1868,7 +1868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -4449,7 +4449,7 @@ dependencies = [
[[package]] [[package]]
name = "mozjs" name = "mozjs"
version = "0.14.1" version = "0.14.1"
source = "git+https://github.com/servo/mozjs#8ac195873f25c66c891478a291d762998fb151fe" source = "git+https://github.com/servo/mozjs#bb560b6276cc587b4706b3162160cf945210335a"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
@ -4462,7 +4462,7 @@ dependencies = [
[[package]] [[package]]
name = "mozjs_sys" name = "mozjs_sys"
version = "0.128.3-8" version = "0.128.3-8"
source = "git+https://github.com/servo/mozjs#8ac195873f25c66c891478a291d762998fb151fe" source = "git+https://github.com/servo/mozjs#bb560b6276cc587b4706b3162160cf945210335a"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
@ -5892,7 +5892,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -7157,7 +7157,7 @@ dependencies = [
"fastrand", "fastrand",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -8416,7 +8416,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]

View file

@ -278,20 +278,22 @@ impl ConsoleActor {
LogLevel::Warn => "warn", LogLevel::Warn => "warn",
LogLevel::Error => "error", LogLevel::Error => "error",
LogLevel::Clear => "clear", LogLevel::Clear => "clear",
_ => "log", LogLevel::Trace => "trace",
LogLevel::Log => "log",
} }
.to_owned(); .to_owned();
let console_api = ConsoleLog { let console_api = ConsoleLog {
level: level.clone(), level,
filename: console_message.filename.clone(), filename: console_message.filename,
line_number: console_message.line_number as u32, line_number: console_message.line_number as u32,
column_number: console_message.column_number as u32, column_number: console_message.column_number as u32,
time_stamp: SystemTime::now() time_stamp: SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.unwrap_or_default() .unwrap_or_default()
.as_millis() as u64, .as_millis() as u64,
arguments: vec![console_message.message.clone()], arguments: vec![console_message.message],
stacktrace: console_message.stacktrace,
}; };
self.cached_events self.cached_events

View file

@ -694,6 +694,7 @@ fn run_server(
filename: css_error.filename, filename: css_error.filename,
line_number: css_error.line as usize, line_number: css_error.line as usize,
column_number: css_error.column as usize, column_number: css_error.column as usize,
stacktrace: vec![],
}; };
handle_console_message( handle_console_message(
actors.clone(), actors.clone(),

View file

@ -3,16 +3,16 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::convert::TryFrom; use std::convert::TryFrom;
use std::io; use std::{io, ptr};
use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg}; use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg, StackFrame};
use js::jsapi::{self, ESClass, PropertyDescriptor}; use js::jsapi::{self, ESClass, PropertyDescriptor};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::wrappers::{ use js::rust::wrappers::{
GetBuiltinClass, GetPropertyKeys, JS_GetOwnPropertyDescriptorById, JS_GetPropertyById, GetBuiltinClass, GetPropertyKeys, JS_GetOwnPropertyDescriptorById, JS_GetPropertyById,
JS_IdToValue, JS_ValueToSource, JS_IdToValue, JS_ValueToSource,
}; };
use js::rust::{describe_scripted_caller, HandleValue, IdVector}; use js::rust::{describe_scripted_caller, CapturedJSStack, HandleValue, IdVector};
use crate::dom::bindings::codegen::Bindings::ConsoleBinding::consoleMethods; use crate::dom::bindings::codegen::Bindings::ConsoleBinding::consoleMethods;
use crate::dom::bindings::conversions::jsstring_to_str; use crate::dom::bindings::conversions::jsstring_to_str;
@ -27,8 +27,8 @@ const MAX_LOG_DEPTH: usize = 10;
/// The maximum elements in an object logged by console methods. /// The maximum elements in an object logged by console methods.
const MAX_LOG_CHILDREN: usize = 15; const MAX_LOG_CHILDREN: usize = 15;
// https://developer.mozilla.org/en-US/docs/Web/API/Console /// <https://developer.mozilla.org/en-US/docs/Web/API/Console>
pub struct Console(()); pub struct Console;
impl Console { impl Console {
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -36,12 +36,14 @@ impl Console {
if let Some(chan) = global.devtools_chan() { if let Some(chan) = global.devtools_chan() {
let caller = let caller =
unsafe { describe_scripted_caller(*GlobalScope::get_cx()) }.unwrap_or_default(); unsafe { describe_scripted_caller(*GlobalScope::get_cx()) }.unwrap_or_default();
let console_message = ConsoleMessage { let console_message = ConsoleMessage {
message, message,
log_level: level, log_level: level,
filename: caller.filename, filename: caller.filename,
line_number: caller.line as usize, line_number: caller.line as usize,
column_number: caller.col as usize, column_number: caller.col as usize,
stacktrace: get_js_stack(*GlobalScope::get_cx()),
}; };
let worker_id = global let worker_id = global
.downcast::<WorkerGlobalScope>() .downcast::<WorkerGlobalScope>()
@ -259,17 +261,20 @@ impl consoleMethods<crate::DomTypeHolder> for Console {
console_messages(global, messages, LogLevel::Error) console_messages(global, messages, LogLevel::Error)
} }
/// <https://console.spec.whatwg.org/#trace>
fn Trace(_cx: JSContext, global: &GlobalScope, messages: Vec<HandleValue>) {
console_messages(global, messages, LogLevel::Trace)
}
// https://developer.mozilla.org/en-US/docs/Web/API/Console/assert // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
fn Assert(_cx: JSContext, global: &GlobalScope, condition: bool, message: HandleValue) { fn Assert(_cx: JSContext, global: &GlobalScope, condition: bool, messages: Vec<HandleValue>) {
if !condition { if !condition {
let message = if message.is_undefined() { let message = DOMString::from(format!(
DOMString::from("no message") "Assertion failed: {}",
} else { stringify_handle_values(messages)
stringify_handle_value(message) ));
}; console_message(global, message, LogLevel::Error);
let message = DOMString::from(format!("Assertion failed: {}", message)); }
console_message(global, message, LogLevel::Error)
};
} }
// https://console.spec.whatwg.org/#time // https://console.spec.whatwg.org/#time
@ -331,3 +336,85 @@ impl consoleMethods<crate::DomTypeHolder> for Console {
} }
} }
} }
#[allow(unsafe_code)]
fn get_js_stack(cx: *mut jsapi::JSContext) -> Vec<StackFrame> {
const MAX_FRAME_COUNT: u32 = 128;
let mut frames = vec![];
rooted!(in(cx) let mut handle = ptr::null_mut());
let captured_js_stack = unsafe { CapturedJSStack::new(cx, handle, Some(MAX_FRAME_COUNT)) };
let Some(captured_js_stack) = captured_js_stack else {
return frames;
};
captured_js_stack.for_each_stack_frame(|frame| {
rooted!(in(cx) let mut result: *mut jsapi::JSString = ptr::null_mut());
// Get function name
unsafe {
jsapi::GetSavedFrameFunctionDisplayName(
cx,
ptr::null_mut(),
frame.into(),
result.handle_mut().into(),
jsapi::SavedFrameSelfHosted::Include,
);
}
let function_name = if let Some(nonnull_result) = ptr::NonNull::new(*result) {
unsafe { jsstring_to_str(cx, nonnull_result) }.into()
} else {
"<anonymous>".into()
};
// Get source file name
result.set(ptr::null_mut());
unsafe {
jsapi::GetSavedFrameSource(
cx,
ptr::null_mut(),
frame.into(),
result.handle_mut().into(),
jsapi::SavedFrameSelfHosted::Include,
);
}
let filename = if let Some(nonnull_result) = ptr::NonNull::new(*result) {
unsafe { jsstring_to_str(cx, nonnull_result) }.into()
} else {
"<anonymous>".into()
};
// get line/column number
let mut line_number = 0;
unsafe {
jsapi::GetSavedFrameLine(
cx,
ptr::null_mut(),
frame.into(),
&mut line_number,
jsapi::SavedFrameSelfHosted::Include,
);
}
let mut column_number = jsapi::JS::TaggedColumnNumberOneOrigin { value_: 0 };
unsafe {
jsapi::GetSavedFrameColumn(
cx,
ptr::null_mut(),
frame.into(),
&mut column_number,
jsapi::SavedFrameSelfHosted::Include,
);
}
let frame = StackFrame {
filename,
function_name,
line_number,
column_number: column_number.value_,
};
frames.push(frame);
});
frames
}

View file

@ -5,16 +5,20 @@
// https://console.spec.whatwg.org/ // https://console.spec.whatwg.org/
[ClassString="Console", [ClassString="Console",
Exposed=(Window,Worker,Worklet)] Exposed=*]
namespace console { namespace console {
// Logging // Logging
undefined log(any... messages); undefined assert(optional boolean condition = false, any... data);
undefined debug(any... messages);
undefined info(any... messages);
undefined warn(any... messages);
undefined error(any... messages);
undefined assert(boolean condition, optional any message);
undefined clear(); undefined clear();
undefined debug(any... messages);
undefined error(any... messages);
undefined info(any... messages);
undefined log(any... messages);
// undefined table(optional any tabularData, optional sequence<DOMString> properties);
undefined trace(any... data);
undefined warn(any... messages);
// undefined dir(optional any item, optional object? options);
// undefined dirxml(any... data);
// Counting // Counting
undefined count(optional DOMString label = "default"); undefined count(optional DOMString label = "default");

View file

@ -280,6 +280,7 @@ pub enum LogLevel {
Warn, Warn,
Error, Error,
Clear, Clear,
Trace,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
@ -290,6 +291,21 @@ pub struct ConsoleMessage {
pub filename: String, pub filename: String,
pub line_number: usize, pub line_number: usize,
pub column_number: usize, pub column_number: usize,
pub stacktrace: Vec<StackFrame>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct StackFrame {
pub filename: String,
#[serde(rename = "functionName")]
pub function_name: String,
#[serde(rename = "columnNumber")]
pub column_number: u32,
#[serde(rename = "lineNumber")]
pub line_number: u32,
} }
bitflags! { bitflags! {
@ -327,7 +343,7 @@ pub struct ConsoleLog {
pub column_number: u32, pub column_number: u32,
pub time_stamp: u64, pub time_stamp: u64,
pub arguments: Vec<String>, pub arguments: Vec<String>,
// pub stacktrace: Vec<...>, pub stacktrace: Vec<StackFrame>,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]

View file

@ -1,13 +1,7 @@
[idlharness.any.html] [idlharness.any.html]
[console namespace: operation assert(optional boolean, any...)]
expected: FAIL
[console namespace: operation table(optional any, optional sequence<DOMString>)] [console namespace: operation table(optional any, optional sequence<DOMString>)]
expected: FAIL expected: FAIL
[console namespace: operation trace(any...)]
expected: FAIL
[console namespace: operation dir(optional any, optional object?)] [console namespace: operation dir(optional any, optional object?)]
expected: FAIL expected: FAIL
@ -16,15 +10,9 @@
[idlharness.any.worker.html] [idlharness.any.worker.html]
[console namespace: operation assert(optional boolean, any...)]
expected: FAIL
[console namespace: operation table(optional any, optional sequence<DOMString>)] [console namespace: operation table(optional any, optional sequence<DOMString>)]
expected: FAIL expected: FAIL
[console namespace: operation trace(any...)]
expected: FAIL
[console namespace: operation dir(optional any, optional object?)] [console namespace: operation dir(optional any, optional object?)]
expected: FAIL expected: FAIL