Skip to content

Commit

Permalink
layout: Respond to shorthand property requests with real values (serv…
Browse files Browse the repository at this point in the history
…o#31277) (full)

{"fail_fast": false, "matrix": [{"name": "Linux WPT", "workflow": "linux", "wpt_layout": "all", "profile": "release", "unit_tests": true, "wpt_tests_to_run": ""}, {"name": "MacOS", "workflow": "macos", "wpt_layout": "none", "profile": "release", "unit_tests": true, "wpt_tests_to_run": ""}, {"name": "Windows", "workflow": "windows", "wpt_layout": "none", "profile": "release", "unit_tests": true, "wpt_tests_to_run": ""}, {"name": "Android", "workflow": "android", "wpt_layout": "none", "profile": "release", "unit_tests": false, "wpt_tests_to_run": ""}]}
  • Loading branch information
syvb committed Feb 15, 2024
1 parent 19667e1 commit e70a1b1
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 10 deletions.
60 changes: 60 additions & 0 deletions components/script/dom/abortcontroller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* 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/. */

use dom_struct::dom_struct;
use js::jsapi::Value;
use js::rust::{Handle, HandleObject};

use crate::dom::abortsignal::AbortSignal;
use crate::dom::bindings::codegen::Bindings::AbortControllerBinding::AbortControllerMethods;
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::JSContext;

#[dom_struct]
pub struct AbortController {
reflector_: Reflector,
signal: DomRoot<AbortSignal>,
}

impl AbortController {
// #[allow(crown::unrooted_must_root)]
pub fn new_inherited(signal: DomRoot<AbortSignal>) -> AbortController {
AbortController {
reflector_: Reflector::new(),
signal: signal,
}
}

fn new_with_proto(
global: &GlobalScope,
proto: Option<HandleObject>,
) -> DomRoot<AbortController> {
reflect_dom_object_with_proto(
Box::new(AbortController::new_inherited(AbortSignal::new(global))),
global,
proto,
)
}

#[allow(non_snake_case)]
pub fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
) -> DomRoot<AbortController> {
AbortController::new_with_proto(global, proto)
}
}

impl AbortControllerMethods for AbortController {
/// <https://dom.spec.whatwg.org/#dom-abortcontroller-signal>
fn Signal(&self) -> DomRoot<AbortSignal> {
self.signal.clone()
}
/// <https://dom.spec.whatwg.org/#dom-abortcontroller-abort>
fn Abort(&self, _cx: JSContext, reason: Handle<'_, Value>) {
self.signal.signal_abort(reason);
}
}
139 changes: 139 additions & 0 deletions components/script/dom/abortsignal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* 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/. */

use std::rc::Rc;

use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{ExceptionStackBehavior, Heap, JS_IsExceptionPending};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::wrappers::JS_SetPendingException;
use js::rust::HandleValue;

use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
use crate::dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventListenerOptions;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::domexception::DOMErrorName;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::types::DOMException;
use crate::script_runtime::JSContext;

#[derive(JSTraceable, MallocSizeOf)]
pub enum AbortAlgorithm {
RemoveEventListener(
DomRoot<EventTarget>,
DOMString,
#[ignore_malloc_size_of = "Rc"] Rc<EventListener>,
#[ignore_malloc_size_of = "generated"] EventListenerOptions,
),
}
impl AbortAlgorithm {
fn exec(self) {
match self {
Self::RemoveEventListener(target, ty, listener, options) => {
target.remove_event_listener(ty, Some(listener), options)
},
}
}
}

#[dom_struct]
pub struct AbortSignal {
event_target: EventTarget,
#[ignore_malloc_size_of = "Defined in rust-mozjs"]
reason: Heap<JSVal>,
abort_algorithms: DomRefCell<Vec<AbortAlgorithm>>,
}

impl AbortSignal {
pub fn new_inherited() -> Self {
Self {
event_target: EventTarget::new_inherited(),
reason: Heap::default(),
abort_algorithms: DomRefCell::default(),
}
}
pub fn new(global: &GlobalScope) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited()), global)
}
/// <https://dom.spec.whatwg.org/#abortsignal-add>
pub fn add_abort_algorithm(&self, alg: AbortAlgorithm) {
if !self.Aborted() {
self.abort_algorithms.borrow_mut().push(alg);
}
}
/// <https://dom.spec.whatwg.org/#abortsignal-signal-abort>
#[allow(unsafe_code)]
pub fn signal_abort(&self, reason: HandleValue) {
// 1. If signal is aborted, then return.
if self.Aborted() {
return;
}
// 2. Set signal’s abort reason to reason if it is given; otherwise to a new "AbortError" DOMException.
let cx = *GlobalScope::get_cx();
rooted!(in(cx) let mut new_reason = UndefinedValue());
let reason = if reason.is_undefined() {
let exception = DOMException::new(&self.global(), DOMErrorName::AbortError);
unsafe {
exception.to_jsval(cx, new_reason.handle_mut());
};
new_reason.handle()
} else {
reason
};
self.reason.set(reason.get());

// 3. For each algorithm of signal’s abort algorithms: run algorithm.
// 4. Empty signal’s abort algorithms.
for algorithm in self.abort_algorithms.borrow_mut().drain(..) {
algorithm.exec();
}

// 5. Fire an event named abort at signal.
let event = Event::new(
&self.global(),
atom!("abort"),
EventBubbles::DoesNotBubble,
EventCancelable::Cancelable,
);
event.fire(self.upcast());
// 6. For each dependentSignal of signal’s dependent signals,
// signal abort on dependentSignal with signal’s abort reason.
// TODO
}
}

impl AbortSignalMethods for AbortSignal {
// https://dom.spec.whatwg.org/#dom-abortsignal-onabort
event_handler!(Abort, GetOnabort, SetOnabort);
/// <https://dom.spec.whatwg.org/#dom-abortsignal-aborted>
fn Aborted(&self) -> bool {
!self.reason.get().is_undefined()
}
/// <https://dom.spec.whatwg.org/#dom-abortsignal-reason>
fn Reason(&self, _cx: JSContext) -> JSVal {
self.reason.get()
}
#[allow(unsafe_code)]
/// <https://dom.spec.whatwg.org/#dom-abortsignal-throwifaborted>
fn ThrowIfAborted(&self) {
let reason = self.reason.get();
if !reason.is_undefined() {
let cx = *GlobalScope::get_cx();
unsafe {
assert!(!JS_IsExceptionPending(cx));
rooted!(in(cx) let mut thrown = UndefinedValue());
reason.to_jsval(cx, thrown.handle_mut());
JS_SetPendingException(cx, thrown.handle(), ExceptionStackBehavior::Capture);
}
}
}
}
13 changes: 7 additions & 6 deletions components/script/dom/bindings/codegen/CodegenRust.py
Original file line number Diff line number Diff line change
Expand Up @@ -2156,7 +2156,7 @@ class CGImports(CGWrapper):
Generates the appropriate import/use statements.
"""
def __init__(self, child, descriptors, callbacks, dictionaries, enums, typedefs, imports, config,
ignored_warnings=None):
ignored_warnings=None, current_name=None):
"""
Adds a set of imports.
"""
Expand Down Expand Up @@ -2267,7 +2267,8 @@ def removeWrapperAndNullableTypes(types):
parentName = descriptor.getParentName()
while parentName:
descriptor = descriptorProvider.getDescriptor(parentName)
extras += [descriptor.path, descriptor.bindingPath]
if current_name != descriptor.ifaceName:
extras += [descriptor.path, descriptor.bindingPath]
parentName = descriptor.getParentName()
elif t.isType() and t.isRecord():
extras += ['crate::dom::bindings::record::Record']
Expand Down Expand Up @@ -6358,7 +6359,7 @@ def define(self):
return self.code


def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None, typedefs=None):
def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None, typedefs=None, currentName=None):
if not callbacks:
callbacks = []
if not dictionaries:
Expand Down Expand Up @@ -6651,7 +6652,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'std::default::Default',
'std::ffi::CString',
'std::ops::Deref',
], config)
], config, current_name=currentName)


class CGDescriptor(CGThing):
Expand Down Expand Up @@ -7219,7 +7220,7 @@ class CGBindingRoot(CGThing):
DomRoot codegen class for binding generation. Instantiate the class, and call
declare or define to generate header or cpp code (respectively).
"""
def __init__(self, config, prefix, webIDLFile):
def __init__(self, config, prefix, webIDLFile, name):
descriptors = config.getDescriptors(webIDLFile=webIDLFile,
hasInterfaceObject=True)
# We also want descriptors that have an interface prototype object
Expand Down Expand Up @@ -7285,7 +7286,7 @@ def __init__(self, config, prefix, webIDLFile):

# Add imports
curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks,
dictionaries, enums, typedefs)
dictionaries, enums, typedefs, name)

