mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
dom: IntersectionObserver initialization (#35314)
* Add internal slot definition Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Add initialization for new IntersectionObserver Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Move observer initialization Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Update WPT tests Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Make a copy of style IntersectionObserverRootMargin Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Move initialization to account for rooted expression Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Fix some fields typing Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Impl rest of IntersectionObserver interface Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Fix tidy issue Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Apply types logic and tidy fix from suggestions Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> * Add allow unrooted for add registration to element Signed-off-by: stevennovaryo <steven.novaryo@gmail.com> --------- Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
parent
610a1c2303
commit
becd097585
11 changed files with 439 additions and 64 deletions
|
@ -515,6 +515,8 @@ pub(crate) struct Document {
|
||||||
/// <https://w3c.github.io/webappsec-upgrade-insecure-requests/#insecure-requests-policy>
|
/// <https://w3c.github.io/webappsec-upgrade-insecure-requests/#insecure-requests-policy>
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
|
inherited_insecure_requests_policy: Cell<Option<InsecureRequestsPolicy>>,
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#document-intersectionobservertaskqueued>
|
||||||
|
intersection_observer_task_queued: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -3744,6 +3746,7 @@ impl Document {
|
||||||
status_code,
|
status_code,
|
||||||
is_initial_about_blank: Cell::new(is_initial_about_blank),
|
is_initial_about_blank: Cell::new(is_initial_about_blank),
|
||||||
inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
|
inherited_insecure_requests_policy: Cell::new(inherited_insecure_requests_policy),
|
||||||
|
intersection_observer_task_queued: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ use xml5ever::serialize::TraversalScope::{
|
||||||
|
|
||||||
use super::customelementregistry::is_valid_custom_element_name;
|
use super::customelementregistry::is_valid_custom_element_name;
|
||||||
use super::htmltablecolelement::{HTMLTableColElement, HTMLTableColElementLayoutHelpers};
|
use super::htmltablecolelement::{HTMLTableColElement, HTMLTableColElementLayoutHelpers};
|
||||||
|
use super::intersectionobserver::{IntersectionObserver, IntersectionObserverRegistration};
|
||||||
use crate::dom::activation::Activatable;
|
use crate::dom::activation::Activatable;
|
||||||
use crate::dom::attr::{Attr, AttrHelpersForLayout};
|
use crate::dom::attr::{Attr, AttrHelpersForLayout};
|
||||||
use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref, RefMut};
|
use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref, RefMut};
|
||||||
|
@ -613,6 +614,50 @@ impl Element {
|
||||||
Some(node) => node.is::<Document>(),
|
Some(node) => node.is::<Document>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return all IntersectionObserverRegistration for this element.
|
||||||
|
/// Lazily initialize the raredata if it does not exist.
|
||||||
|
pub(crate) fn registered_intersection_observers_mut(
|
||||||
|
&self,
|
||||||
|
) -> RefMut<Vec<IntersectionObserverRegistration>> {
|
||||||
|
RefMut::map(self.ensure_rare_data(), |rare_data| {
|
||||||
|
&mut rare_data.registered_intersection_observers
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn registered_intersection_observers(
|
||||||
|
&self,
|
||||||
|
) -> Option<Ref<Vec<IntersectionObserverRegistration>>> {
|
||||||
|
let rare_data: Ref<_> = self.rare_data.borrow();
|
||||||
|
|
||||||
|
if rare_data.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Ref::map(rare_data, |rare_data| {
|
||||||
|
&rare_data
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.registered_intersection_observers
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new IntersectionObserverRegistration to the element.
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
|
pub(crate) fn add_intersection_observer_registration(
|
||||||
|
&self,
|
||||||
|
registration: IntersectionObserverRegistration,
|
||||||
|
) {
|
||||||
|
self.ensure_rare_data()
|
||||||
|
.registered_intersection_observers
|
||||||
|
.push(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a certain IntersectionObserver.
|
||||||
|
pub(crate) fn remove_intersection_observer(&self, observer: &IntersectionObserver) {
|
||||||
|
self.ensure_rare_data()
|
||||||
|
.registered_intersection_observers
|
||||||
|
.retain(|reg_obs| *reg_obs.observer != *observer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
|
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
|
||||||
|
|
|
@ -2,23 +2,44 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use base::cross_process_instant::CrossProcessInstant;
|
||||||
|
use cssparser::{Parser, ParserInput};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::rust::{HandleObject, MutableHandleValue};
|
use js::rust::{HandleObject, MutableHandleValue};
|
||||||
|
use style::context::QuirksMode;
|
||||||
|
use style::parser::{Parse, ParserContext};
|
||||||
|
use style::stylesheets::{CssRuleType, Origin};
|
||||||
|
use style_traits::{ParsingMode, ToCss};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use super::bindings::codegen::Bindings::IntersectionObserverBinding::{
|
use super::bindings::codegen::Bindings::IntersectionObserverBinding::{
|
||||||
IntersectionObserverCallback, IntersectionObserverMethods,
|
IntersectionObserverCallback, IntersectionObserverMethods,
|
||||||
};
|
};
|
||||||
use super::bindings::codegen::UnionTypes::ElementOrDocument;
|
use super::intersectionobserverentry::IntersectionObserverEntry;
|
||||||
use super::types::{Element, IntersectionObserverEntry};
|
use super::intersectionobserverrootmargin::IntersectionObserverRootMargin;
|
||||||
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::IntersectionObserverBinding::IntersectionObserverInit;
|
use crate::dom::bindings::codegen::Bindings::IntersectionObserverBinding::IntersectionObserverInit;
|
||||||
|
use crate::dom::bindings::codegen::UnionTypes::{DoubleOrDoubleSequence, ElementOrDocument};
|
||||||
|
use crate::dom::bindings::error::Error;
|
||||||
|
use crate::dom::bindings::import::module::Fallible;
|
||||||
|
use crate::dom::bindings::num::Finite;
|
||||||
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
|
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
|
use crate::dom::bindings::utils::to_frozen_array;
|
||||||
|
use crate::dom::element::Element;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
use crate::script_runtime::{CanGc, JSContext};
|
use crate::script_runtime::{CanGc, JSContext};
|
||||||
|
|
||||||
|
/// > The intersection root for an IntersectionObserver is the value of its root attribute if the attribute is non-null;
|
||||||
|
/// > otherwise, it is the top-level browsing context’s document node, referred to as the implicit root.
|
||||||
|
///
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#intersectionobserver-intersection-root>
|
||||||
|
pub type IntersectionRoot = Option<ElementOrDocument>;
|
||||||
|
|
||||||
/// The Intersection Observer interface
|
/// The Intersection Observer interface
|
||||||
///
|
///
|
||||||
/// > The IntersectionObserver interface can be used to observe changes in the intersection
|
/// > The IntersectionObserver interface can be used to observe changes in the intersection
|
||||||
|
@ -29,33 +50,226 @@ use crate::script_runtime::{CanGc, JSContext};
|
||||||
pub(crate) struct IntersectionObserver {
|
pub(crate) struct IntersectionObserver {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
|
|
||||||
|
/// > The root provided to the IntersectionObserver constructor, or null if none was provided.
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-root>
|
||||||
|
root: IntersectionRoot,
|
||||||
|
|
||||||
/// > This callback will be invoked when there are changes to a target’s intersection
|
/// > This callback will be invoked when there are changes to a target’s intersection
|
||||||
/// > with the intersection root, as per the processing model.
|
/// > with the intersection root, as per the processing model.
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#intersection-observer-callback>
|
/// <https://w3c.github.io/IntersectionObserver/#intersection-observer-callback>
|
||||||
#[ignore_malloc_size_of = "Rc are hard"]
|
#[ignore_malloc_size_of = "Rc are hard"]
|
||||||
callback: Rc<IntersectionObserverCallback>,
|
callback: Rc<IntersectionObserverCallback>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-queuedentries-slot>
|
||||||
|
queued_entries: DomRefCell<Vec<Dom<IntersectionObserverEntry>>>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-observationtargets-slot>
|
||||||
|
observation_targets: DomRefCell<Vec<Dom<Element>>>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin-slot>
|
||||||
|
#[no_trace]
|
||||||
|
#[ignore_malloc_size_of = "Defined in style"]
|
||||||
|
root_margin: RefCell<IntersectionObserverRootMargin>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-scrollmargin-slot>
|
||||||
|
#[no_trace]
|
||||||
|
#[ignore_malloc_size_of = "Defined in style"]
|
||||||
|
scroll_margin: RefCell<IntersectionObserverRootMargin>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-thresholds-slot>
|
||||||
|
thresholds: RefCell<Vec<Finite<f64>>>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-delay-slot>
|
||||||
|
delay: Cell<i32>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility-slot>
|
||||||
|
track_visibility: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntersectionObserver {
|
impl IntersectionObserver {
|
||||||
pub(crate) fn new_inherited(
|
pub(crate) fn new_inherited(
|
||||||
callback: Rc<IntersectionObserverCallback>,
|
callback: Rc<IntersectionObserverCallback>,
|
||||||
_init: &IntersectionObserverInit,
|
root: IntersectionRoot,
|
||||||
|
root_margin: IntersectionObserverRootMargin,
|
||||||
|
scroll_margin: IntersectionObserverRootMargin,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
|
root,
|
||||||
callback,
|
callback,
|
||||||
|
queued_entries: Default::default(),
|
||||||
|
observation_targets: Default::default(),
|
||||||
|
root_margin: RefCell::new(root_margin),
|
||||||
|
scroll_margin: RefCell::new(scroll_margin),
|
||||||
|
thresholds: Default::default(),
|
||||||
|
delay: Default::default(),
|
||||||
|
track_visibility: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#initialize-new-intersection-observer>
|
||||||
fn new(
|
fn new(
|
||||||
window: &Window,
|
window: &Window,
|
||||||
proto: Option<HandleObject>,
|
proto: Option<HandleObject>,
|
||||||
callback: Rc<IntersectionObserverCallback>,
|
callback: Rc<IntersectionObserverCallback>,
|
||||||
init: &IntersectionObserverInit,
|
init: &IntersectionObserverInit,
|
||||||
can_gc: CanGc,
|
can_gc: CanGc,
|
||||||
) -> DomRoot<Self> {
|
) -> Fallible<DomRoot<Self>> {
|
||||||
let observer = Box::new(Self::new_inherited(callback, init));
|
// Step 3.
|
||||||
reflect_dom_object_with_proto(observer, window, proto, can_gc)
|
// > Attempt to parse a margin from options.rootMargin. If a list is returned,
|
||||||
|
// > set this’s internal [[rootMargin]] slot to that. Otherwise, throw a SyntaxError exception.
|
||||||
|
let root_margin = if let Ok(margin) = parse_a_margin(init.rootMargin.as_ref()) {
|
||||||
|
margin
|
||||||
|
} else {
|
||||||
|
return Err(Error::Syntax);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
|
// > Attempt to parse a margin from options.scrollMargin. If a list is returned,
|
||||||
|
// > set this’s internal [[scrollMargin]] slot to that. Otherwise, throw a SyntaxError exception.
|
||||||
|
let scroll_margin = if let Ok(margin) = parse_a_margin(init.scrollMargin.as_ref()) {
|
||||||
|
margin
|
||||||
|
} else {
|
||||||
|
return Err(Error::Syntax);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 1 and step 2, 3, 4 setter
|
||||||
|
// > 1. Let this be a new IntersectionObserver object
|
||||||
|
// > 2. Set this’s internal [[callback]] slot to callback.
|
||||||
|
// > 3. ... set this’s internal [[rootMargin]] slot to that.
|
||||||
|
// > 4. ,.. set this’s internal [[scrollMargin]] slot to that.
|
||||||
|
//
|
||||||
|
// Owned root is also passed to the constructor.
|
||||||
|
let observer = reflect_dom_object_with_proto(
|
||||||
|
Box::new(Self::new_inherited(
|
||||||
|
callback,
|
||||||
|
init.root.clone(),
|
||||||
|
root_margin,
|
||||||
|
scroll_margin,
|
||||||
|
)),
|
||||||
|
window,
|
||||||
|
proto,
|
||||||
|
can_gc,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 5-13
|
||||||
|
observer.init_observer(init)?;
|
||||||
|
|
||||||
|
Ok(observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Step 5-13 of <https://w3c.github.io/IntersectionObserver/#initialize-new-intersection-observer>
|
||||||
|
fn init_observer(&self, init: &IntersectionObserverInit) -> Fallible<()> {
|
||||||
|
// Step 5
|
||||||
|
// > Let thresholds be a list equal to options.threshold.
|
||||||
|
//
|
||||||
|
// Non-sequence value should be converted into Vec.
|
||||||
|
// Default value of thresholds is [0].
|
||||||
|
let mut thresholds = match &init.threshold {
|
||||||
|
Some(DoubleOrDoubleSequence::Double(num)) => vec![*num],
|
||||||
|
Some(DoubleOrDoubleSequence::DoubleSequence(sequence)) => sequence.clone(),
|
||||||
|
None => vec![Finite::wrap(0.)],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 6
|
||||||
|
// > If any value in thresholds is less than 0.0 or greater than 1.0, throw a RangeError exception.
|
||||||
|
for num in &thresholds {
|
||||||
|
if **num < 0.0 || **num > 1.0 {
|
||||||
|
return Err(Error::Range(
|
||||||
|
"Value in thresholds should not be less than 0.0 or greater than 1.0"
|
||||||
|
.to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7
|
||||||
|
// > Sort thresholds in ascending order.
|
||||||
|
thresholds.sort_by(|lhs, rhs| lhs.partial_cmp(&**rhs).unwrap());
|
||||||
|
|
||||||
|
// Step 8
|
||||||
|
// > If thresholds is empty, append 0 to thresholds.
|
||||||
|
if thresholds.is_empty() {
|
||||||
|
thresholds.push(Finite::wrap(0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9
|
||||||
|
// > The thresholds attribute getter will return this sorted thresholds list.
|
||||||
|
//
|
||||||
|
// Set this’s internal [[thresholds]] slot to the sorted thresholds list
|
||||||
|
// and getter will return the internal [[thresholds]] slot.
|
||||||
|
self.thresholds.replace(thresholds);
|
||||||
|
|
||||||
|
// Step 10
|
||||||
|
// > Let delay be the value of options.delay.
|
||||||
|
//
|
||||||
|
// Default value of delay is 0.
|
||||||
|
let mut delay = init.delay.unwrap_or(0);
|
||||||
|
|
||||||
|
// Step 11
|
||||||
|
// > If options.trackVisibility is true and delay is less than 100, set delay to 100.
|
||||||
|
if init.trackVisibility {
|
||||||
|
delay = delay.max(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 12
|
||||||
|
// > Set this’s internal [[delay]] slot to options.delay to delay.
|
||||||
|
self.delay.set(delay);
|
||||||
|
|
||||||
|
// Step 13
|
||||||
|
// > Set this’s internal [[trackVisibility]] slot to options.trackVisibility.
|
||||||
|
self.track_visibility.set(init.trackVisibility);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#observe-target-element>
|
||||||
|
fn observe_target_element(&self, target: &Element) {
|
||||||
|
// Step 1
|
||||||
|
// > If target is in observer’s internal [[ObservationTargets]] slot, return.
|
||||||
|
let is_present = self
|
||||||
|
.observation_targets
|
||||||
|
.borrow()
|
||||||
|
.iter()
|
||||||
|
.any(|element| &**element == target);
|
||||||
|
if is_present {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
// > Let intersectionObserverRegistration be an IntersectionObserverRegistration record with
|
||||||
|
// > an observer property set to observer, a previousThresholdIndex property set to -1,
|
||||||
|
// > a previousIsIntersecting property set to false, and a previousIsVisible property set to false.
|
||||||
|
// Step 3
|
||||||
|
// > Append intersectionObserverRegistration to target’s internal [[RegisteredIntersectionObservers]] slot.
|
||||||
|
target.add_intersection_observer_registration(IntersectionObserverRegistration {
|
||||||
|
observer: Dom::from_ref(self),
|
||||||
|
previous_threshold_index: Cell::new(-1),
|
||||||
|
previous_is_intersecting: Cell::new(false),
|
||||||
|
last_update_time: Cell::new(CrossProcessInstant::epoch()),
|
||||||
|
previous_is_visible: Cell::new(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 4
|
||||||
|
// > Add target to observer’s internal [[ObservationTargets]] slot.
|
||||||
|
self.observation_targets
|
||||||
|
.borrow_mut()
|
||||||
|
.push(Dom::from_ref(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#unobserve-target-element>
|
||||||
|
fn unobserve_target_element(&self, target: &Element) {
|
||||||
|
// Step 1
|
||||||
|
// > Remove the IntersectionObserverRegistration record whose observer property is equal to
|
||||||
|
// > this from target’s internal [[RegisteredIntersectionObservers]] slot, if present.
|
||||||
|
target
|
||||||
|
.registered_intersection_observers_mut()
|
||||||
|
.retain(|registration| &*registration.observer != self);
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
// > Remove target from this’s internal [[ObservationTargets]] slot, if present
|
||||||
|
self.observation_targets
|
||||||
|
.borrow_mut()
|
||||||
|
.retain(|element| &**element != target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +278,7 @@ impl IntersectionObserverMethods<crate::DomTypeHolder> for IntersectionObserver
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-root>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-root>
|
||||||
fn GetRoot(&self) -> Option<ElementOrDocument> {
|
fn GetRoot(&self) -> Option<ElementOrDocument> {
|
||||||
None
|
self.root.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// > Offsets applied to the root intersection rectangle, effectively growing or
|
/// > Offsets applied to the root intersection rectangle, effectively growing or
|
||||||
|
@ -74,7 +288,7 @@ impl IntersectionObserverMethods<crate::DomTypeHolder> for IntersectionObserver
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin>
|
||||||
fn RootMargin(&self) -> DOMString {
|
fn RootMargin(&self) -> DOMString {
|
||||||
DOMString::new()
|
DOMString::from_string(self.root_margin.borrow().to_css_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// > Offsets are applied to scrollports on the path from intersection root to target,
|
/// > Offsets are applied to scrollports on the path from intersection root to target,
|
||||||
|
@ -82,7 +296,7 @@ impl IntersectionObserverMethods<crate::DomTypeHolder> for IntersectionObserver
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-scrollmargin>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-scrollmargin>
|
||||||
fn ScrollMargin(&self) -> DOMString {
|
fn ScrollMargin(&self) -> DOMString {
|
||||||
DOMString::new()
|
DOMString::from_string(self.scroll_margin.borrow().to_css_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// > A list of thresholds, sorted in increasing numeric order, where each threshold
|
/// > A list of thresholds, sorted in increasing numeric order, where each threshold
|
||||||
|
@ -92,39 +306,59 @@ impl IntersectionObserverMethods<crate::DomTypeHolder> for IntersectionObserver
|
||||||
/// > constructor, or the sequence is empty, the value of this attribute will be `[0]`.
|
/// > constructor, or the sequence is empty, the value of this attribute will be `[0]`.
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-thresholds>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-thresholds>
|
||||||
fn Thresholds(&self, _context: JSContext, _retval: MutableHandleValue) {}
|
fn Thresholds(&self, context: JSContext, retval: MutableHandleValue) {
|
||||||
|
to_frozen_array(&self.thresholds.borrow(), context, retval);
|
||||||
|
}
|
||||||
|
|
||||||
/// > A number indicating the minimum delay in milliseconds between notifications from
|
/// > A number indicating the minimum delay in milliseconds between notifications from
|
||||||
/// > this observer for a given target.
|
/// > this observer for a given target.
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-delay>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-delay>
|
||||||
fn Delay(&self) -> i32 {
|
fn Delay(&self) -> i32 {
|
||||||
0
|
self.delay.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// > A boolean indicating whether this IntersectionObserver will track changes in a target’s visibility.
|
/// > A boolean indicating whether this IntersectionObserver will track changes in a target’s visibility.
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility>
|
||||||
fn TrackVisibility(&self) -> bool {
|
fn TrackVisibility(&self) -> bool {
|
||||||
false
|
self.track_visibility.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// > Run the observe a target Element algorithm, providing this and target.
|
/// > Run the observe a target Element algorithm, providing this and target.
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-observe>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-observe>
|
||||||
fn Observe(&self, _target: &Element) {}
|
fn Observe(&self, target: &Element) {
|
||||||
|
self.observe_target_element(target);
|
||||||
|
}
|
||||||
|
|
||||||
/// > Run the unobserve a target Element algorithm, providing this and target.
|
/// > Run the unobserve a target Element algorithm, providing this and target.
|
||||||
///
|
///
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-unobserve>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-unobserve>
|
||||||
fn Unobserve(&self, _target: &Element) {}
|
fn Unobserve(&self, target: &Element) {
|
||||||
|
self.unobserve_target_element(target);
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-disconnect>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-disconnect>
|
||||||
fn Disconnect(&self) {}
|
fn Disconnect(&self) {
|
||||||
|
// > For each target in this’s internal [[ObservationTargets]] slot:
|
||||||
|
self.observation_targets.borrow().iter().for_each(|target| {
|
||||||
|
// > 1. Remove the IntersectionObserverRegistration record whose observer property is equal to
|
||||||
|
// > this from target’s internal [[RegisteredIntersectionObservers]] slot.
|
||||||
|
target.remove_intersection_observer(self);
|
||||||
|
});
|
||||||
|
// > 2. Remove target from this’s internal [[ObservationTargets]] slot.
|
||||||
|
self.observation_targets.borrow_mut().clear();
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-takerecords>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-takerecords>
|
||||||
fn TakeRecords(&self) -> Vec<DomRoot<IntersectionObserverEntry>> {
|
fn TakeRecords(&self) -> Vec<DomRoot<IntersectionObserverEntry>> {
|
||||||
vec![]
|
// Step 1-3.
|
||||||
|
self.queued_entries
|
||||||
|
.take()
|
||||||
|
.iter()
|
||||||
|
.map(|entry| entry.as_rooted())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-intersectionobserver>
|
/// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-intersectionobserver>
|
||||||
|
@ -134,7 +368,50 @@ impl IntersectionObserverMethods<crate::DomTypeHolder> for IntersectionObserver
|
||||||
can_gc: CanGc,
|
can_gc: CanGc,
|
||||||
callback: Rc<IntersectionObserverCallback>,
|
callback: Rc<IntersectionObserverCallback>,
|
||||||
init: &IntersectionObserverInit,
|
init: &IntersectionObserverInit,
|
||||||
) -> DomRoot<IntersectionObserver> {
|
) -> Fallible<DomRoot<IntersectionObserver>> {
|
||||||
Self::new(window, proto, callback, init, can_gc)
|
Self::new(window, proto, callback, init, can_gc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#intersectionobserverregistration>
|
||||||
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||||
|
pub(crate) struct IntersectionObserverRegistration {
|
||||||
|
pub(crate) observer: Dom<IntersectionObserver>,
|
||||||
|
previous_threshold_index: Cell<i32>,
|
||||||
|
previous_is_intersecting: Cell<bool>,
|
||||||
|
#[no_trace]
|
||||||
|
last_update_time: Cell<CrossProcessInstant>,
|
||||||
|
previous_is_visible: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#parse-a-margin>
|
||||||
|
fn parse_a_margin(value: Option<&DOMString>) -> Result<IntersectionObserverRootMargin, ()> {
|
||||||
|
// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserverinit-rootmargin> &&
|
||||||
|
// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserverinit-scrollmargin>
|
||||||
|
// > ... defaulting to "0px".
|
||||||
|
let value = match value {
|
||||||
|
Some(str) => str.str(),
|
||||||
|
_ => "0px",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create necessary style ParserContext and utilize stylo's IntersectionObserverRootMargin
|
||||||
|
let mut input = ParserInput::new(value);
|
||||||
|
let mut parser = Parser::new(&mut input);
|
||||||
|
|
||||||
|
let url = Url::parse("about:blank").unwrap().into();
|
||||||
|
let context = ParserContext::new(
|
||||||
|
Origin::Author,
|
||||||
|
&url,
|
||||||
|
Some(CssRuleType::Style),
|
||||||
|
ParsingMode::DEFAULT,
|
||||||
|
QuirksMode::NoQuirks,
|
||||||
|
/* namespaces = */ Default::default(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
parser
|
||||||
|
.parse_entirely(|p| IntersectionObserverRootMargin::parse(&context, p))
|
||||||
|
.map_err(|_| ())
|
||||||
|
}
|
||||||
|
|
84
components/script/dom/intersectionobserverrootmargin.rs
Normal file
84
components/script/dom/intersectionobserverrootmargin.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! Copy of Stylo Gecko's [`style::values::specified::gecko::IntersectionObserverRootMargin`] implementation.
|
||||||
|
//! TODO: expose the object to servo as well in Stylo
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use cssparser::{match_ignore_ascii_case, Parser, Token};
|
||||||
|
use style::parser::{Parse, ParserContext};
|
||||||
|
use style::values::computed::{self, Length, LengthPercentage};
|
||||||
|
use style::values::generics::rect::Rect;
|
||||||
|
use style_traits::values::SequenceWriter;
|
||||||
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
|
fn parse_pixel_or_percent<'i>(
|
||||||
|
_context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, '_>,
|
||||||
|
) -> Result<LengthPercentage, ParseError<'i>> {
|
||||||
|
let location = input.current_source_location();
|
||||||
|
let token = input.next()?;
|
||||||
|
let value = match *token {
|
||||||
|
Token::Dimension {
|
||||||
|
value, ref unit, ..
|
||||||
|
} => {
|
||||||
|
match_ignore_ascii_case! { unit,
|
||||||
|
"px" => Ok(LengthPercentage::new_length(Length::new(value))),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Token::Percentage { unit_value, .. } => Ok(LengthPercentage::new_percent(
|
||||||
|
computed::Percentage(unit_value),
|
||||||
|
)),
|
||||||
|
_ => Err(()),
|
||||||
|
};
|
||||||
|
value.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value of an IntersectionObserver's rootMargin property.
|
||||||
|
///
|
||||||
|
/// Only bare px or percentage values are allowed. Other length units and
|
||||||
|
/// calc() values are not allowed.
|
||||||
|
///
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#parse-a-root-margin>
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct IntersectionObserverRootMargin(pub Rect<LengthPercentage>);
|
||||||
|
|
||||||
|
impl Parse for IntersectionObserverRootMargin {
|
||||||
|
fn parse<'i>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, '_>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
use style::Zero;
|
||||||
|
if input.is_exhausted() {
|
||||||
|
// If there are zero elements in tokens, set tokens to ["0px"].
|
||||||
|
return Ok(IntersectionObserverRootMargin(Rect::all(
|
||||||
|
LengthPercentage::zero(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let rect = Rect::parse_with(context, input, parse_pixel_or_percent)?;
|
||||||
|
Ok(IntersectionObserverRootMargin(rect))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strictly speaking this is not ToCss. It's serializing for DOM. But
|
||||||
|
// we can just reuse the infrastructure of this.
|
||||||
|
//
|
||||||
|
// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin>
|
||||||
|
impl ToCss for IntersectionObserverRootMargin {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
// We cannot use the ToCss impl of Rect, because that would
|
||||||
|
// merge items when they are equal. We want to list them all.
|
||||||
|
let mut writer = SequenceWriter::new(dest, " ");
|
||||||
|
let rect = &self.0;
|
||||||
|
writer.item(&rect.0)?;
|
||||||
|
writer.item(&rect.1)?;
|
||||||
|
writer.item(&rect.2)?;
|
||||||
|
writer.item(&rect.3)
|
||||||
|
}
|
||||||
|
}
|
|
@ -422,6 +422,7 @@ pub(crate) mod imagedata;
|
||||||
pub(crate) mod inputevent;
|
pub(crate) mod inputevent;
|
||||||
pub(crate) mod intersectionobserver;
|
pub(crate) mod intersectionobserver;
|
||||||
pub(crate) mod intersectionobserverentry;
|
pub(crate) mod intersectionobserverentry;
|
||||||
|
pub(crate) mod intersectionobserverrootmargin;
|
||||||
pub(crate) mod keyboardevent;
|
pub(crate) mod keyboardevent;
|
||||||
pub(crate) mod location;
|
pub(crate) mod location;
|
||||||
pub(crate) mod mediadeviceinfo;
|
pub(crate) mod mediadeviceinfo;
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crate::dom::customelementregistry::{
|
||||||
};
|
};
|
||||||
use crate::dom::elementinternals::ElementInternals;
|
use crate::dom::elementinternals::ElementInternals;
|
||||||
use crate::dom::htmlslotelement::SlottableData;
|
use crate::dom::htmlslotelement::SlottableData;
|
||||||
|
use crate::dom::intersectionobserver::IntersectionObserverRegistration;
|
||||||
use crate::dom::mutationobserver::RegisteredObserver;
|
use crate::dom::mutationobserver::RegisteredObserver;
|
||||||
use crate::dom::node::UniqueId;
|
use crate::dom::node::UniqueId;
|
||||||
use crate::dom::shadowroot::ShadowRoot;
|
use crate::dom::shadowroot::ShadowRoot;
|
||||||
|
@ -58,4 +59,9 @@ pub(crate) struct ElementRareData {
|
||||||
pub(crate) client_rect: Option<LayoutValue<Rect<i32>>>,
|
pub(crate) client_rect: Option<LayoutValue<Rect<i32>>>,
|
||||||
/// <https://html.spec.whatwg.org/multipage#elementinternals>
|
/// <https://html.spec.whatwg.org/multipage#elementinternals>
|
||||||
pub(crate) element_internals: Option<Dom<ElementInternals>>,
|
pub(crate) element_internals: Option<Dom<ElementInternals>>,
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/IntersectionObserver/#element-private-slots>
|
||||||
|
/// > Element objects have an internal [[RegisteredIntersectionObservers]] slot,
|
||||||
|
/// > which is initialized to an empty list. This list holds IntersectionObserverRegistration records, which have:
|
||||||
|
pub(crate) registered_intersection_observers: Vec<IntersectionObserverRegistration>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -608,6 +608,10 @@ Unions = {
|
||||||
'derives': ['Clone']
|
'derives': ['Clone']
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'ElementOrDocument': {
|
||||||
|
'derives': ['Clone', 'MallocSizeOf']
|
||||||
|
},
|
||||||
|
|
||||||
'HTMLCanvasElementOrOffscreenCanvas': {
|
'HTMLCanvasElementOrOffscreenCanvas': {
|
||||||
'derives': ['Clone', 'MallocSizeOf']
|
'derives': ['Clone', 'MallocSizeOf']
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,7 +18,7 @@ dictionary IntersectionObserverInit {
|
||||||
|
|
||||||
[Pref="dom_intersection_observer_enabled", Exposed=(Window)]
|
[Pref="dom_intersection_observer_enabled", Exposed=(Window)]
|
||||||
interface IntersectionObserver {
|
interface IntersectionObserver {
|
||||||
constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
|
[Throws] constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
|
||||||
readonly attribute (Element or Document)? root;
|
readonly attribute (Element or Document)? root;
|
||||||
readonly attribute DOMString rootMargin;
|
readonly attribute DOMString rootMargin;
|
||||||
readonly attribute DOMString scrollMargin;
|
readonly attribute DOMString scrollMargin;
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[idlharness.window.html]
|
|
||||||
[IntersectionObserver interface: observer must inherit property "thresholds" with the proper type]
|
|
||||||
expected: FAIL
|
|
|
@ -1,21 +0,0 @@
|
||||||
[observer-attributes.html]
|
|
||||||
[observer.thresholds]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[observer.rootMargin]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[empty observer.thresholds]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[whitespace observer.rootMargin]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[set observer.root]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[set observer.thresholds]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[set observer.rootMargin]
|
|
||||||
expected: FAIL
|
|
|
@ -1,21 +0,0 @@
|
||||||
[observer-exceptions.html]
|
|
||||||
[IntersectionObserver constructor with { threshold: [1.1\] }]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[IntersectionObserver constructor with { rootMargin: "1" }]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[IntersectionObserver constructor with { rootMargin: "2em" }]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[IntersectionObserver constructor with { rootMargin: "auto" }]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[IntersectionObserver constructor with { rootMargin: "calc(1px + 2px)" }]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[IntersectionObserver constructor with { rootMargin: "1px !important" }]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[IntersectionObserver constructor with { rootMargin: "1px 1px 1px 1px 1px" }]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue