Compare commits

...

10 Commits

Author SHA1 Message Date
Tim van der Lippe
6d70fcda1b script: Fire selectionchange events for textcontrol elements (#44461)
Part of #7492

Testing: WPT

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2026-04-25 13:04:22 +00:00
Tim van der Lippe
6f43bba0f4 script: Pass &mut JSContext to more attribute setters (#44494)
Part of #42812

Testing: it compiles

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2026-04-25 12:51:02 +00:00
Jan Varga
902d5d10d9 storage: Add support for temporary storage (#44433)
Add support for temporary storage via a new config option
`temporary_storage`
and a corresponding command-line argument `--temporary-storage`.

When enabled, client storage uses a storage directory
(e.g. `clientstorage/temporary/<uuid>`) instead of the shared default
location.
This can be used to provide isolation between concurrent servo
instances.

This is especially useful for WPT runs, where multiple Servo instances
may
execute in parallel and would otherwise share the same storage, leading
to
cross-test interference.

Based on that, this PR also updates the WPT runner to enable temporary
storage
by default.

Testing: Manual testing and a full try run.

Signed-off-by: Jan Varga <jvarga@igalia.com>
2026-04-25 10:18:21 +00:00
Martin Robinson
1a9808d421 layout_api: Use a "type bundle" to encapsulate all layout DOM types (#44454)
This change creates a new "type bundle" trait, which holds all of the
concrete implementations of the layout DOM types. The benefit here is
that each implementation of a layout DOM type needs a single associated
type. In addition, and most importantly, `Layout` itself only needs to
parameterized over a single type (the concrete type bundle). This will
make parameterizing layout a lot friendlier.

The downside is that extracting the concrete type from the type bundle
is a bit ugly in Rust, so the change also exposes some type aliases to
make this nicer. In the future, default associated types can make
things a bit simpler as well.

Testing: This should not change behavior so no new tests are necessary.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-25 09:21:16 +00:00
Gae24
e0d7542d37 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>
2026-04-25 09:06:46 +00:00
Martin Robinson
ac9f747509 layout: Remove some newline-related dead code from shaping (#44485)
In #44436, I neglected to remove some hard line break-related code from
the shaping code. This code is effectively dead code now that
hard line breaks never make it to this code path, so it can safely be
removed.

Testing: This should not change behavior, so existing tests should
suffice.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-25 08:38:45 +00:00
Abbas Olanrewaju Sarafa
55ec768fb7 script: Refactor HttpsState move from Document to GlobalScope (#44407)
Refactored `HttpsState` by moving it from `Document` to `GlobalScope`

Testing: `./mach test-wpt
tests/wpt/tests/mixed-content/csp.https.window.js` & `./mach test-wpt
tests/wpt/tests/fetch/api/request/destination/fetch-destination.https.html`
passed locally. `./mach try` failed due to billing issue on my account.
Fixes: #44342

Signed-off-by: Sabb <sarafaabbas@gmail.com>
2026-04-25 08:22:30 +00:00
rovertrack
b768a93a47 embedder_traits: Move Wakelock trait to Embedder_traits (#44343)
Moved the Wakelock trait from wakelock to embedder_trait
[components/shared/embedder/lib.rs](https://github.com/servo/servo/compare/main...rovertrack:servo:issue-44239?expand=1#diff-71d8f825ba6f796e220d49bc548e9a34783586a5a597edc6311a26e31dbf7020)
Added Required dependency in `components/shared/embedder/lib.rs`
imported the Wakelock trait from `components/shared/embedder/lib.rs` to
`components/wakelock/lib.rs`
Added dependency `embedder_trait`
[components/wakelock/Cargo.toml](https://github.com/servo/servo/compare/main...rovertrack:servo:issue-44239?expand=1#diff-11c410f6e5a491394348dac2f1402d2b29bdc9d2d1320059d12589eb1feb2504)

Testing: All expected test pass as there is no change in flow of working

Fixes: #44239

---------

Signed-off-by: Rover track <rishan.pgowda@gmail.com>
2026-04-25 08:07:56 +00:00
webbeef
87daffa1fd script: Use a u16 for prototype id to speed up Castable::is<T>() (#44364)
`Castable::is<T>()` calls `get_dom_class()` in a way that incurs a
roundtrip to the JS engine and
multiple pointer indirections:

self.reflector().get_jsobject().get()  -> JSObject
Object.shape -> BaseShape.clasp    -> JSClass
DOMJSClass.dom_class              -> DOMClass
interface_chain[depth] == ID       -> bool

Since the ID doesn't change after creation of reflectors, we can instead
store the ID on the reflector
and avoid most of that cost. The trick is to use a Depth First Search to
generate IDs that allow a
range check for `Castable::is<T>`. An example of IDs in the DFS looks
like:

EventTarget = 0
  Node = 1
    CharacterData = 2
      Text = 3
      CDATASection = 4
      Comment = 5
      ProcessingInstruction = 6
    Document = 7
      HTMLDocument = 8
      XMLDocument = 9

Testing: Green try run at
https://github.com/webbeef/servo/actions/runs/24640508040 and manual
testing with general browsing.

Signed-off-by: webbeef <me@webbeef.org>
2026-04-25 07:54:35 +00:00
Josh Matthews
d0908b0342 script: Wrap debugger global scope pointers in debuggee's realm. (#44386)
Debug mozjs builds don't like when we try to trace members of one global
object (realm) that originate from another global object (realm). This
triggers when we use `Dom<DebuggerGlobalScope>` in our global objects,
since all `Dom<T>` pointers are expected to in the same realm as the
containing object. To address this, we store a JS value of the debugger
global's reflector wrapped in the current global's relam, and use value
to reconstitute a `DebuggerGlobalScope` pointer as needed.

Testing: We don't run debug-mozjs in CI, but this change makes a bunch
of tests stop asserting.
Fixes: #44385

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2026-04-25 07:45:14 +00:00
66 changed files with 662 additions and 523 deletions

9
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",
@@ -8886,6 +8886,7 @@ name = "servo-wakelock"
version = "0.1.0"
dependencies = [
"serde",
"servo-embedder-traits",
]
[[package]]

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

@@ -60,6 +60,9 @@ pub struct Opts {
/// Directory for a default config directory
pub config_dir: Option<PathBuf>,
/// Use temporary storage (data on disk will not persist across restarts).
pub temporary_storage: bool,
/// Path to PEM encoded SSL CA certificate store.
pub certificate_path: Option<String>,
@@ -222,6 +225,7 @@ impl Default for Opts {
sandbox: false,
debug: Default::default(),
config_dir: None,
temporary_storage: false,
shaders_path: None,
certificate_path: None,
ignore_certificate_errors: false,

View File

@@ -111,8 +111,8 @@ use embedder_traits::{
GenericEmbedderProxy, InputEvent, InputEventAndId, InputEventOutcome, JSValue,
JavaScriptEvaluationError, JavaScriptEvaluationId, KeyboardEvent, MediaSessionActionType,
MediaSessionEvent, MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent,
NewWebViewDetails, PaintHitTestResult, Theme, ViewportDetails, WebDriverCommandMsg,
WebDriverLoadStatus, WebDriverScriptCommand,
NewWebViewDetails, PaintHitTestResult, Theme, ViewportDetails, WakeLockDelegate, WakeLockType,
WebDriverCommandMsg, WebDriverLoadStatus, WebDriverScriptCommand,
};
use euclid::Size2D;
use euclid::default::Size2D as UntypedSize2D;
@@ -168,7 +168,6 @@ use servo_constellation_traits::{
TraversalDirection, UserContentManagerAction, WindowSizeType,
};
use servo_url::{Host, ImmutableOrigin, ServoUrl};
use servo_wakelock::{WakeLockProvider, WakeLockType};
use storage_traits::StorageThreads;
use storage_traits::client_storage::ClientStorageThreadMessage;
use storage_traits::indexeddb::{IndexedDBThreadMsg, SyncOperation};
@@ -493,7 +492,7 @@ pub struct Constellation<STF, SWF> {
screen_wake_lock_count: u32,
/// Provider for OS-level screen wake lock acquisition and release.
wake_lock_provider: Box<dyn WakeLockProvider>,
wake_lock_provider: Box<dyn WakeLockDelegate>,
/// The image bytes associated with the BrokenImageIcon embedder resource.
/// Read during startup and provided to image caches that are created
@@ -594,7 +593,7 @@ pub struct InitialConstellationState {
pub async_runtime: Box<dyn AsyncRuntime>,
/// The wake lock provider for acquiring and releasing OS-level screen wake locks.
pub wake_lock_provider: Box<dyn WakeLockProvider>,
pub wake_lock_provider: Box<dyn WakeLockDelegate>,
}
/// When we are exiting a pipeline, we can either force exiting or not. A normal exit

View File

@@ -278,12 +278,9 @@ impl TextRunSegment {
// Split off any trailing whitespace into a separate glyph run.
let mut whitespace = slice.end..slice.end;
let mut rev_char_indices = word.char_indices().rev().peekable();
let rev_char_indices = word.char_indices().rev().peekable();
let mut ends_with_whitespace = false;
let ends_with_newline = rev_char_indices
.peek()
.is_some_and(|&(_, character)| character == '\n');
if let Some((first_white_space_index, first_white_space_character)) = rev_char_indices
.take_while(|&(_, character)| char_is_whitespace(character))
.last()
@@ -291,14 +288,12 @@ impl TextRunSegment {
ends_with_whitespace = true;
whitespace.start = slice.start + first_white_space_index;
// If line breaking for a piece of text that has `white-space-collapse: break-spaces` there
// is a line break opportunity *after* every preserved space, but not before. This means
// that we should not split off the first whitespace, unless that white-space is a preserved
// newline.
// If line breaking for a piece of text that has `white-space-collapse:
// break-spaces` there is a line break opportunity *after* every preserved space,
// but not before. This means that we should not split off the first whitespace.
//
// An exception to this is if the style tells us that we can break in the middle of words.
if text_style.white_space_collapse == WhiteSpaceCollapse::BreakSpaces &&
first_white_space_character != '\n' &&
!can_break_anywhere
{
whitespace.start += first_white_space_character.len_utf8();
@@ -352,25 +347,7 @@ impl TextRunSegment {
continue;
}
// The breaker breaks after every newline, so either there is none,
// or there is exactly one at the very end. In the latter case,
// split it into a different run. That's because shaping considers
// a newline to have the same advance as a space, but during layout
// we want to treat the newline as having no advance.
if ends_with_newline && whitespace.len() > 1 {
self.shape_and_push_range(
&(whitespace.start..whitespace.end - 1),
formatting_context_text,
&options,
);
self.shape_and_push_range(
&(whitespace.end - 1..whitespace.end),
formatting_context_text,
&options,
);
} else {
self.shape_and_push_range(&whitespace, formatting_context_text, &options);
}
self.shape_and_push_range(&whitespace, formatting_context_text, &options);
}
}
}

View File

@@ -10,9 +10,9 @@ use embedder_traits::UntrustedNodeAddress;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
use itertools::Itertools;
use layout_api::{
AxesOverflow, BoxAreaType, CSSPixelRectIterator, LayoutElement, LayoutElementType, LayoutNode,
LayoutNodeType, OffsetParentResponse, PhysicalSides, ScrollContainerQueryFlags,
ScrollContainerResponse,
AxesOverflow, BoxAreaType, CSSPixelRectIterator, DangerousStyleElementOf, LayoutElement,
LayoutElementType, LayoutNode, LayoutNodeType, OffsetParentResponse, PhysicalSides,
ScrollContainerQueryFlags, ScrollContainerResponse,
};
use paint_api::display_list::ScrollTree;
use script::layout_dom::ServoLayoutNode;
@@ -1483,7 +1483,7 @@ where
};
context
.stylist
.compute_for_declarations::<E::ConcreteDangerousStyleElement>(
.compute_for_declarations::<DangerousStyleElementOf<'dom, E::ConcreteTypeBundle>>(
&context.guards,
parent_style,
ServoArc::new(shared_lock.wrap(declarations)),

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,6 +13,7 @@ use embedder_traits::resources::{self, Resource};
use js::context::JSContext;
use js::rust::wrappers2::JS_DefineDebuggerObject;
use net_traits::ResourceThreads;
use net_traits::response::HttpsState;
use profile_traits::{mem, time};
use servo_base::generic_channel::{GenericCallback, GenericSender, channel};
use servo_base::id::{Index, PipelineId, PipelineNamespaceId};
@@ -117,6 +118,7 @@ impl DebuggerGlobalScope {
None,
false,
None, // font_context
HttpsState::None,
),
devtools_to_script_sender,
get_possible_breakpoints_result_sender: RefCell::new(None),

View File

@@ -7,6 +7,7 @@ use js::context::JSContext;
use js::jsapi::{Heap, JSObject};
use js::jsval::UndefinedValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue};
use net_traits::response::HttpsState;
use servo_base::id::PipelineId;
use servo_constellation_traits::{ScriptToConstellationMessage, StructuredSerializedData};
use servo_url::ServoUrl;
@@ -69,6 +70,7 @@ impl DissimilarOriginWindow {
Some(global_to_clone_from.is_secure_context()),
false,
global_to_clone_from.font_context().cloned(),
HttpsState::None,
),
window_proxy: Dom::from_ref(window_proxy),
location: Default::default(),

View File

@@ -42,7 +42,6 @@ use net_traits::pub_domains::is_pub_domain;
use net_traits::request::{
InsecureRequestsPolicy, PreloadId, PreloadKey, PreloadedResources, RequestBuilder,
};
use net_traits::response::HttpsState;
use percent_encoding::percent_decode;
use profile_traits::generic_channel as profile_generic_channel;
use profile_traits::time::TimerMetadataFrameType;
@@ -438,9 +437,7 @@ pub(crate) struct Document {
unload_event_start: Cell<Option<CrossProcessInstant>>,
#[no_trace]
unload_event_end: Cell<Option<CrossProcessInstant>>,
/// <https://html.spec.whatwg.org/multipage/#concept-document-https-state>
#[no_trace]
https_state: Cell<HttpsState>,
/// The document's origin.
#[no_trace]
origin: DomRefCell<MutableOrigin>,
@@ -842,10 +839,6 @@ impl Document {
self.content_type.matches(APPLICATION, "xhtml+xml")
}
pub(crate) fn set_https_state(&self, https_state: HttpsState) {
self.https_state.set(https_state);
}
pub(crate) fn is_fully_active(&self) -> bool {
self.activity.get() == DocumentActivity::FullyActive
}
@@ -3590,7 +3583,6 @@ impl Document {
load_event_end: Cell::new(Default::default()),
unload_event_start: Cell::new(Default::default()),
unload_event_end: Cell::new(Default::default()),
https_state: Cell::new(HttpsState::None),
origin: DomRefCell::new(origin),
referrer,
target_element: MutNullableDom::new(None),

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

@@ -771,6 +771,7 @@ impl GlobalScope {
inherited_secure_context: Option<bool>,
unminify_js: bool,
font_context: Option<Arc<FontContext>>,
initial_https_state: HttpsState,
) -> Self {
Self {
task_manager: Default::default(),
@@ -809,7 +810,7 @@ impl GlobalScope {
#[cfg(feature = "webgpu")]
gpu_devices: DomRefCell::new(HashMapTracedValues::new_fx()),
frozen_supported_performance_entry_types: CachedFrozenArray::new(),
https_state: Cell::new(HttpsState::None),
https_state: Cell::new(initial_https_state),
console_group_stack: DomRefCell::new(Vec::new()),
console_count_map: Default::default(),
inherited_secure_context,

View File

@@ -41,7 +41,7 @@ impl HTMLBodyElement {
}
pub(crate) fn new(
cx: &mut js::context::JSContext,
cx: &mut JSContext,
local_name: LocalName,
prefix: Option<Prefix>,
document: &Document,
@@ -73,11 +73,14 @@ impl HTMLBodyElementMethods<crate::DomTypeHolder> for HTMLBodyElement {
make_getter!(Background, "background");
/// <https://html.spec.whatwg.org/multipage/#dom-body-background>
fn SetBackground(&self, input: DOMString, can_gc: CanGc) {
fn SetBackground(&self, cx: &mut JSContext, input: DOMString) {
let value =
AttrValue::from_resolved_url(&self.owner_document().base_url().get_arc(), input.into());
self.upcast::<Element>()
.set_attribute(&local_name!("background"), value, can_gc);
self.upcast::<Element>().set_attribute(
&local_name!("background"),
value,
CanGc::from_cx(cx),
);
}
// https://html.spec.whatwg.org/multipage/#windoweventhandlers
@@ -146,12 +149,7 @@ impl VirtualMethods for HTMLBodyElement {
}
}
fn attribute_mutated(
&self,
cx: &mut js::context::JSContext,
attr: &Attr,
mutation: AttributeMutation,
) {
fn attribute_mutated(&self, cx: &mut JSContext, attr: &Attr, mutation: AttributeMutation) {
let do_super_mutate = match (attr.local_name(), mutation) {
(name, AttributeMutation::Set(..)) if name.starts_with("on") => {
let document = self.owner_document();

View File

@@ -5,6 +5,7 @@
use cssparser::match_ignore_ascii_case;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, local_name, ns};
use js::context::JSContext;
use js::rust::HandleObject;
use style::attr::AttrValue;
use style::color::AbsoluteColor;
@@ -43,7 +44,7 @@ impl HTMLFontElement {
}
pub(crate) fn new(
cx: &mut js::context::JSContext,
cx: &mut JSContext,
local_name: LocalName,
prefix: Option<Prefix>,
document: &Document,
@@ -100,15 +101,15 @@ impl HTMLFontElementMethods<crate::DomTypeHolder> for HTMLFontElement {
make_getter!(Face, "face");
// https://html.spec.whatwg.org/multipage/#dom-font-face
make_atomic_setter!(SetFace, "face");
make_atomic_setter!(cx, SetFace, "face");
// https://html.spec.whatwg.org/multipage/#dom-font-size
make_getter!(Size, "size");
/// <https://html.spec.whatwg.org/multipage/#dom-font-size>
fn SetSize(&self, value: DOMString, can_gc: CanGc) {
fn SetSize(&self, cx: &mut JSContext, value: DOMString) {
let element = self.upcast::<Element>();
element.set_attribute(&local_name!("size"), parse_size(&value), can_gc);
element.set_attribute(&local_name!("size"), parse_size(&value), CanGc::from_cx(cx));
}
}

View File

@@ -62,7 +62,7 @@ impl HTMLHRElementMethods<crate::DomTypeHolder> for HTMLHRElement {
make_getter!(Align, "align");
// https://html.spec.whatwg.org/multipage/#dom-hr-align
make_atomic_setter!(SetAlign, "align");
make_atomic_setter!(cx, SetAlign, "align");
// https://html.spec.whatwg.org/multipage/#dom-hr-color
make_getter!(Color, "color");
@@ -74,19 +74,19 @@ impl HTMLHRElementMethods<crate::DomTypeHolder> for HTMLHRElement {
make_bool_getter!(NoShade, "noshade");
// https://html.spec.whatwg.org/multipage/#dom-hr-noshade
make_bool_setter!(SetNoShade, "noshade");
make_bool_setter!(cx, SetNoShade, "noshade");
// https://html.spec.whatwg.org/multipage/#dom-hr-size
make_getter!(Size, "size");
// https://html.spec.whatwg.org/multipage/#dom-hr-size
make_dimension_setter!(SetSize, "size");
make_dimension_setter!(cx, SetSize, "size");
// <https://html.spec.whatwg.org/multipage/#dom-hr-width>
make_getter!(Width, "width");
// <https://html.spec.whatwg.org/multipage/#dom-hr-width>
make_dimension_setter!(SetWidth, "width");
make_dimension_setter!(cx, SetWidth, "width");
}
/// The result of applying the presentational hint for the `size` attribute.

View File

@@ -67,7 +67,7 @@ impl HTMLTableColElementMethods<crate::DomTypeHolder> for HTMLTableColElement {
make_getter!(Width, "width");
// <https://html.spec.whatwg.org/multipage/#dom-col-width>
make_dimension_setter!(SetWidth, "width");
make_dimension_setter!(cx, SetWidth, "width");
}
impl<'dom> LayoutDom<'dom, HTMLTableColElement> {

View File

@@ -471,15 +471,15 @@ impl HTMLTableElementMethods<crate::DomTypeHolder> for HTMLTableElement {
make_nonzero_dimension_setter!(SetWidth, "width");
// <https://html.spec.whatwg.org/multipage/#dom-table-align>
make_setter!(SetAlign, "align");
make_setter!(cx, SetAlign, "align");
make_getter!(Align, "align");
// <https://html.spec.whatwg.org/multipage/#dom-table-cellpadding>
make_setter!(SetCellPadding, "cellpadding");
make_setter!(cx, SetCellPadding, "cellpadding");
make_getter!(CellPadding, "cellpadding");
// <https://html.spec.whatwg.org/multipage/#dom-table-cellspacing>
make_setter!(SetCellSpacing, "cellspacing");
make_setter!(cx, SetCellSpacing, "cellspacing");
make_getter!(CellSpacing, "cellspacing");
}

View File

@@ -25,6 +25,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTex
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::error::ErrorResult;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
@@ -33,6 +34,8 @@ use crate::dom::document::Document;
use crate::dom::document_embedder_controls::ControlElement;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::event::Event;
use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
use crate::dom::eventtarget::EventTarget;
use crate::dom::html::htmlelement::HTMLElement;
use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
@@ -69,6 +72,9 @@ pub(crate) struct HTMLTextAreaElement {
#[no_trace]
#[conditional_malloc_size_of]
shared_selection: SharedSelection,
/// <https://w3c.github.io/selection-api/#dfn-has-scheduled-selectionchange-event>
has_scheduled_selectionchange_event: Cell<bool>,
}
impl LayoutDom<'_, HTMLTextAreaElement> {
@@ -131,6 +137,7 @@ impl HTMLTextAreaElement {
validity_state: Default::default(),
text_input_widget: Default::default(),
shared_selection: Default::default(),
has_scheduled_selectionchange_event: Default::default(),
}
}
@@ -232,6 +239,41 @@ impl HTMLTextAreaElement {
self.maybe_update_shared_selection();
}
}
/// <https://w3c.github.io/selection-api/#dfn-schedule-a-selectionchange-event>
fn schedule_a_selection_change_event(&self) {
// Step 1. If target's has scheduled selectionchange event is true, abort these steps.
if self.has_scheduled_selectionchange_event.get() {
return;
}
// Step 2. Set target's has scheduled selectionchange event to true.
self.has_scheduled_selectionchange_event.set(true);
// Step 3. Queue a task on the user interaction task source to fire a selectionchange event on target.
let this = Trusted::new(self);
self.owner_global()
.task_manager()
.user_interaction_task_source()
.queue(
// https://w3c.github.io/selection-api/#firing-selectionchange-event
task!(selectionchange_task_steps: move |cx| {
let this = this.root();
// Step 1. Set target's has scheduled selectionchange event to false.
this.has_scheduled_selectionchange_event.set(false);
// Step 2. If target is an element, fire an event named selectionchange, which bubbles and not cancelable, at target.
this.upcast::<EventTarget>().fire_event_with_params(
atom!("selectionchange"),
EventBubbles::Bubbles,
EventCancelable::NotCancelable,
EventComposed::Composed,
CanGc::from_cx(cx),
);
// Step 3. Otherwise, if target is a document, fire an event named selectionchange,
// which does not bubble and not cancelable, at target.
//
// n/a
}),
);
}
}
impl TextControlElement for HTMLTextAreaElement {
@@ -263,9 +305,19 @@ impl TextControlElement for HTMLTextAreaElement {
let enabled = self.upcast::<Element>().focus_state();
let mut shared_selection = self.shared_selection.borrow_mut();
if range == shared_selection.range && enabled == shared_selection.enabled {
let range_remained_equal = range == shared_selection.range;
if range_remained_equal && enabled == shared_selection.enabled {
return;
}
if !range_remained_equal {
// https://w3c.github.io/selection-api/#selectionchange-event
// > When an input or textarea element provide a text selection and its selection changes
// > (in either extent or direction),
// > the user agent must schedule a selectionchange event on the element.
self.schedule_a_selection_change_event();
}
*shared_selection = ScriptSelection {
range,
character_range: self

View File

@@ -44,6 +44,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputE
use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
@@ -52,6 +53,7 @@ use crate::dom::document::Document;
use crate::dom::document_embedder_controls::ControlElement;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::event::Event;
use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
use crate::dom::eventtarget::EventTarget;
use crate::dom::filelist::FileList;
use crate::dom::globalscope::GlobalScope;
@@ -156,6 +158,9 @@ pub(crate) struct HTMLInputElement {
validity_state: MutNullableDom<ValidityState>,
#[no_trace]
pending_webdriver_response: RefCell<Option<PendingWebDriverResponse>>,
/// <https://w3c.github.io/selection-api/#dfn-has-scheduled-selectionchange-event>
has_scheduled_selectionchange_event: Cell<bool>,
}
#[derive(JSTraceable)]
@@ -212,6 +217,7 @@ impl HTMLInputElement {
labels_node_list: MutNullableDom::new(None),
validity_state: Default::default(),
pending_webdriver_response: Default::default(),
has_scheduled_selectionchange_event: Default::default(),
}
}
@@ -893,6 +899,41 @@ impl HTMLInputElement {
fn textinput_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
self.textinput.borrow_mut()
}
/// <https://w3c.github.io/selection-api/#dfn-schedule-a-selectionchange-event>
fn schedule_a_selection_change_event(&self) {
// Step 1. If target's has scheduled selectionchange event is true, abort these steps.
if self.has_scheduled_selectionchange_event.get() {
return;
}
// Step 2. Set target's has scheduled selectionchange event to true.
self.has_scheduled_selectionchange_event.set(true);
// Step 3. Queue a task on the user interaction task source to fire a selectionchange event on target.
let this = Trusted::new(self);
self.owner_global()
.task_manager()
.user_interaction_task_source()
.queue(
// https://w3c.github.io/selection-api/#firing-selectionchange-event
task!(selectionchange_task_steps: move |cx| {
let this = this.root();
// Step 1. Set target's has scheduled selectionchange event to false.
this.has_scheduled_selectionchange_event.set(false);
// Step 2. If target is an element, fire an event named selectionchange, which bubbles and not cancelable, at target.
this.upcast::<EventTarget>().fire_event_with_params(
atom!("selectionchange"),
EventBubbles::Bubbles,
EventCancelable::NotCancelable,
EventComposed::Composed,
CanGc::from_cx(cx),
);
// Step 3. Otherwise, if target is a document, fire an event named selectionchange,
// which does not bubble and not cancelable, at target.
//
// n/a
}),
);
}
}
impl<'dom> LayoutDom<'dom, HTMLInputElement> {
@@ -961,10 +1002,19 @@ impl TextControlElement for HTMLInputElement {
let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
let mut shared_selection = self.shared_selection.borrow_mut();
if range == shared_selection.range && enabled == shared_selection.enabled {
let range_remained_equal = range == shared_selection.range;
if range_remained_equal && enabled == shared_selection.enabled {
return;
}
if !range_remained_equal {
// https://w3c.github.io/selection-api/#selectionchange-event
// > When an input or textarea element provide a text selection and its selection changes
// > (in either extent or direction),
// > the user agent must schedule a selectionchange event on the element.
self.schedule_a_selection_change_event();
}
*shared_selection = ScriptSelection {
range,
character_range: self

View File

@@ -263,30 +263,52 @@ macro_rules! make_enumerated_getter(
);
);
macro_rules! make_setter_inner(
( $self:ident, $value:ident, $htmlname:tt, $can_gc:expr ) => (
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = $self.upcast::<Element>();
element.set_string_attribute(&html5ever::local_name!($htmlname), $value, $can_gc)
);
);
// concat_idents! doesn't work for function name positions, so
// we have to specify both the content name and the HTML name here
#[macro_export]
macro_rules! make_setter(
( $attr:ident, $htmlname:tt ) => (
fn $attr(&self, value: DOMString) {
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = self.upcast::<Element>();
element.set_string_attribute(&html5ever::local_name!($htmlname), value, CanGc::deprecated_note())
make_setter_inner!(self, value, $htmlname, CanGc::deprecated_note());
}
);
( $cx:ident, $attr:ident, $htmlname:tt ) => (
fn $attr(&self, $cx: &mut js::context::JSContext, value: DOMString) {
make_setter_inner!(self, value, $htmlname, CanGc::from_cx($cx));
}
);
);
macro_rules! make_bool_setter_inner(
( $self:ident, $value:ident, $htmlname:tt, $can_gc:expr ) => (
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = $self.upcast::<Element>();
element.set_bool_attribute(&html5ever::local_name!($htmlname), $value, $can_gc)
);
);
#[macro_export]
macro_rules! make_bool_setter(
( $attr:ident, $htmlname:tt ) => (
fn $attr(&self, value: bool) {
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = self.upcast::<Element>();
element.set_bool_attribute(&html5ever::local_name!($htmlname), value, CanGc::deprecated_note())
make_bool_setter_inner!(self, value, $htmlname, CanGc::deprecated_note());
}
);
( $cx:ident, $attr:ident, $htmlname:tt ) => (
fn $attr(&self, $cx: &mut js::context::JSContext, value: bool) {
make_bool_setter_inner!(self, value, $htmlname, CanGc::from_cx($cx));
}
);
);
@@ -316,7 +338,7 @@ macro_rules! make_uint_setter(
#[macro_export]
macro_rules! make_clamped_uint_setter(
($attr:ident, $htmlname:tt, $min:expr, $max:expr, $default:expr) => (
fn $attr(&self, value: u32) {
fn $attr(&self, cx: &mut js::context::JSContext, value: u32) {
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::dom::values::UNSIGNED_LONG_MAX;
@@ -328,7 +350,7 @@ macro_rules! make_clamped_uint_setter(
};
let element = self.upcast::<Element>();
element.set_uint_attribute(&html5ever::local_name!($htmlname), value, CanGc::deprecated_note())
element.set_uint_attribute(&html5ever::local_name!($htmlname), value, CanGc::from_cx(cx))
}
);
);
@@ -385,28 +407,39 @@ macro_rules! make_atomic_setter(
#[macro_export]
macro_rules! make_legacy_color_setter(
( $attr:ident, $htmlname:tt ) => (
fn $attr(&self, value: DOMString) {
fn $attr(&self, cx: &mut js::context::JSContext, value: DOMString) {
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use style::attr::AttrValue;
use $crate::script_runtime::CanGc;
let element = self.upcast::<Element>();
let value = AttrValue::from_legacy_color(value.into());
element.set_attribute(&html5ever::local_name!($htmlname), value, CanGc::deprecated_note())
element.set_attribute(&html5ever::local_name!($htmlname), value, CanGc::from_cx(cx))
}
);
);
macro_rules! make_dimension_setter_inner(
( $self:ident, $value:ident, $htmlname:tt, $can_gc:expr ) => (
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = $self.upcast::<Element>();
let value = AttrValue::from_dimension($value.into());
element.set_attribute(&html5ever::local_name!($htmlname), value, $can_gc)
);
);
#[macro_export]
macro_rules! make_dimension_setter(
( $attr:ident, $htmlname:tt ) => (
fn $attr(&self, value: DOMString) {
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = self.upcast::<Element>();
let value = AttrValue::from_dimension(value.into());
element.set_attribute(&html5ever::local_name!($htmlname), value, CanGc::deprecated_note())
make_dimension_setter_inner!(self, value, $htmlname, CanGc::deprecated_note());
}
);
( $cx:ident, $attr:ident, $htmlname:tt ) => (
fn $attr(&self, $cx: &mut js::context::JSContext, value: DOMString) {
make_dimension_setter_inner!(self, value, $htmlname, CanGc::from_cx($cx));
}
);
);
@@ -414,13 +447,13 @@ macro_rules! make_dimension_setter(
#[macro_export]
macro_rules! make_nonzero_dimension_setter(
( $attr:ident, $htmlname:tt ) => (
fn $attr(&self, value: DOMString) {
fn $attr(&self, cx: &mut js::context::JSContext, value: DOMString) {
use $crate::dom::bindings::inheritance::Castable;
use $crate::dom::element::Element;
use $crate::script_runtime::CanGc;
let element = self.upcast::<Element>();
let value = AttrValue::from_nonzero_dimension(value.into());
element.set_attribute(&html5ever::local_name!($htmlname), value, CanGc::deprecated_note())
element.set_attribute(&html5ever::local_name!($htmlname), value, CanGc::from_cx(cx))
}
);
);
@@ -522,7 +555,7 @@ macro_rules! define_window_owned_event_handler(
}
}
fn $setter(&self, listener: Option<::std::rc::Rc<$handler>>) {
fn $setter(&self, _cx: &mut js::context::JSContext, listener: Option<::std::rc::Rc<$handler>>) {
let document = self.owner_document();
if document.has_browsing_context() {
document.window().$setter(listener)

View File

@@ -92,7 +92,7 @@ impl RoutedPromiseListener<AllowOrDeny> for WakeLock {
let global = self.global();
global.as_window().send_to_constellation(
ScriptToConstellationMessage::AcquireWakeLock(
servo_wakelock::WakeLockType::Screen,
embedder_traits::WakeLockType::Screen,
),
);

View File

@@ -58,6 +58,7 @@ use net_traits::image_cache::{
ImageResponse, PendingImageId, PendingImageResponse, RasterizationCompleteResponse,
};
use net_traits::request::Referrer;
use net_traits::response::HttpsState;
use net_traits::{ResourceFetchTiming, ResourceThreads};
use num_traits::ToPrimitive;
use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
@@ -3677,6 +3678,7 @@ impl Window {
inherited_secure_context: Option<bool>,
theme: Theme,
weak_script_thread: Weak<ScriptThread>,
initial_https_state: HttpsState,
) -> DomRoot<Self> {
let error_reporter = CSSErrorReporter {
pipelineid: pipeline_id,
@@ -3702,6 +3704,7 @@ impl Window {
inherited_secure_context,
unminify_js,
Some(font_context),
initial_https_state,
),
ongoing_navigation: Default::default(),
script_chan,

View File

@@ -39,7 +39,7 @@ use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
@@ -215,7 +215,6 @@ pub(crate) struct DedicatedWorkerGlobalScope {
control_receiver: Receiver<DedicatedWorkerControlMsg>,
#[no_trace]
queued_worker_tasks: DomRefCell<Vec<MessageData>>,
debugger_global: Dom<DebuggerGlobalScope>,
}
impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
@@ -280,7 +279,6 @@ impl DedicatedWorkerGlobalScope {
control_receiver: Receiver<DedicatedWorkerControlMsg>,
insecure_requests_policy: InsecureRequestsPolicy,
font_context: Option<Arc<FontContext>>,
debugger_global: &DebuggerGlobalScope,
) -> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
@@ -305,7 +303,6 @@ impl DedicatedWorkerGlobalScope {
browsing_context,
control_receiver,
queued_worker_tasks: Default::default(),
debugger_global: Dom::from_ref(debugger_global),
}
}
@@ -350,9 +347,13 @@ impl DedicatedWorkerGlobalScope {
control_receiver,
insecure_requests_policy,
font_context,
debugger_global,
));
DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, scope)
let scope = DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, scope);
scope
.upcast::<WorkerGlobalScope>()
.init_debugger_global(debugger_global, cx);
scope
}
/// <https://html.spec.whatwg.org/multipage/#run-a-worker>
@@ -671,20 +672,9 @@ impl DedicatedWorkerGlobalScope {
}
// FIXME(#26324): `self.worker` is None in devtools messages.
match msg {
MixedMessage::Devtools(msg) => match msg {
DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _bool_val) => {},
DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
self.debugger_global.fire_eval(
cx,
code.into(),
id,
Some(self.upcast::<WorkerGlobalScope>().worker_id()),
frame_actor_id,
reply,
);
},
_ => debug!("got an unusable devtools control message inside the worker!"),
},
MixedMessage::Devtools(msg) => self
.upcast::<WorkerGlobalScope>()
.handle_devtools_message(msg, cx),
MixedMessage::Worker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => {
let _ar = AutoWorkerReset::new(self, linked_worker);
self.handle_script_event(msg, cx);

View File

@@ -8,7 +8,7 @@ use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};
use crossbeam_channel::{Receiver, Sender, after};
use devtools_traits::{DebuggerValue, DevtoolScriptControlMsg, EvaluateJSReply};
use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct;
use fonts::FontContext;
use js::jsapi::{JS_AddInterruptCallback, JSContext};
@@ -34,7 +34,7 @@ use crate::dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding;
use crate::dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods;
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::CustomTraceable;
@@ -182,8 +182,6 @@ pub(crate) struct ServiceWorkerGlobalScope {
/// currently only used to signal shutdown.
#[no_trace]
control_receiver: Receiver<ServiceWorkerControlMsg>,
debugger_global: Option<Dom<DebuggerGlobalScope>>,
}
impl WorkerEventLoopMethods for ServiceWorkerGlobalScope {
@@ -242,7 +240,6 @@ impl ServiceWorkerGlobalScope {
control_receiver: Receiver<ServiceWorkerControlMsg>,
closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
debugger_global: Option<&DebuggerGlobalScope>,
) -> ServiceWorkerGlobalScope {
ServiceWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
@@ -265,7 +262,6 @@ impl ServiceWorkerGlobalScope {
swmanager_sender,
scope_url,
control_receiver,
debugger_global: debugger_global.map(Dom::from_ref),
}
}
@@ -283,7 +279,7 @@ impl ServiceWorkerGlobalScope {
control_receiver: Receiver<ServiceWorkerControlMsg>,
closing: Arc<AtomicBool>,
font_context: Arc<FontContext>,
debugger_global: Option<&DebuggerGlobalScope>,
debugger_global: &DebuggerGlobalScope,
cx: &mut js::context::JSContext,
) -> DomRoot<ServiceWorkerGlobalScope> {
let scope = Box::new(ServiceWorkerGlobalScope::new_inherited(
@@ -299,9 +295,13 @@ impl ServiceWorkerGlobalScope {
control_receiver,
closing,
font_context,
debugger_global,
));
ServiceWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, scope)
let scope = ServiceWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, scope);
scope
.upcast::<WorkerGlobalScope>()
.init_debugger_global(debugger_global, cx);
scope
}
/// <https://w3c.github.io/ServiceWorker/#run-service-worker-algorithm>
@@ -347,27 +347,23 @@ impl ServiceWorkerGlobalScope {
pipeline_id,
} = worker_load_origin;
let debugger_global =
let debugger_global = DebuggerGlobalScope::new(
pipeline_id,
init.to_devtools_sender.clone(),
init.from_devtools_sender
.clone()
.map(|from_devtools_sender| {
let debugger_global = DebuggerGlobalScope::new(
pipeline_id,
init.to_devtools_sender.clone(),
from_devtools_sender,
init.mem_profiler_chan.clone(),
init.time_profiler_chan.clone(),
init.script_to_constellation_chan.clone(),
init.script_to_embedder_chan.clone(),
init.resource_threads.clone(),
init.storage_threads.clone(),
#[cfg(feature = "webgpu")]
Arc::new(IdentityHub::default()),
cx,
);
debugger_global.execute(cx);
debugger_global
});
.expect("Guaranteed by update_serviceworker"),
init.mem_profiler_chan.clone(),
init.time_profiler_chan.clone(),
init.script_to_constellation_chan.clone(),
init.script_to_embedder_chan.clone(),
init.resource_threads.clone(),
init.storage_threads.clone(),
#[cfg(feature = "webgpu")]
Arc::new(IdentityHub::default()),
cx,
);
debugger_global.execute(cx);
// Service workers are time limited
// https://w3c.github.io/ServiceWorker/#service-worker-lifetime
@@ -390,21 +386,19 @@ impl ServiceWorkerGlobalScope {
control_receiver,
closing,
font_context,
debugger_global.as_deref(),
&debugger_global,
cx,
);
let worker_scope = global.upcast::<WorkerGlobalScope>();
let global_scope = global.upcast::<GlobalScope>();
if let Some(debugger_global) = debugger_global.as_deref() {
debugger_global.fire_add_debuggee(
cx,
global_scope,
pipeline_id,
Some(worker_scope.worker_id()),
);
}
debugger_global.fire_add_debuggee(
cx,
global_scope,
pipeline_id,
Some(worker_scope.worker_id()),
);
let referrer = referrer_url
.map(Referrer::ReferrerUrl)
@@ -495,27 +489,9 @@ impl ServiceWorkerGlobalScope {
fn handle_mixed_message(&self, msg: MixedMessage, cx: &mut js::context::JSContext) -> bool {
match msg {
MixedMessage::Devtools(msg) => match msg {
DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
if let Some(debugger_global) = self.debugger_global.as_deref() {
debugger_global.fire_eval(
cx,
code.into(),
id,
Some(self.upcast::<WorkerGlobalScope>().worker_id()),
frame_actor_id,
reply,
);
} else {
let _ = reply.send(EvaluateJSReply {
value: DebuggerValue::VoidValue,
has_exception: true,
});
}
},
_ => debug!("got an unusable devtools control message inside the worker!"),
},
MixedMessage::Devtools(msg) => self
.upcast::<WorkerGlobalScope>()
.handle_devtools_message(msg, cx),
MixedMessage::ServiceWorker(msg) => {
self.handle_script_event(msg, cx);
},

View File

@@ -15,7 +15,7 @@ use dom_struct::dom_struct;
use encoding_rs::UTF_8;
use fonts::FontContext;
use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
use js::jsapi::JSContext as RawJSContext;
use js::jsapi::{Heap, JSContext as RawJSContext, Value};
use js::realm::CurrentRealm;
use js::rust::{HandleValue, MutableHandleValue, ParentRuntime};
use mime::Mime;
@@ -24,8 +24,12 @@ use net_traits::policy_container::PolicyContainer;
use net_traits::request::{
CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, RequestBuilder, RequestId,
};
use net_traits::response::HttpsState;
use net_traits::{FetchMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming};
use profile_traits::mem::{ProcessReports, perform_memory_report};
use script_bindings::conversions::{SafeToJSValConvertible, root_from_handlevalue};
use script_bindings::reflector::DomObject;
use script_bindings::root::rooted_heap_handle;
use servo_base::cross_process_instant::CrossProcessInstant;
use servo_base::generic_channel::{GenericSend, GenericSender, RoutedReceiver};
use servo_base::id::{PipelineId, PipelineNamespace};
@@ -59,6 +63,7 @@ use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::utils::define_all_exposed_interfaces;
use crate::dom::crypto::Crypto;
use crate::dom::csp::{GlobalCspReporting, Violation, parse_csp_list_from_metadata};
use crate::dom::debugger::debuggerglobalscope::DebuggerGlobalScope;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::global_scope_script_execution::{ErrorReporting, RethrowErrors};
use crate::dom::globalscope::GlobalScope;
@@ -319,6 +324,13 @@ pub(crate) struct WorkerGlobalScope {
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-endpoints>
#[no_trace]
endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
/// The debugger global object associated with this worker global.
/// All traced members of DOM objects must be same-compartment with the
/// realm being traced, so this is the debugger global object wrapped into
/// this global's compartment.
#[ignore_malloc_size_of = "Measured by the JS engine"]
debugger_global: Heap<Value>,
}
impl WorkerGlobalScope {
@@ -361,6 +373,7 @@ impl WorkerGlobalScope {
init.inherited_secure_context,
init.unminify_js,
font_context,
HttpsState::None,
),
microtask_queue: runtime.microtask_queue.clone(),
worker_id: init.worker_id,
@@ -384,6 +397,7 @@ impl WorkerGlobalScope {
reporting_observer_list: Default::default(),
report_list: Default::default(),
endpoints_list: Default::default(),
debugger_global: Default::default(),
}
}
@@ -1054,6 +1068,51 @@ impl WorkerGlobalScope {
factory.abort_pending_upgrades();
}
}
pub(super) fn init_debugger_global(
&self,
debugger_global: &DebuggerGlobalScope,
cx: &mut js::context::JSContext,
) {
let mut realm = enter_auto_realm(cx, self);
let cx = &mut realm.current_realm();
// Convert the debugger globals reflector to a Value, wrapping it from its originating realm (debugger realm)
// into the active realm (debuggee realm) so that it can be passed across compartments.
rooted!(&in(cx) let mut wrapped_global: Value);
debugger_global.reflector().safe_to_jsval(
cx.into(),
wrapped_global.handle_mut(),
CanGc::from_cx(cx),
);
self.debugger_global.set(*wrapped_global);
}
pub(super) fn handle_devtools_message(
&self,
msg: DevtoolScriptControlMsg,
cx: &mut js::context::JSContext,
) {
match msg {
DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
let debugger_global =
root_from_handlevalue::<DebuggerGlobalScope>(debugger_global_handle, cx.into())
.expect("must be a debugger global scope");
debugger_global.fire_eval(
cx,
code.into(),
id,
Some(self.worker_id()),
frame_actor_id,
reply,
);
},
_ => debug!("got an unusable devtools control message inside the worker!"),
}
}
}
#[expect(unsafe_code)]

View File

@@ -12,6 +12,7 @@ use embedder_traits::{JavaScriptEvaluationError, ScriptToEmbedderChan};
use js::context::JSContext;
use net_traits::ResourceThreads;
use net_traits::image_cache::ImageCache;
use net_traits::response::HttpsState;
use profile_traits::{mem, time};
use script_traits::Painter;
use servo_base::generic_channel::{GenericCallback, GenericSender};
@@ -124,6 +125,7 @@ impl WorkletGlobalScope {
inherited_secure_context,
false,
None, // font_context
HttpsState::None,
),
base_url,
to_script_thread_sender: init.to_script_thread_sender.clone(),

View File

@@ -27,10 +27,24 @@ mod servo_dangerous_style_shadow_root;
mod servo_layout_element;
mod servo_layout_node;
use std::marker::PhantomData;
pub use iterators::*;
use layout_api::LayoutDomTypeBundle;
pub use servo_dangerous_style_document::*;
pub use servo_dangerous_style_element::*;
pub use servo_dangerous_style_node::*;
pub use servo_dangerous_style_shadow_root::*;
pub use servo_layout_element::*;
pub use servo_layout_node::*;
pub struct ServoLayoutDomTypeBundle<'dom> {
phantom: PhantomData<&'dom ()>,
}
impl<'dom> LayoutDomTypeBundle<'dom> for ServoLayoutDomTypeBundle<'dom> {
type ConcreteLayoutElement = ServoLayoutElement<'dom>;
type ConcreteLayoutNode = ServoLayoutNode<'dom>;
type ConcreteDangerousStyleNode = ServoDangerousStyleNode<'dom>;
type ConcreteDangerousStyleElement = ServoDangerousStyleElement<'dom>;
}

View File

@@ -54,7 +54,7 @@ use crate::dom::html::htmlslotelement::HTMLSlotElement;
use crate::dom::node::{Node, NodeFlags};
use crate::layout_dom::{
DOMDescendantIterator, ServoDangerousStyleNode, ServoDangerousStyleShadowRoot,
ServoLayoutElement, ServoLayoutNode,
ServoLayoutDomTypeBundle, ServoLayoutElement, ServoLayoutNode,
};
/// A wrapper around [`LayoutDom<_, Element>`] to be used with `stylo` and `selectors`.
@@ -81,9 +81,9 @@ impl<'dom> From<LayoutDom<'dom, Element>> for ServoDangerousStyleElement<'dom> {
}
impl<'dom> DangerousStyleElement<'dom> for ServoDangerousStyleElement<'dom> {
type ConcreteLayoutElement = ServoLayoutElement<'dom>;
type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
fn layout_element(&self) -> Self::ConcreteLayoutElement {
fn layout_element(&self) -> ServoLayoutElement<'dom> {
self.element.into()
}
}

View File

@@ -19,7 +19,7 @@ use super::{ServoDangerousStyleDocument, ServoDangerousStyleShadowRoot};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::root::LayoutDom;
use crate::dom::node::{Node, NodeFlags};
use crate::layout_dom::{ServoDangerousStyleElement, ServoLayoutNode};
use crate::layout_dom::{ServoDangerousStyleElement, ServoLayoutDomTypeBundle, ServoLayoutNode};
/// A wrapper around [`LayoutDom<_, Node>`] to be used with `stylo` and `selectors`.
///
@@ -75,9 +75,9 @@ impl<'dom> From<LayoutDom<'dom, Node>> for ServoDangerousStyleNode<'dom> {
}
impl<'dom> DangerousStyleNode<'dom> for ServoDangerousStyleNode<'dom> {
type ConcreteLayoutNode = ServoLayoutNode<'dom>;
type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
fn layout_node(&self) -> Self::ConcreteLayoutNode {
fn layout_node(&self) -> ServoLayoutNode<'dom> {
self.node.into()
}
}

View File

@@ -24,7 +24,8 @@ use crate::dom::bindings::root::LayoutDom;
use crate::dom::element::Element;
use crate::dom::node::{Node, NodeFlags};
use crate::layout_dom::{
ServoDangerousStyleElement, ServoDangerousStyleShadowRoot, ServoLayoutNode,
ServoDangerousStyleElement, ServoDangerousStyleShadowRoot, ServoLayoutDomTypeBundle,
ServoLayoutNode,
};
impl fmt::Debug for LayoutDom<'_, Element> {
@@ -72,8 +73,7 @@ impl<'dom> From<LayoutDom<'dom, Element>> for ServoLayoutElement<'dom> {
}
impl<'dom> LayoutElement<'dom> for ServoLayoutElement<'dom> {
type ConcreteLayoutNode = ServoLayoutNode<'dom>;
type ConcreteStyleElement = ServoDangerousStyleElement<'dom>;
type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
fn with_pseudo(&self, pseudo_element: PseudoElement) -> Option<Self> {
if pseudo_element.is_eager() &&
@@ -174,7 +174,7 @@ impl<'dom> LayoutElement<'dom> for ServoLayoutElement<'dom> {
},
PseudoElementCascadeType::Precomputed => context
.stylist
.precomputed_values_for_pseudo::<Self::ConcreteStyleElement>(
.precomputed_values_for_pseudo::<ServoDangerousStyleElement<'dom>>(
&context.guards,
&pseudo_element,
Some(base_style),

View File

@@ -28,7 +28,7 @@ use crate::dom::bindings::root::LayoutDom;
use crate::dom::element::Element;
use crate::dom::node::{Node, NodeFlags, NodeTypeIdWrapper};
use crate::layout_dom::{
ServoDangerousStyleElement, ServoDangerousStyleNode, ServoLayoutNodeChildrenIterator,
ServoDangerousStyleNode, ServoLayoutDomTypeBundle, ServoLayoutNodeChildrenIterator,
};
impl fmt::Debug for LayoutDom<'_, Node> {
@@ -113,10 +113,7 @@ impl<'dom> From<LayoutDom<'dom, Node>> for ServoLayoutNode<'dom> {
}
impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
type ConcreteDangerousStyleNode = ServoDangerousStyleNode<'dom>;
type ConcreteDangerousStyleElement = ServoDangerousStyleElement<'dom>;
type ConcreteLayoutElement = ServoLayoutElement<'dom>;
type ChildIterator = ServoLayoutNodeChildrenIterator<'dom>;
type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self> {
Some(
@@ -126,7 +123,7 @@ impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
)
}
unsafe fn dangerous_style_node(self) -> Self::ConcreteDangerousStyleNode {
unsafe fn dangerous_style_node(self) -> ServoDangerousStyleNode<'dom> {
self.node.into()
}
@@ -224,11 +221,11 @@ impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
}
}
fn flat_tree_children(&self) -> LayoutIterator<ServoLayoutNodeChildrenIterator<'dom>> {
fn flat_tree_children(&self) -> impl Iterator<Item = Self> {
LayoutIterator(ServoLayoutNodeChildrenIterator::new_for_flat_tree(*self))
}
fn dom_children(&self) -> LayoutIterator<ServoLayoutNodeChildrenIterator<'dom>> {
fn dom_children(&self) -> impl Iterator<Item = Self> {
LayoutIterator(ServoLayoutNodeChildrenIterator::new_for_dom_tree(*self))
}

View File

@@ -3439,6 +3439,7 @@ impl ScriptThread {
incomplete.load_data.inherited_secure_context,
incomplete.theme,
self.this.clone(),
metadata.https_state,
);
self.debugger_global
.fire_add_debuggee(cx, window.upcast(), incomplete.pipeline_id, None);
@@ -3600,7 +3601,6 @@ impl ScriptThread {
),
);
document.set_https_state(metadata.https_state);
document.set_navigation_start(incomplete.navigation_start);
if is_html_document == IsHTMLDocument::NonHTMLDocument {

View File

@@ -425,7 +425,7 @@ DOMInterfaces = {
},
"HTMLBodyElement": {
"canGc": ["SetBackground"]
'implicitCxSetters': True,
},
'HTMLButtonElement': {
@@ -480,7 +480,7 @@ DOMInterfaces = {
},
'HTMLFontElement': {
'canGc': ['SetSize']
'implicitCxSetters': True,
},
'HTMLFormControlsCollection': {
@@ -492,10 +492,18 @@ DOMInterfaces = {
'cx': ['CheckValidity', 'ReportValidity'],
},
'HTMLFrameSetElement': {
'implicitCxSetters': True,
},
'HTMLIFrameElement': {
'cx': ['Sandbox', 'SetSrcdoc'],
},
'HTMLHRElement': {
'implicitCxSetters': True,
},
'HTMLImageElement': {
'cx': ['Decode', 'Image', 'ReportValidity', 'SetCrossOrigin'],
},
@@ -584,14 +592,20 @@ DOMInterfaces = {
'DeleteTFoot',
'DeleteTHead',
'InsertRow',
'SetCaption',
'SetTFoot',
'SetTHead'
],
'implicitCxSetters': True,
},
'HTMLTableCellElement': {
'implicitCxSetters': True,
},
'HTMLTableColElement': {
'implicitCxSetters': True,
},
'HTMLTableRowElement': {
'cx': ['DeleteCell', 'InsertCell']
'cx': ['DeleteCell', 'InsertCell'],
'implicitCxSetters': True,
},
'HTMLTableSectionElement': {

View File

@@ -72,6 +72,11 @@ from configuration import (
)
AUTOGENERATED_WARNING_COMMENT = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
# DFS-ordered prototype ID ranges, populated by GlobalGenRoots.PrototypeList()
# and consumed by CGIDLInterface to generate PROTO_FIRST/PROTO_LAST constants.
_proto_ranges: dict[str, tuple[int, int]] = {}
ALLOWED_WARNING_LIST = [
'non_camel_case_types',
'non_upper_case_globals',
@@ -3395,6 +3400,7 @@ let val = PrivateValue(ptr::null());
JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, &val);
"""
name = self.descriptor.name
return CGGeneric(f"""
let raw = Root::new(MaybeUnreflectedDom::from_box(object));
@@ -3409,6 +3415,7 @@ assert!(!canonical_proto.is_null());
{create}
let root = raw.reflect_with(obj.get());
root.reflector().set_proto_id(PrototypeList::ID::{name} as u16);
{unforgeable}
@@ -3441,6 +3448,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
members = [f"{function}::<D>(cx.into(), obj.handle(), {array.variableName()}.get(), obj.handle());"
for (function, array) in pairs if array.length() > 0]
membersStr = "\n".join(members)
name = self.descriptor.name
return CGGeneric(f"""
unsafe {{
@@ -3459,6 +3467,7 @@ unsafe {{
assert!(!obj.is_null());
let root = raw.reflect_with(obj.get());
root.reflector().set_proto_id(PrototypeList::ID::{name} as u16);
let _ac = JSAutoRealm::new(cx.raw_cx(), obj.get());
rooted!(&in(cx) let mut canonical_proto = ptr::null_mut::<JSObject>());
@@ -3503,12 +3512,16 @@ class CGIDLInterface(CGThing):
check = f"ptr::eq(class, unsafe {{ {bindingModule}::Class.get() }})"
else:
check = f"ptr::eq(class, unsafe {{ &{bindingModule}::Class.get().dom_class }})"
# Get DFS-ordered ID range for this interface (set by PrototypeList generation).
proto_first, proto_last = _proto_ranges.get(name, (0, 65535))
return f"""
impl IDLInterface for {name} {{
#[inline]
fn derives(class: &'static DOMClass) -> bool {{
{check}
}}
const PROTO_FIRST: u16 = {proto_first};
const PROTO_LAST: u16 = {proto_last};
}}
"""
@@ -9142,8 +9155,51 @@ class GlobalGenRoots():
@staticmethod
def PrototypeList(config: Configuration) -> CGThing:
# Prototype ID enum.
# Assign IDs in DFS preorder of the inheritance tree so that
# all descendants of a type occupy a contiguous ID range.
# This enables O(1) is::<T>() checks via range comparison.
interfaces = config.getDescriptors(isCallback=False, isNamespace=False)
protos = [d.name for d in interfaces]
# Build a tree: parent_name -> sorted list of child names
children: dict[str | None, list[str]] = {}
name_set = set()
for d in interfaces:
name = d.name
name_set.add(name)
parent_name = d.prototypeChain[-2] if len(d.prototypeChain) >= 2 else None
children.setdefault(parent_name, []).append(name)
# Sort children alphabetically for determinism within each level
for key in children:
children[key].sort()
# DFS preorder traversal
protos: list[str] = []
# Track range for each prototype: (first_id, last_id)
proto_ranges: dict[str, tuple[int, int]] = {}
def dfs(name: str) -> None:
first_id = len(protos)
protos.append(name)
for child in children.get(name, []):
dfs(child)
proto_ranges[name] = (first_id, len(protos) - 1)
# Start from roots (interfaces with no parent in our set)
roots = sorted(children.get(None, []))
for root in roots:
dfs(root)
# Any interfaces not in the tree (shouldn't happen, but be safe)
for d in interfaces:
if d.name not in proto_ranges:
first_id = len(protos)
protos.append(d.name)
proto_ranges[d.name] = (first_id, first_id)
# Store ranges at module level for use by CGIDLInterface.
global _proto_ranges
_proto_ranges = proto_ranges
constructors = sorted([MakeNativeName(d.name)
for d in config.getDescriptors(hasInterfaceObject=True)
if d.shouldHaveGetConstructorObjectMethod()])

View File

@@ -51,6 +51,11 @@ impl<T: ToJSValConvertible + ?Sized> SafeToJSValConvertible for T {
pub trait IDLInterface {
/// Returns whether the given DOM class derives that interface.
fn derives(_: &'static DOMClass) -> bool;
/// First prototype ID in the DFS-ordered range for this interface and its descendants.
const PROTO_FIRST: u16 = 0;
/// Last prototype ID in the DFS-ordered range for this interface and its descendants.
const PROTO_LAST: u16 = u16::MAX;
}
/// A trait to mark an IDL interface as deriving from another one.

View File

@@ -6,7 +6,7 @@
use std::mem;
use crate::conversions::{DerivedFrom, IDLInterface, get_dom_class};
use crate::conversions::{DerivedFrom, IDLInterface};
use crate::reflector::DomObject;
use crate::script_runtime::runtime_is_alive;
@@ -26,8 +26,8 @@ pub trait Castable: IDLInterface + DomObject + Sized {
"Attempting to interact with DOM objects after JS runtime has shut down."
);
let class = unsafe { get_dom_class(self.reflector().get_jsobject().get()).unwrap() };
T::derives(class)
let id = self.reflector().proto_id();
id >= T::PROTO_FIRST && id <= T::PROTO_LAST
}
/// Cast a DOM object upwards to one of the interfaces it derives from.

View File

@@ -42,6 +42,8 @@ pub struct Reflector<T = ()> {
object: Heap<*mut JSObject>,
/// Associated memory size (of rust side). Used for memory reporting to SM.
size: T,
/// Cached prototype ID for fast type checks.
proto_id: Cell<u16>,
}
unsafe impl<T> js::gc::Traceable for Reflector<T> {
@@ -62,6 +64,18 @@ impl<T> Reflector<T> {
unsafe { HandleObject::from_raw(self.object.handle()) }
}
/// Get the cached prototype ID.
#[inline]
pub fn proto_id(&self) -> u16 {
self.proto_id.get()
}
/// Set the cached prototype ID.
#[inline]
pub fn set_proto_id(&self, id: u16) {
self.proto_id.set(id);
}
/// Initialize the reflector. (May be called only once.)
///
/// # Safety
@@ -88,6 +102,7 @@ impl<T: AssociatedMemorySize> Reflector<T> {
pub fn new() -> Reflector<T> {
Reflector {
object: Heap::default(),
proto_id: Cell::new(u16::MAX),
size: T::default(),
}
}

View File

@@ -7,8 +7,9 @@ use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::{fmt, mem, ptr};
use js::gc::Traceable as JSTraceable;
use js::jsapi::{JSObject, JSTracer};
use js::gc::{Handle, Traceable as JSTraceable};
use js::jsapi::{Heap, JSObject, JSTracer};
use js::rust::GCMethods;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use style::thread_state;
@@ -469,3 +470,17 @@ where
unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
}
}
/// Returns a handle to a Heap member of a reflected DOM object.
/// The provided callback acts as a projection of the rooted-ness of
/// the provided DOM object; it must return a reference to a Heap
/// member of the DOM object.
pub fn rooted_heap_handle<'a, T: DomObject, U: GCMethods + Copy>(
object: &'a T,
f: impl Fn(&'a T) -> &'a Heap<U>,
) -> Handle<'a, U> {
// SAFETY: Heap::handle is safe to call when the Heap is a member
// of a rooted object. Our safety invariants for DOM objects
// ensure that a &T is obtained via a root of T.
unsafe { Handle::from_raw(f(object).handle()) }
}

View File

@@ -69,7 +69,7 @@ use servo_geometry::{
};
use servo_media::ServoMedia;
use servo_media::player::context::GlContext;
use servo_wakelock::NoOpWakeLockProvider;
use servo_wakelock::DefaultWakeLockDelegate;
use storage::new_storage_threads;
use storage_traits::StorageThreads;
use style::global_style_data::StyleThreadPool;
@@ -930,8 +930,11 @@ impl Servo {
protocols.clone(),
);
let (private_storage_threads, public_storage_threads) =
new_storage_threads(mem_profiler_chan.clone(), opts.config_dir.clone());
let (private_storage_threads, public_storage_threads) = new_storage_threads(
mem_profiler_chan.clone(),
opts.config_dir.clone(),
opts.temporary_storage,
);
create_constellation(
embedder_to_constellation_receiver,
@@ -1191,7 +1194,7 @@ fn create_constellation(
wgpu_image_map: paint.webgpu_image_map(),
async_runtime,
privileged_urls,
wake_lock_provider: Box::new(NoOpWakeLockProvider),
wake_lock_provider: Box::new(DefaultWakeLockDelegate),
};
let layout_factory = Arc::new(LayoutFactoryImpl());

View File

@@ -12,6 +12,7 @@ use embedder_traits::user_contents::UserContentManagerId;
use embedder_traits::{
AnimationState, FocusSequenceNumber, JSValue, JavaScriptEvaluationError,
JavaScriptEvaluationId, MediaSessionEvent, ScriptToEmbedderChan, Theme, ViewportDetails,
WakeLockType,
};
use encoding_rs::Encoding;
use euclid::default::Size2D as UntypedSize2D;
@@ -37,7 +38,6 @@ use servo_base::id::{
use servo_canvas_traits::canvas::{CanvasId, CanvasMsg};
use servo_canvas_traits::webgl::WebGLChan;
use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
use servo_wakelock::WakeLockType;
use storage_traits::StorageThreads;
use storage_traits::webstorage_thread::WebStorageType;
use strum::IntoStaticStr;

View File

@@ -1148,3 +1148,25 @@ impl UrlRequest {
self
}
}
/// The type of wake lock to acquire or release.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum WakeLockType {
Screen,
}
/// Trait for platform-specific wake lock support.
///
/// Implementations are responsible for interacting with the OS to prevent
/// the screen (or other resources) from sleeping while a wake lock is held.
pub trait WakeLockDelegate: Send + Sync {
/// Acquire a wake lock of the given type, preventing the associated
/// resource from sleeping. Called when the aggregate lock count transitions
/// from 0 to 1. Returns an error if the OS fails to grant the lock.
fn acquire(&self, type_: WakeLockType) -> Result<(), Box<dyn std::error::Error>>;
/// Release a previously acquired wake lock of the given type, allowing
/// the resource to sleep. Called when the aggregate lock count transitions
/// from N to 0.
fn release(&self, type_: WakeLockType) -> Result<(), Box<dyn std::error::Error>>;
}

View File

@@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![deny(missing_docs)]
use crate::{DangerousStyleElement, DangerousStyleNode, LayoutElement, LayoutNode};
/// A trait that holds all the concrete implementations of the Layout DOM traits. This is
/// useful because it means that other types (specifically the implementation of the `Layout`
/// trait) can be parameterized over a single type (`LayoutDomTypeBundle`) rather than all of
/// the various Layout DOM trait implementations.
pub trait LayoutDomTypeBundle<'dom> {
/// The concrete implementation of [`LayoutNode`] from `script`.
type ConcreteLayoutNode: LayoutNode<'dom>;
/// The concrete implementation of [`LayoutElement`] from `script`.
type ConcreteLayoutElement: LayoutElement<'dom>;
/// The concrete implementation of [`DangerousStyleNode`] from `script`.
type ConcreteDangerousStyleNode: DangerousStyleNode<'dom>;
/// The concrete implementation of [`DangerousStyleElement`] from `script`.
type ConcreteDangerousStyleElement: DangerousStyleElement<'dom>;
}
// The type aliases below simplify extracting the concrete types out of the type bundle. It will be
// possible to simplify this once default associated types have landed and are stable:
// https://github.com/rust-lang/rust/issues/29661.
/// Type alias to extract `ConcreteLayoutNode` from a `LayoutDomTypeBundle` implementation.
pub type LayoutNodeOf<'dom, T> = <T as LayoutDomTypeBundle<'dom>>::ConcreteLayoutNode;
/// Type alias to extract `ConcreteLayoutElement` from a `LayoutDomTypeBundle` implementation.
pub type LayoutElementOf<'dom, T> = <T as LayoutDomTypeBundle<'dom>>::ConcreteLayoutElement;
/// Type alias to extract `ConcreteDangerousStyleNode` from a `LayoutDomTypeBundle` implementation.
pub type DangerousStyleNodeOf<'dom, T> =
<T as LayoutDomTypeBundle<'dom>>::ConcreteDangerousStyleNode;
/// Type alias to extract `ConcreteDangerousStyleElement` from a `LayoutDomTypeBundle` implementation.
pub type DangerousStyleElementOf<'dom, T> =
<T as LayoutDomTypeBundle<'dom>>::ConcreteDangerousStyleElement;

View File

@@ -16,7 +16,8 @@ use style::dom::TElement;
use style::properties::ComputedValues;
use style::selector_parser::{PseudoElement, SelectorImpl};
use crate::{LayoutDataTrait, LayoutNode, LayoutNodeType, PseudoElementChain, StyleData};
use crate::layout_dom::{DangerousStyleElementOf, LayoutElementOf, LayoutNodeOf};
use crate::{LayoutDataTrait, LayoutDomTypeBundle, LayoutNodeType, PseudoElementChain, StyleData};
/// A trait that exposes a DOM element to layout. Implementors of this trait must abide by certain
/// safety requirements. Layout will only ever access and mutate each element from a single thread
@@ -28,12 +29,8 @@ use crate::{LayoutDataTrait, LayoutNode, LayoutNodeType, PseudoElementChain, Sty
/// that API is marked as `unsafe` here. In general [`DangerousStyleElement`] should only be used
/// when interfacing with the `stylo` and `selectors`.
pub trait LayoutElement<'dom>: Copy + Debug + Send + Sync {
/// An associated type that refers to the concrete implementation of [`DangerousStyleElement`]
/// implemented in `script`.
type ConcreteStyleElement: DangerousStyleElement<'dom>;
/// An associated type that refers to the concrete implementation of [`LayoutNode`]
/// implemented in `script`.
type ConcreteLayoutNode: LayoutNode<'dom>;
/// The concrete implementation of [`LayoutDomTypeBundle`] implemented in `script`.
type ConcreteTypeBundle: LayoutDomTypeBundle<'dom>;
/// Creates a new `LayoutElement` for the same `LayoutElement`
/// with a different pseudo-element type.
@@ -53,7 +50,7 @@ pub trait LayoutElement<'dom>: Copy + Debug + Send + Sync {
/// Return this [`LayoutElement`] as a [`LayoutNode`], preserving the internal
/// pseudo-element chain.
fn as_node(&self) -> Self::ConcreteLayoutNode;
fn as_node(&self) -> LayoutNodeOf<'dom, Self::ConcreteTypeBundle>;
/// Returns access to a version of this LayoutElement that can be used by stylo
/// and selectors. This is dangerous as it allows more access to ancestor nodes
@@ -65,7 +62,9 @@ pub trait LayoutElement<'dom>: Copy + Debug + Send + Sync {
/// This should only ever be called from the main script thread. It is never
/// okay to explicitly create a node for style while any layout worker threads
/// are running.
unsafe fn dangerous_style_element(self) -> Self::ConcreteStyleElement;
unsafe fn dangerous_style_element(
self,
) -> DangerousStyleElementOf<'dom, Self::ConcreteTypeBundle>;
/// Initialize this node with empty style and opaque layout data.
fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
@@ -144,9 +143,8 @@ pub trait LayoutElement<'dom>: Copy + Debug + Send + Sync {
pub trait DangerousStyleElement<'dom>:
TElement + ::selectors::Element<Impl = SelectorImpl> + Send + Sync
{
/// The concrete implementation of [`LayoutElement`] implemented in `script`.
type ConcreteLayoutElement: LayoutElement<'dom>;
/// The concrete implementation of [`LayoutNode`] implemented in `script`.
/// The concrete implementation of [`LayoutDomTypeBundle`] implemented in `script`.
type ConcreteTypeBundle: LayoutDomTypeBundle<'dom>;
/// Get a handle to the original "safe" version of this element, a [`LayoutElement`].
fn layout_element(&self) -> Self::ConcreteLayoutElement;
fn layout_element(&self) -> LayoutElementOf<'dom, Self::ConcreteTypeBundle>;
}

View File

@@ -14,15 +14,15 @@ use servo_arc::Arc;
use servo_base::id::{BrowsingContextId, PipelineId};
use servo_url::ServoUrl;
use style::context::SharedStyleContext;
use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, TNode};
use style::dom::{NodeInfo, OpaqueNode, TNode};
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use crate::layout_element::{DangerousStyleElement, LayoutElement};
use crate::layout_dom::{DangerousStyleNodeOf, LayoutElementOf, LayoutNodeOf};
use crate::pseudo_element_chain::PseudoElementChain;
use crate::{
GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutDataTrait, LayoutNodeType,
SVGElementData, SharedSelection,
GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutDataTrait, LayoutDomTypeBundle,
LayoutNodeType, SVGElementData, SharedSelection,
};
/// A trait that exposes a DOM nodes to layout. Implementors of this trait must abide by certain
@@ -35,14 +35,8 @@ use crate::{
/// that API is marked as `unsafe` here. In general [`DangerousStyleNode`] should only be used
/// when interfacing with the `stylo` and `selectors`.
pub trait LayoutNode<'dom>: Copy + Debug + NodeInfo + Send + Sync {
/// The concrete implementation of [`DangerousStyleNode`] implemented in `script`.
type ConcreteDangerousStyleNode: DangerousStyleNode<'dom>;
/// The concrete implementation of [`DangerousStyleElement`] implemented in `script`.
type ConcreteDangerousStyleElement: DangerousStyleElement<'dom>;
/// The concrete implementation of [`ConcreteLayoutElement`] implemented in `script`.
type ConcreteLayoutElement: LayoutElement<'dom>;
/// The concrete implementation of [`ChildIterator`] implemented in `script`.
type ChildIterator: Iterator<Item = Self> + Sized;
/// The concrete implementation of [`LayoutDomTypeBundle`] implemented in `script`.
type ConcreteTypeBundle: LayoutDomTypeBundle<'dom>;
/// Creates a new `LayoutNode` for the same `LayoutNode` with a different pseudo-element type.
///
@@ -70,7 +64,7 @@ pub trait LayoutNode<'dom>: Copy + Debug + NodeInfo + Send + Sync {
/// This should only ever be called from the main script thread. It is never
/// okay to explicitly create a node for style while any layout worker threads
/// are running.
unsafe fn dangerous_style_node(self) -> Self::ConcreteDangerousStyleNode;
unsafe fn dangerous_style_node(self) -> DangerousStyleNodeOf<'dom, Self::ConcreteTypeBundle>;
/// Returns access to the DOM parent node of this node. This *does not* take
/// into account shadow tree children and slottables. For that use
@@ -115,18 +109,18 @@ pub trait LayoutNode<'dom>: Copy + Debug + NodeInfo + Send + Sync {
/// takes into account shadow tree children and slottables.
///
/// [flat tree]: https://drafts.csswg.org/css-shadow-1/#flat-tree
fn flat_tree_children(&self) -> LayoutIterator<Self::ChildIterator>;
fn flat_tree_children(&self) -> impl Iterator<Item = Self> + Sized;
/// Returns an iterator over this node's children in the DOM. This
/// *does not* take shadow roots and assigned slottables into account.
/// For that use [`Self::flat_tree_children`].
fn dom_children(&self) -> LayoutIterator<Self::ChildIterator>;
fn dom_children(&self) -> impl Iterator<Item = Self> + Sized;
/// Returns a [`LayoutElement`] if this is an element in the HTML namespace, None otherwise.
fn as_html_element(&self) -> Option<Self::ConcreteLayoutElement>;
fn as_html_element(&self) -> Option<LayoutElementOf<'dom, Self::ConcreteTypeBundle>>;
/// Returns a [`LayoutElement`] if this is an element.
fn as_element(&self) -> Option<Self::ConcreteLayoutElement>;
fn as_element(&self) -> Option<LayoutElementOf<'dom, Self::ConcreteTypeBundle>>;
/// Returns the computed style for the given node, properly handling pseudo-elements. For
/// elements this returns their style and for other nodes, this returns the style of the parent
@@ -236,8 +230,8 @@ pub trait LayoutNode<'dom>: Copy + Debug + NodeInfo + Send + Sync {
/// If you are not interfacing with `stylo` and `selectors` you *should not* use this
/// type, unless you know what you are doing.
pub trait DangerousStyleNode<'dom>: TNode + Sized + NodeInfo + Send + Sync {
/// The concrete implementation of [`LayoutNode`] implemented in `script`.
type ConcreteLayoutNode: LayoutNode<'dom>;
/// The concrete implementation of [`LayoutDomTypeBundle`] implemented in `script`.
type ConcreteTypeBundle: LayoutDomTypeBundle<'dom>;
/// Get a handle to the original "safe" version of this node, a [`LayoutNode`] implementation.
fn layout_node(&self) -> Self::ConcreteLayoutNode;
fn layout_node(&self) -> LayoutNodeOf<'dom, Self::ConcreteTypeBundle>;
}

View File

@@ -9,6 +9,7 @@
#![deny(unsafe_code)]
mod layout_damage;
mod layout_dom;
mod layout_element;
mod layout_node;
mod pseudo_element_chain;
@@ -29,6 +30,10 @@ use embedder_traits::{Cursor, ScriptToEmbedderChan, Theme, UntrustedNodeAddress,
use euclid::{Point2D, Rect};
use fonts::{FontContext, TextByteRange, WebFontDocumentContext};
pub use layout_damage::LayoutDamage;
pub use layout_dom::{
DangerousStyleElementOf, DangerousStyleNodeOf, LayoutDomTypeBundle, LayoutElementOf,
LayoutNodeOf,
};
pub use layout_element::{DangerousStyleElement, LayoutElement};
pub use layout_node::{DangerousStyleNode, LayoutNode};
use libc::c_void;

View File

@@ -642,19 +642,25 @@ impl RegistryEngine for SqliteEngine {
}
pub trait ClientStorageThreadFactory {
fn new(config_dir: Option<PathBuf>) -> Self;
fn new(config_dir: Option<PathBuf>, temporary_storage: bool) -> Self;
}
impl ClientStorageThreadFactory for ClientStorageThreadHandle {
fn new(config_dir: Option<PathBuf>) -> ClientStorageThreadHandle {
fn new(config_dir: Option<PathBuf>, temporary_storage: bool) -> ClientStorageThreadHandle {
let (generic_sender, generic_receiver) = generic_channel::channel().unwrap();
let storage_dir = config_dir
let base_dir = config_dir
.unwrap_or_else(|| {
let tmp_dir = tempfile::tempdir().unwrap();
tmp_dir.path().to_path_buf()
})
.join("clientstorage");
let storage_dir = if temporary_storage {
let unique_id = uuid::Uuid::new_v4().to_string();
base_dir.join("temporary").join(unique_id)
} else {
base_dir.join("default_v1")
};
std::fs::create_dir_all(&storage_dir)
.expect("Failed to create ClientStorage storage directory");
let sender_clone = generic_sender.clone();

View File

@@ -16,10 +16,11 @@ use crate::{ClientStorageThreadFactory, IndexedDBThreadFactory, WebStorageThread
fn new_storage_thread_group(
mem_profiler_chan: MemProfilerChan,
config_dir: Option<PathBuf>,
temporary_storage: bool,
label: &str,
) -> StorageThreads {
let client_storage: ClientStorageThreadHandle =
ClientStorageThreadFactory::new(config_dir.clone());
ClientStorageThreadFactory::new(config_dir.clone(), temporary_storage);
let idb: GenericSender<IndexedDBThreadMsg> = IndexedDBThreadFactory::new(
config_dir.clone(),
mem_profiler_chan.clone(),
@@ -37,10 +38,16 @@ fn new_storage_thread_group(
pub fn new_storage_threads(
mem_profiler_chan: MemProfilerChan,
config_dir: Option<PathBuf>,
temporary_storage: bool,
) -> (StorageThreads, StorageThreads) {
let private_storage_threads =
new_storage_thread_group(mem_profiler_chan.clone(), config_dir.clone(), "private");
let public_storage_threads = new_storage_thread_group(mem_profiler_chan, config_dir, "public");
let private_storage_threads = new_storage_thread_group(
mem_profiler_chan.clone(),
config_dir.clone(),
temporary_storage,
"private",
);
let public_storage_threads =
new_storage_thread_group(mem_profiler_chan, config_dir, temporary_storage, "public");
(private_storage_threads, public_storage_threads)
}

View File

@@ -19,7 +19,11 @@ fn install_test_namespace() {
}
fn registry_db_path(tmp_dir: &tempfile::TempDir) -> PathBuf {
tmp_dir.path().join("clientstorage").join("reg.sqlite")
tmp_dir
.path()
.join("clientstorage")
.join("default_v1")
.join("reg.sqlite")
}
fn open_registry(tmp_dir: &tempfile::TempDir) -> Connection {
@@ -42,7 +46,7 @@ fn obtain_bottle_map(
#[test]
fn test_exit() {
let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(None);
let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(None, false);
let (sender, receiver) = generic_channel::channel().unwrap();
handle
@@ -60,7 +64,7 @@ fn test_workflow() {
install_test_namespace();
let tmp_dir = tempfile::tempdir().unwrap();
let handle: ClientStorageThreadHandle =
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()));
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false);
let url = ServoUrl::parse("https://example.com").unwrap();
@@ -124,7 +128,7 @@ fn test_repeated_local_obtain_reuses_same_logical_rows() {
install_test_namespace();
let tmp_dir = tempfile::tempdir().unwrap();
let handle: ClientStorageThreadHandle =
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()));
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false);
let origin = ServoUrl::parse("https://example.com").unwrap().origin();
let webview = WebViewId::new(servo_base::id::TEST_PAINTER_ID);
@@ -179,7 +183,7 @@ fn test_repeated_session_obtain_reuses_same_logical_rows() {
install_test_namespace();
let tmp_dir = tempfile::tempdir().unwrap();
let handle: ClientStorageThreadHandle =
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()));
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false);
let origin = ServoUrl::parse("https://example.com").unwrap().origin();
let webview = WebViewId::new(servo_base::id::TEST_PAINTER_ID);
@@ -235,7 +239,7 @@ fn test_local_persistence_and_estimate() {
install_test_namespace();
let tmp_dir = tempfile::tempdir().unwrap();
let handle: ClientStorageThreadHandle =
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()));
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false);
let origin = ServoUrl::parse("https://example.com").unwrap().origin();
let webview = WebViewId::new(servo_base::id::TEST_PAINTER_ID);
@@ -287,7 +291,7 @@ fn test_storage_manager_operations_fail_for_opaque_origins() {
install_test_namespace();
let tmp_dir = tempfile::tempdir().unwrap();
let handle: ClientStorageThreadHandle =
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()));
ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false);
let origin = ServoUrl::parse("data:text/plain,hello").unwrap().origin();

View File

@@ -39,7 +39,7 @@ fn shutdown_storage_group(threads: &StorageThreads) {
fn test_new_storage_threads_create_independent_groups() {
let mem_profiler_chan = profile_mem::Profiler::create();
let (private_storage_threads, public_storage_threads) =
storage::new_storage_threads(mem_profiler_chan, None);
storage::new_storage_threads(mem_profiler_chan, None, false);
shutdown_storage_group(&private_storage_threads);
shutdown_storage_group(&public_storage_threads);

View File

@@ -22,7 +22,7 @@ impl WebStorageTest {
let tmp_dir = tempfile::tempdir().unwrap();
let config_dir = tmp_dir.path().to_path_buf();
let mem_profiler_chan = profile_mem::Profiler::create();
let threads = storage::new_storage_threads(mem_profiler_chan, Some(config_dir));
let threads = storage::new_storage_threads(mem_profiler_chan, Some(config_dir), false);
Self {
tmp_dir: Some(tmp_dir),
@@ -32,7 +32,7 @@ impl WebStorageTest {
pub(crate) fn new_in_memory() -> Self {
let mem_profiler_chan = profile_mem::Profiler::create();
let threads = storage::new_storage_threads(mem_profiler_chan, None);
let threads = storage::new_storage_threads(mem_profiler_chan, None, false);
Self {
tmp_dir: None,
@@ -44,7 +44,7 @@ impl WebStorageTest {
let tmp_dir = self.tmp_dir.take();
let config_dir = tmp_dir.as_ref().map(|d| d.path().to_path_buf());
let mem_profiler_chan = profile_mem::Profiler::create();
let threads = storage::new_storage_threads(mem_profiler_chan, config_dir);
let threads = storage::new_storage_threads(mem_profiler_chan, config_dir, false);
Self {
tmp_dir: tmp_dir,

View File

@@ -14,4 +14,5 @@ name = "servo_wakelock"
path = "lib.rs"
[dependencies]
embedder_traits = { workspace = true }
serde = { workspace = true }

View File

@@ -4,43 +4,21 @@
//! Platform abstraction for the Screen Wake Lock API.
//!
//! Defines [`WakeLockProvider`], a trait for acquiring and releasing OS-level
//! Defines [`WakeLockDelegate`], a trait for acquiring and releasing OS-level
//! wake locks. Platform-specific implementations will be added in follow-up
//! work. For now, [`NoOpWakeLockProvider`] is the only implementation and
//! work. For now, [`DefaultWakeLockDelegate`] is the only implementation and
//! does nothing.
//!
//! <https://w3c.github.io/screen-wake-lock/>
use std::error::Error;
use serde::{Deserialize, Serialize};
use embedder_traits::{WakeLockDelegate, WakeLockType};
/// The type of wake lock to acquire or release.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum WakeLockType {
Screen,
}
/// Trait for platform-specific wake lock support.
///
/// Implementations are responsible for interacting with the OS to prevent
/// the screen (or other resources) from sleeping while a wake lock is held.
pub trait WakeLockProvider: Send + Sync {
/// Acquire a wake lock of the given type, preventing the associated
/// resource from sleeping. Called when the aggregate lock count transitions
/// from 0 to 1. Returns an error if the OS fails to grant the lock.
fn acquire(&self, type_: WakeLockType) -> Result<(), Box<dyn Error>>;
/// Release a previously acquired wake lock of the given type, allowing
/// the resource to sleep. Called when the aggregate lock count transitions
/// from N to 0.
fn release(&self, type_: WakeLockType) -> Result<(), Box<dyn Error>>;
}
/// A no-op [`WakeLockProvider`] used when no platform implementation is
/// A no-op [`WakeLockDelegate`] used when no platform implementation is
/// available. All operations succeed silently.
pub struct NoOpWakeLockProvider;
pub struct DefaultWakeLockDelegate;
impl WakeLockProvider for NoOpWakeLockProvider {
impl WakeLockDelegate for DefaultWakeLockDelegate {
fn acquire(&self, _type_: WakeLockType) -> Result<(), Box<dyn Error>> {
Ok(())
}

View File

@@ -378,6 +378,10 @@ struct CmdArgs {
#[bpaf(argument("~/.config/servo"))]
config_dir: Option<PathBuf>,
/// Use temporary storage (data on disk will not persist across restarts).
#[bpaf(long)]
temporary_storage: bool,
///
/// Run as a content process and connect to the given pipe.
#[bpaf(argument("servo-ipc-channel.abcdefg"))]
@@ -634,6 +638,7 @@ fn parse_arguments_helper(args_without_binary: Args) -> ArgumentParsingResult {
fs::create_dir_all(config_dir).expect("Could not create config_dir");
}
});
let temporary_storage = cmd_args.temporary_storage;
if let Some(ref time_profiler_trace_path) = cmd_args.profiler_trace_path {
let mut path = PathBuf::from(time_profiler_trace_path);
path.pop();
@@ -706,6 +711,7 @@ fn parse_arguments_helper(args_without_binary: Args) -> ArgumentParsingResult {
random_pipeline_closure_probability: cmd_args.random_pipeline_closure_probability,
random_pipeline_closure_seed: cmd_args.random_pipeline_closure_seed,
config_dir,
temporary_storage,
shaders_path: cmd_args.shaders,
certificate_path: cmd_args
.certificate_path

View File

@@ -136,6 +136,9 @@ def run_tests(default_binary_path: str, multiprocess: bool, **kwargs: Any) -> in
with tempfile.TemporaryDirectory(prefix="servo-") as config_dir:
kwargs["binary_args"] += ["--config-dir", config_dir]
# Temporary workaround to avoid shared storage across parallel processes.
# Can be removed once per-process config dirs are supported.
kwargs["binary_args"] += ["--temporary-storage"]
wptrunner.run_tests(**kwargs)

View File

@@ -29,11 +29,11 @@ macro_rules! sizeof_checker (
);
// Update the sizes here
sizeof_checker!(size_event_target, EventTarget, 48);
sizeof_checker!(size_node, Node, 152);
sizeof_checker!(size_element, Element, 344);
sizeof_checker!(size_htmlelement, HTMLElement, 360);
sizeof_checker!(size_div, HTMLDivElement, 360);
sizeof_checker!(size_span, HTMLSpanElement, 360);
sizeof_checker!(size_text, Text, 184);
sizeof_checker!(size_characterdata, CharacterData, 184);
sizeof_checker!(size_event_target, EventTarget, 56);
sizeof_checker!(size_node, Node, 160);
sizeof_checker!(size_element, Element, 352);
sizeof_checker!(size_htmlelement, HTMLElement, 368);
sizeof_checker!(size_div, HTMLDivElement, 368);
sizeof_checker!(size_span, HTMLSpanElement, 368);
sizeof_checker!(size_text, Text, 192);
sizeof_checker!(size_characterdata, CharacterData, 192);

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

@@ -1,3 +0,0 @@
[fire-selectionchange-event-on-textcontrol-element-on-pressing-backspace.html]
[selectionchange event fired on an input element]
expected: FAIL

View File

@@ -1,6 +0,0 @@
[onselectionchange-on-distinct-text-controls.html]
[selectionchange event on each input element fires independently]
expected: FAIL
[selectionchange event on each textarea element fires independently]
expected: FAIL

View File

@@ -1,13 +0,0 @@
[selectionchange-bubble.html]
expected: TIMEOUT
[selectionchange bubbles from input]
expected: TIMEOUT
[selectionchange bubbles from input when focused]
expected: NOTRUN
[selectionchange bubbles from textarea]
expected: NOTRUN
[selectionchange bubbles from textarea when focused]
expected: NOTRUN

View File

@@ -1,3 +0,0 @@
[selectionchange-on-shadow-dom.html]
[selectionchange event fired on a shadow dom bubble to the document]
expected: FAIL

View File

@@ -1,132 +0,0 @@
[selectionchange.html]
[Modifying selectionStart value of the input element]
expected: FAIL
[Modifying selectionEnd value of the input element]
expected: FAIL
[Calling setSelectionRange() on the input element]
expected: FAIL
[Calling select() on the input element]
expected: FAIL
[Calling setRangeText() on the input element]
expected: FAIL
[Setting the same selectionStart value twice on the input element]
expected: FAIL
[Setting the same selectionEnd value twice on the input element]
expected: FAIL
[Setting the same selection range twice on the input element]
expected: FAIL
[Calling select() twice on the input element]
expected: FAIL
[Calling setRangeText() after select() on the input element]
expected: FAIL
[Calling setRangeText() repeatedly on the input element]
expected: FAIL
[Modifying selectionStart value of the disconnected input element]
expected: FAIL
[Modifying selectionEnd value of the disconnected input element]
expected: FAIL
[Calling setSelectionRange() on the disconnected input element]
expected: FAIL
[Calling select() on the disconnected input element]
expected: FAIL
[Calling setRangeText() on the disconnected input element]
expected: FAIL
[Setting the same selectionStart value twice on the disconnected input element]
expected: FAIL
[Setting the same selectionEnd value twice on the disconnected input element]
expected: FAIL
[Setting the same selection range twice on the disconnected input element]
expected: FAIL
[Calling select() twice on the disconnected input element]
expected: FAIL
[Calling setRangeText() after select() on the disconnected input element]
expected: FAIL
[Calling setRangeText() repeatedly on the disconnected input element]
expected: FAIL
[Modifying selectionStart value of the textarea element]
expected: FAIL
[Modifying selectionEnd value of the textarea element]
expected: FAIL
[Calling setSelectionRange() on the textarea element]
expected: FAIL
[Calling select() on the textarea element]
expected: FAIL
[Calling setRangeText() on the textarea element]
expected: FAIL
[Setting the same selectionStart value twice on the textarea element]
expected: FAIL
[Setting the same selectionEnd value twice on the textarea element]
expected: FAIL
[Setting the same selection range twice on the textarea element]
expected: FAIL
[Calling select() twice on the textarea element]
expected: FAIL
[Calling setRangeText() after select() on the textarea element]
expected: FAIL
[Calling setRangeText() repeatedly on the textarea element]
expected: FAIL
[Modifying selectionStart value of the disconnected textarea element]
expected: FAIL
[Modifying selectionEnd value of the disconnected textarea element]
expected: FAIL
[Calling setSelectionRange() on the disconnected textarea element]
expected: FAIL
[Calling select() on the disconnected textarea element]
expected: FAIL
[Calling setRangeText() on the disconnected textarea element]
expected: FAIL
[Setting the same selectionStart value twice on the disconnected textarea element]
expected: FAIL
[Setting the same selectionEnd value twice on the disconnected textarea element]
expected: FAIL
[Setting the same selection range twice on the disconnected textarea element]
expected: FAIL
[Calling select() twice on the disconnected textarea element]
expected: FAIL
[Calling setRangeText() after select() on the disconnected textarea element]
expected: FAIL
[Calling setRangeText() repeatedly on the disconnected textarea element]
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