# Add the auto-generated comment.
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/bindings/codegen/Configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def __init__(self, config, interface, desc):
self.register = desc.get('register', True)
self.path = desc.get('path', pathDefault)
self.inRealmMethods = [name for name in desc.get('inRealms', [])]
self.ifaceName = ifaceName
self.bindingPath = f"crate::dom::bindings::codegen::Bindings::{ifaceName}Binding::{ifaceName}_Binding"
self.outerObjectHook = desc.get('outerObjectHook', 'None')
self.proxy = False
Expand Down
5 changes: 3 additions & 2 deletions components/script/dom/bindings/codegen/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ def main():

for webidl in webidls:
filename = os.path.join(webidls_dir, webidl)
prefix = "Bindings/%sBinding" % webidl[:-len(".webidl")]
module = CGBindingRoot(config, prefix, filename).define()
name = webidl[:-len(".webidl")]
prefix = "Bindings/%sBinding" % name
module = CGBindingRoot(config, prefix, filename, name).define()
if module:
with open(os.path.join(out_dir, prefix + ".rs"), "wb") as f:
f.write(module.encode("utf-8"))
Expand Down
14 changes: 12 additions & 2 deletions components/script/dom/eventtarget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use servo_atoms::Atom;
use servo_url::ServoUrl;

use super::bindings::trace::HashMapTracedValues;
use crate::dom::abortsignal::AbortAlgorithm;
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
use crate::dom::bindings::callback::{CallbackContainer, CallbackFunction, ExceptionHandling};
use crate::dom::bindings::cell::DomRefCell;
Expand Down Expand Up @@ -711,7 +712,7 @@ impl EventTarget {
None => return,
};
let mut handlers = self.handlers.borrow_mut();
let entry = match handlers.entry(Atom::from(ty)) {
let entry = match handlers.entry(Atom::from(ty.clone())) {
Occupied(entry) => entry.into_mut(),
Vacant(entry) => entry.insert(EventListeners(vec![])),
};
Expand All @@ -723,12 +724,20 @@ impl EventTarget {
};
let new_entry = EventListenerEntry {
phase: phase,
listener: EventListenerType::Additive(listener),
listener: EventListenerType::Additive(Rc::clone(&listener)),
once: options.once,
};
if !entry.contains(&new_entry) {
entry.push(new_entry);
}
if let Some(signal) = options.signal {
signal.add_abort_algorithm(AbortAlgorithm::RemoveEventListener(
DomRoot::from_ref(&self),
ty,
listener,
options.parent,
));
};
}

// https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
Expand Down Expand Up @@ -809,6 +818,7 @@ impl From<AddEventListenerOptionsOrBoolean> for AddEventListenerOptions {
AddEventListenerOptionsOrBoolean::Boolean(capture) => Self {
parent: EventListenerOptions { capture },
once: false,
signal: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/mediaquerylist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl MediaQueryListMethods for MediaQueryList {
AddEventListenerOptions {
parent: EventListenerOptions { capture: false },
once: false,
signal: None,
},
);
}
Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ pub mod types {
include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
}

pub mod abortcontroller;
pub mod abortsignal;
pub mod abstractworker;
pub mod abstractworkerglobalscope;
pub mod activation;
Expand Down
13 changes: 13 additions & 0 deletions components/script/dom/webidls/AbortController.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* 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/. */

// https://dom.spec.whatwg.org/#interface-abortcontroller
[Exposed=*]
interface AbortController {
constructor();

[SameObject] readonly attribute AbortSignal signal;

undefined abort(optional any reason);
};
17 changes: 17 additions & 0 deletions components/script/dom/webidls/AbortSignal.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* 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/. */

// https://dom.spec.whatwg.org/#interface-AbortSignal
[Exposed=*]
interface AbortSignal : EventTarget {
// [NewObject] static AbortSignal abort(optional any reason);
// [Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
// [NewObject] static AbortSignal _any(sequence<AbortSignal> signals);

readonly attribute boolean aborted;
readonly attribute any reason;
undefined throwIfAborted();

attribute EventHandler onabort;
};
1 change: 1 addition & 0 deletions components/script/dom/webidls/EventTarget.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ dictionary EventListenerOptions {
dictionary AddEventListenerOptions : EventListenerOptions {
// boolean passive = false;
boolean once = false;
AbortSignal signal;
};

0 comments on commit e70a1b1

Please sign in to comment.