script: Unify script-based "update the rendering" and throttle it to 60 FPS

Instead of running "update the rendering" at every IPC message, only run
it when a timeout has occured in script. In addition, avoid updating the
rendering if a rendering update isn't necessary. This should greatly
reduce the amount of processing that has to happen in script.

Because we are running many fewer calls to "update the rendering" it is
reasonable now to ensure that these always work the same way. In
particular, we always run rAF and update the animation timeline when
updating the ernder

In addition, pull the following things out of reflow:

 - Code dealing with informing the Constellation that a Pipeline has
   become Idle when waiting for a screenshot.
 - Detecting when it is time to fulfill the `document.fonts.ready`
   promise.

The latter means that reflow can never cause a garbage collection,
making timing of reflows more consistent and simplifying many callsites
that need to do script queries.

Followup changes will seek to simplify the way that ScriptThread-driven
animation timeouts happen even simpler.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2025-07-14 10:26:29 +02:00
parent fed1953198
commit 48e14be7ff
40 changed files with 566 additions and 654 deletions

View file

@ -495,6 +495,10 @@ impl Layout for LayoutThread {
.as_mut() .as_mut()
.and_then(|tree| tree.compositor_info.scroll_tree.scroll_offset(id)) .and_then(|tree| tree.compositor_info.scroll_tree.scroll_offset(id))
} }
fn needs_new_display_list(&self) -> bool {
self.need_new_display_list.get()
}
} }
impl LayoutThread { impl LayoutThread {

View file

@ -1097,13 +1097,8 @@ impl CanvasState {
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
pub(crate) fn set_shadow_color( pub(crate) fn set_shadow_color(&self, canvas: Option<&HTMLCanvasElement>, value: DOMString) {
&self, if let Ok(rgba) = parse_color(canvas, &value) {
canvas: Option<&HTMLCanvasElement>,
value: DOMString,
can_gc: CanGc,
) {
if let Ok(rgba) = parse_color(canvas, &value, can_gc) {
self.state.borrow_mut().shadow_color = rgba; self.state.borrow_mut().shadow_color = rgba;
} }
} }
@ -1130,11 +1125,10 @@ impl CanvasState {
&self, &self,
canvas: Option<&HTMLCanvasElement>, canvas: Option<&HTMLCanvasElement>,
value: StringOrCanvasGradientOrCanvasPattern, value: StringOrCanvasGradientOrCanvasPattern,
can_gc: CanGc,
) { ) {
match value { match value {
StringOrCanvasGradientOrCanvasPattern::String(string) => { StringOrCanvasGradientOrCanvasPattern::String(string) => {
if let Ok(rgba) = parse_color(canvas, &string, can_gc) { if let Ok(rgba) = parse_color(canvas, &string) {
self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba); self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
} }
}, },
@ -1174,11 +1168,10 @@ impl CanvasState {
&self, &self,
canvas: Option<&HTMLCanvasElement>, canvas: Option<&HTMLCanvasElement>,
value: StringOrCanvasGradientOrCanvasPattern, value: StringOrCanvasGradientOrCanvasPattern,
can_gc: CanGc,
) { ) {
match value { match value {
StringOrCanvasGradientOrCanvasPattern::String(string) => { StringOrCanvasGradientOrCanvasPattern::String(string) => {
if let Ok(rgba) = parse_color(canvas, &string, can_gc) { if let Ok(rgba) = parse_color(canvas, &string) {
self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba); self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba);
} }
}, },
@ -1383,7 +1376,6 @@ impl CanvasState {
x: f64, x: f64,
y: f64, y: f64,
max_width: Option<f64>, max_width: Option<f64>,
can_gc: CanGc,
) { ) {
if !x.is_finite() || !y.is_finite() { if !x.is_finite() || !y.is_finite() {
return; return;
@ -1392,11 +1384,7 @@ impl CanvasState {
return; return;
} }
if self.state.borrow().font_style.is_none() { if self.state.borrow().font_style.is_none() {
self.set_font( self.set_font(canvas, CanvasContextState::DEFAULT_FONT_STYLE.into())
canvas,
CanvasContextState::DEFAULT_FONT_STYLE.into(),
can_gc,
)
} }
let is_rtl = match self.state.borrow().direction { let is_rtl = match self.state.borrow().direction {
@ -1429,11 +1417,7 @@ impl CanvasState {
can_gc: CanGc, can_gc: CanGc,
) -> DomRoot<TextMetrics> { ) -> DomRoot<TextMetrics> {
if self.state.borrow().font_style.is_none() { if self.state.borrow().font_style.is_none() {
self.set_font( self.set_font(canvas, CanvasContextState::DEFAULT_FONT_STYLE.into());
canvas,
CanvasContextState::DEFAULT_FONT_STYLE.into(),
can_gc,
);
} }
let (sender, receiver) = ipc::channel::<CanvasTextMetrics>().unwrap(); let (sender, receiver) = ipc::channel::<CanvasTextMetrics>().unwrap();
@ -1463,20 +1447,14 @@ impl CanvasState {
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-font // https://html.spec.whatwg.org/multipage/#dom-context-2d-font
pub(crate) fn set_font( pub(crate) fn set_font(&self, canvas: Option<&HTMLCanvasElement>, value: DOMString) {
&self,
canvas: Option<&HTMLCanvasElement>,
value: DOMString,
can_gc: CanGc,
) {
let canvas = match canvas { let canvas = match canvas {
Some(element) => element, Some(element) => element,
None => return, // offscreen canvas doesn't have a placeholder canvas None => return, // offscreen canvas doesn't have a placeholder canvas
}; };
let node = canvas.upcast::<Node>(); let node = canvas.upcast::<Node>();
let window = canvas.owner_window(); let window = canvas.owner_window();
let resolved_font_style = let resolved_font_style = match window.resolved_font_style_query(node, value.to_string()) {
match window.resolved_font_style_query(node, value.to_string(), can_gc) {
Some(value) => value, Some(value) => value,
None => return, // syntax error None => return, // syntax error
}; };
@ -2204,7 +2182,6 @@ impl Drop for CanvasState {
pub(crate) fn parse_color( pub(crate) fn parse_color(
canvas: Option<&HTMLCanvasElement>, canvas: Option<&HTMLCanvasElement>,
string: &str, string: &str,
can_gc: CanGc,
) -> Result<AbsoluteColor, ()> { ) -> Result<AbsoluteColor, ()> {
let mut input = ParserInput::new(string); let mut input = ParserInput::new(string);
let mut parser = Parser::new(&mut input); let mut parser = Parser::new(&mut input);
@ -2233,8 +2210,8 @@ pub(crate) fn parse_color(
None => AbsoluteColor::BLACK, None => AbsoluteColor::BLACK,
Some(canvas) => { Some(canvas) => {
let canvas_element = canvas.upcast::<Element>(); let canvas_element = canvas.upcast::<Element>();
match canvas_element.style(can_gc) { match canvas_element.style() {
Some(ref s) if canvas_element.has_css_layout_box(can_gc) => { Some(ref s) if canvas_element.has_css_layout_box() => {
s.get_inherited_text().color s.get_inherited_text().color
}, },
_ => AbsoluteColor::BLACK, _ => AbsoluteColor::BLACK,

View file

@ -216,7 +216,7 @@ pub(crate) fn handle_get_attribute_style(
let name = style.Item(i); let name = style.Item(i);
NodeStyle { NodeStyle {
name: name.to_string(), name: name.to_string(),
value: style.GetPropertyValue(name.clone(), can_gc).to_string(), value: style.GetPropertyValue(name.clone()).to_string(),
priority: style.GetPropertyPriority(name).to_string(), priority: style.GetPropertyPriority(name).to_string(),
} }
}) })
@ -259,7 +259,7 @@ pub(crate) fn handle_get_stylesheet_style(
let name = style.Item(i); let name = style.Item(i);
NodeStyle { NodeStyle {
name: name.to_string(), name: name.to_string(),
value: style.GetPropertyValue(name.clone(), can_gc).to_string(), value: style.GetPropertyValue(name.clone()).to_string(),
priority: style.GetPropertyPriority(name).to_string(), priority: style.GetPropertyPriority(name).to_string(),
} }
}) })
@ -315,7 +315,6 @@ pub(crate) fn handle_get_computed_style(
pipeline: PipelineId, pipeline: PipelineId,
node_id: String, node_id: String,
reply: IpcSender<Option<Vec<NodeStyle>>>, reply: IpcSender<Option<Vec<NodeStyle>>>,
can_gc: CanGc,
) { ) {
let node = match find_node_by_unique_id(documents, pipeline, &node_id) { let node = match find_node_by_unique_id(documents, pipeline, &node_id) {
None => return reply.send(None).unwrap(), None => return reply.send(None).unwrap(),
@ -333,9 +332,7 @@ pub(crate) fn handle_get_computed_style(
let name = computed_style.Item(i); let name = computed_style.Item(i);
NodeStyle { NodeStyle {
name: name.to_string(), name: name.to_string(),
value: computed_style value: computed_style.GetPropertyValue(name.clone()).to_string(),
.GetPropertyValue(name.clone(), can_gc)
.to_string(),
priority: computed_style.GetPropertyPriority(name).to_string(), priority: computed_style.GetPropertyPriority(name).to_string(),
} }
}) })
@ -375,7 +372,7 @@ pub(crate) fn handle_get_layout(
position: String::from(computed_style.Position()), position: String::from(computed_style.Position()),
z_index: String::from(computed_style.ZIndex()), z_index: String::from(computed_style.ZIndex()),
box_sizing: String::from(computed_style.BoxSizing()), box_sizing: String::from(computed_style.BoxSizing()),
auto_margins: determine_auto_margins(&node, can_gc), auto_margins: determine_auto_margins(&node),
margin_top: String::from(computed_style.MarginTop()), margin_top: String::from(computed_style.MarginTop()),
margin_right: String::from(computed_style.MarginRight()), margin_right: String::from(computed_style.MarginRight()),
margin_bottom: String::from(computed_style.MarginBottom()), margin_bottom: String::from(computed_style.MarginBottom()),
@ -394,8 +391,8 @@ pub(crate) fn handle_get_layout(
.unwrap(); .unwrap();
} }
fn determine_auto_margins(node: &Node, can_gc: CanGc) -> AutoMargins { fn determine_auto_margins(node: &Node) -> AutoMargins {
let style = node.style(can_gc).unwrap(); let style = node.style().unwrap();
let margin = style.get_margin(); let margin = style.get_margin();
AutoMargins { AutoMargins {
top: margin.margin_top.is_auto(), top: margin.margin_top.is_auto(),

View file

@ -57,12 +57,12 @@ impl CanvasGradient {
impl CanvasGradientMethods<crate::DomTypeHolder> for CanvasGradient { impl CanvasGradientMethods<crate::DomTypeHolder> for CanvasGradient {
// https://html.spec.whatwg.org/multipage/#dom-canvasgradient-addcolorstop // https://html.spec.whatwg.org/multipage/#dom-canvasgradient-addcolorstop
fn AddColorStop(&self, offset: Finite<f64>, color: DOMString, can_gc: CanGc) -> ErrorResult { fn AddColorStop(&self, offset: Finite<f64>, color: DOMString) -> ErrorResult {
if *offset < 0f64 || *offset > 1f64 { if *offset < 0f64 || *offset > 1f64 {
return Err(Error::IndexSize); return Err(Error::IndexSize);
} }
let color = match parse_color(None, &color, can_gc) { let color = match parse_color(None, &color) {
Ok(color) => color, Ok(color) => color,
Err(_) => return Err(Error::Syntax), Err(_) => return Err(Error::Syntax),
}; };

View file

@ -327,15 +327,9 @@ impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingCo
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext
fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>, can_gc: CanGc) { fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
self.canvas_state.fill_text( self.canvas_state
self.canvas.canvas().as_deref(), .fill_text(self.canvas.canvas().as_deref(), text, x, y, max_width);
text,
x,
y,
max_width,
can_gc,
);
self.mark_as_dirty(); self.mark_as_dirty();
} }
@ -355,9 +349,9 @@ impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingCo
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-font // https://html.spec.whatwg.org/multipage/#dom-context-2d-font
fn SetFont(&self, value: DOMString, can_gc: CanGc) { fn SetFont(&self, value: DOMString) {
self.canvas_state self.canvas_state
.set_font(self.canvas.canvas().as_deref(), value, can_gc) .set_font(self.canvas.canvas().as_deref(), value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign
@ -504,9 +498,9 @@ impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingCo
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern, can_gc: CanGc) { fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
self.canvas_state self.canvas_state
.set_stroke_style(self.canvas.canvas().as_deref(), value, can_gc) .set_stroke_style(self.canvas.canvas().as_deref(), value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
@ -515,9 +509,9 @@ impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingCo
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern, can_gc: CanGc) { fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
self.canvas_state self.canvas_state
.set_fill_style(self.canvas.canvas().as_deref(), value, can_gc) .set_fill_style(self.canvas.canvas().as_deref(), value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata
@ -715,8 +709,8 @@ impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingCo
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
fn SetShadowColor(&self, value: DOMString, can_gc: CanGc) { fn SetShadowColor(&self, value: DOMString) {
self.canvas_state self.canvas_state
.set_shadow_color(self.canvas.canvas().as_deref(), value, can_gc) .set_shadow_color(self.canvas.canvas().as_deref(), value)
} }
} }

View file

@ -204,7 +204,7 @@ macro_rules! css_properties(
$id.enabled_for_all_content(), $id.enabled_for_all_content(),
"Someone forgot a #[Pref] annotation" "Someone forgot a #[Pref] annotation"
); );
self.get_property_value($id, CanGc::note()) self.get_property_value($id)
} }
fn $setter(&self, value: DOMString) -> ErrorResult { fn $setter(&self, value: DOMString) -> ErrorResult {
debug_assert!( debug_assert!(
@ -268,7 +268,7 @@ impl CSSStyleDeclaration {
) )
} }
fn get_computed_style(&self, property: PropertyId, can_gc: CanGc) -> DOMString { fn get_computed_style(&self, property: PropertyId) -> DOMString {
match self.owner { match self.owner {
CSSStyleOwner::CSSRule(..) => { CSSStyleOwner::CSSRule(..) => {
panic!("get_computed_style called on CSSStyleDeclaration with a CSSRule owner") panic!("get_computed_style called on CSSStyleDeclaration with a CSSRule owner")
@ -280,20 +280,20 @@ impl CSSStyleDeclaration {
} }
let addr = node.to_trusted_node_address(); let addr = node.to_trusted_node_address();
node.owner_window() node.owner_window()
.resolved_style_query(addr, self.pseudo, property, can_gc) .resolved_style_query(addr, self.pseudo, property)
}, },
CSSStyleOwner::Null => DOMString::new(), CSSStyleOwner::Null => DOMString::new(),
} }
} }
fn get_property_value(&self, id: PropertyId, can_gc: CanGc) -> DOMString { fn get_property_value(&self, id: PropertyId) -> DOMString {
if matches!(self.owner, CSSStyleOwner::Null) { if matches!(self.owner, CSSStyleOwner::Null) {
return DOMString::new(); return DOMString::new();
} }
if self.readonly { if self.readonly {
// Readonly style declarations are used for getComputedStyle. // Readonly style declarations are used for getComputedStyle.
return self.get_computed_style(id, can_gc); return self.get_computed_style(id);
} }
let mut string = String::new(); let mut string = String::new();
@ -431,12 +431,12 @@ impl CSSStyleDeclarationMethods<crate::DomTypeHolder> for CSSStyleDeclaration {
} }
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
fn GetPropertyValue(&self, property: DOMString, can_gc: CanGc) -> DOMString { fn GetPropertyValue(&self, property: DOMString) -> DOMString {
let id = match PropertyId::parse_enabled_for_all_content(&property) { let id = match PropertyId::parse_enabled_for_all_content(&property) {
Ok(id) => id, Ok(id) => id,
Err(..) => return DOMString::new(), Err(..) => return DOMString::new(),
}; };
self.get_property_value(id, can_gc) self.get_property_value(id)
} }
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
@ -502,8 +502,8 @@ impl CSSStyleDeclarationMethods<crate::DomTypeHolder> for CSSStyleDeclaration {
} }
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
fn CssFloat(&self, can_gc: CanGc) -> DOMString { fn CssFloat(&self) -> DOMString {
self.get_property_value(PropertyId::NonCustom(LonghandId::Float.into()), can_gc) self.get_property_value(PropertyId::NonCustom(LonghandId::Float.into()))
} }
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat

View file

@ -1034,7 +1034,7 @@ impl Document {
/// Scroll to the target element, and when we do not find a target /// Scroll to the target element, and when we do not find a target
/// and the fragment is empty or "top", scroll to the top. /// and the fragment is empty or "top", scroll to the top.
/// <https://html.spec.whatwg.org/multipage/#scroll-to-the-fragment-identifier> /// <https://html.spec.whatwg.org/multipage/#scroll-to-the-fragment-identifier>
pub(crate) fn check_and_scroll_fragment(&self, fragment: &str, can_gc: CanGc) { pub(crate) fn check_and_scroll_fragment(&self, fragment: &str) {
let target = self.find_fragment_node(fragment); let target = self.find_fragment_node(fragment);
// Step 1 // Step 1
@ -1047,9 +1047,7 @@ impl Document {
// inside other scrollable containers. Ideally this should use an implementation of // inside other scrollable containers. Ideally this should use an implementation of
// `scrollIntoView` when that is available: // `scrollIntoView` when that is available:
// See https://github.com/servo/servo/issues/24059. // See https://github.com/servo/servo/issues/24059.
let rect = element let rect = element.upcast::<Node>().bounding_content_box_or_zero();
.upcast::<Node>()
.bounding_content_box_or_zero(can_gc);
// In order to align with element edges, we snap to unscaled pixel boundaries, since // In order to align with element edges, we snap to unscaled pixel boundaries, since
// the paint thread currently does the same for drawing elements. This is important // the paint thread currently does the same for drawing elements. This is important
@ -1073,7 +1071,7 @@ impl Document {
if let Some((x, y)) = point { if let Some((x, y)) = point {
self.window self.window
.scroll(x as f64, y as f64, ScrollBehavior::Instant, can_gc) .scroll(x as f64, y as f64, ScrollBehavior::Instant)
} }
} }
@ -1349,7 +1347,7 @@ impl Document {
// Notify the embedder to display an input method. // Notify the embedder to display an input method.
if let Some(kind) = elem.input_method_type() { if let Some(kind) = elem.input_method_type() {
let rect = elem.upcast::<Node>().bounding_content_box_or_zero(can_gc); let rect = elem.upcast::<Node>().bounding_content_box_or_zero();
let rect = Rect::new( let rect = Rect::new(
Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()), Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()),
Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()), Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()),
@ -2886,7 +2884,7 @@ impl Document {
// We finished loading the page, so if the `Window` is still waiting for // We finished loading the page, so if the `Window` is still waiting for
// the first layout, allow it. // the first layout, allow it.
if self.has_browsing_context && self.is_fully_active() { if self.has_browsing_context && self.is_fully_active() {
self.window().allow_layout_if_necessary(can_gc); self.window().allow_layout_if_necessary();
} }
// Deferred scripts have to wait for page to finish loading, // Deferred scripts have to wait for page to finish loading,
@ -3124,7 +3122,7 @@ impl Document {
update_with_current_instant(&document.load_event_end); update_with_current_instant(&document.load_event_end);
if let Some(fragment) = document.url().fragment() { if let Some(fragment) = document.url().fragment() {
document.check_and_scroll_fragment(fragment, CanGc::note()); document.check_and_scroll_fragment(fragment);
} }
})); }));
@ -3658,6 +3656,31 @@ impl Document {
self.webgpu_contexts.clone() self.webgpu_contexts.clone()
} }
/// Whether or not this [`Document`] needs a rendering update, due to changed
/// contents or pending events.
pub(crate) fn needs_rendering_update(&self) -> bool {
if !self.is_fully_active() {
return false;
}
if !self.window().layout_blocked() &&
(!self.restyle_reason().is_empty() ||
self.window().layout().needs_new_display_list())
{
return true;
}
if self.has_pending_input_events() {
return true;
}
if self.has_pending_scroll_events() {
return true;
}
if self.window().has_unhandled_resize_event() {
return true;
}
false
}
/// An implementation of step 22 from /// An implementation of step 22 from
/// <https://html.spec.whatwg.org/multipage/#update-the-rendering>: /// <https://html.spec.whatwg.org/multipage/#update-the-rendering>:
/// ///
@ -3665,7 +3688,7 @@ impl Document {
// > doc and its node navigable to reflect the current state. // > doc and its node navigable to reflect the current state.
// //
// Returns true if a reflow occured. // Returns true if a reflow occured.
pub(crate) fn update_the_rendering(&self, can_gc: CanGc) -> bool { pub(crate) fn update_the_rendering(&self) -> bool {
self.update_animating_images(); self.update_animating_images();
// All dirty canvases are flushed before updating the rendering. // All dirty canvases are flushed before updating the rendering.
@ -3701,10 +3724,40 @@ impl Document {
} }
self.window() self.window()
.reflow(ReflowGoal::UpdateTheRendering, can_gc) .reflow(ReflowGoal::UpdateTheRendering)
.reflow_issued .reflow_issued
} }
/// From <https://drafts.csswg.org/css-font-loading/#font-face-set-ready>:
///
/// > A FontFaceSet is pending on the environment if any of the following are true:
/// > - the document is still loading
/// > - the document has pending stylesheet requests
/// > - the document has pending layout operations which might cause the user agent to request
/// > a font, or which depend on recently-loaded fonts
///
/// Returns true if the promise was fulfilled.
pub(crate) fn maybe_fulfill_font_ready_promise(&self, can_gc: CanGc) -> bool {
if !self.is_fully_active() {
return false;
}
let fonts = self.Fonts(can_gc);
if !fonts.waiting_to_fullfill_promise() {
return false;
}
if self.window().font_context().web_fonts_still_loading() != 0 {
return false;
}
if self.ReadyState() != DocumentReadyState::Complete {
return false;
}
if !self.restyle_reason().is_empty() {
return false;
}
fonts.fulfill_ready_promise_if_needed(can_gc)
}
pub(crate) fn id_map(&self) -> Ref<HashMapTracedValues<Atom, Vec<Dom<Element>>>> { pub(crate) fn id_map(&self) -> Ref<HashMapTracedValues<Atom, Vec<Dom<Element>>>> {
self.id_map.borrow() self.id_map.borrow()
} }
@ -3720,19 +3773,22 @@ impl Document {
.push(Dom::from_ref(resize_observer)); .push(Dom::from_ref(resize_observer));
} }
/// Whether or not this [`Document`] has any active [`ResizeObserver`].
pub(crate) fn has_resize_observers(&self) -> bool {
!self.resize_observers.borrow().is_empty()
}
/// <https://drafts.csswg.org/resize-observer/#gather-active-observations-h> /// <https://drafts.csswg.org/resize-observer/#gather-active-observations-h>
/// <https://drafts.csswg.org/resize-observer/#has-active-resize-observations> /// <https://drafts.csswg.org/resize-observer/#has-active-resize-observations>
pub(crate) fn gather_active_resize_observations_at_depth( pub(crate) fn gather_active_resize_observations_at_depth(
&self, &self,
depth: &ResizeObservationDepth, depth: &ResizeObservationDepth,
can_gc: CanGc,
) -> bool { ) -> bool {
let mut has_active_resize_observations = false; let mut has_active_resize_observations = false;
for observer in self.resize_observers.borrow_mut().iter_mut() { for observer in self.resize_observers.borrow_mut().iter_mut() {
observer.gather_active_resize_observations_at_depth( observer.gather_active_resize_observations_at_depth(
depth, depth,
&mut has_active_resize_observations, &mut has_active_resize_observations,
can_gc,
); );
} }
has_active_resize_observations has_active_resize_observations
@ -4431,6 +4487,18 @@ impl Document {
mem::take(&mut *self.pending_input_events.borrow_mut()) mem::take(&mut *self.pending_input_events.borrow_mut())
} }
/// Whether or not this [`Document`] has any pending input events to be processed during
/// "update the rendering."
pub(crate) fn has_pending_input_events(&self) -> bool {
!self.pending_input_events.borrow().is_empty()
}
/// Whether or not this [`Document`] has any pending scroll events to be processed during
/// "update the rendering."
fn has_pending_scroll_events(&self) -> bool {
!self.pending_scroll_event_targets.borrow().is_empty()
}
pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) { pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
self.policy_container.borrow_mut().set_csp_list(csp_list); self.policy_container.borrow_mut().set_csp_list(csp_list);
} }
@ -6524,39 +6592,27 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
); );
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
fn ElementFromPoint( fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
&self,
x: Finite<f64>,
y: Finite<f64>,
can_gc: CanGc,
) -> Option<DomRoot<Element>> {
self.document_or_shadow_root.element_from_point( self.document_or_shadow_root.element_from_point(
x, x,
y, y,
self.GetDocumentElement(), self.GetDocumentElement(),
self.has_browsing_context, self.has_browsing_context,
can_gc,
) )
} }
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
fn ElementsFromPoint( fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
&self,
x: Finite<f64>,
y: Finite<f64>,
can_gc: CanGc,
) -> Vec<DomRoot<Element>> {
self.document_or_shadow_root.elements_from_point( self.document_or_shadow_root.elements_from_point(
x, x,
y, y,
self.GetDocumentElement(), self.GetDocumentElement(),
self.has_browsing_context, self.has_browsing_context,
can_gc,
) )
} }
/// <https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement> /// <https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement>
fn GetScrollingElement(&self, can_gc: CanGc) -> Option<DomRoot<Element>> { fn GetScrollingElement(&self) -> Option<DomRoot<Element>> {
// Step 1. If the Document is in quirks mode, follow these steps: // Step 1. If the Document is in quirks mode, follow these steps:
if self.quirks_mode() == QuirksMode::Quirks { if self.quirks_mode() == QuirksMode::Quirks {
// Step 1.1. If the body element exists, // Step 1.1. If the body element exists,
@ -6565,7 +6621,7 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
// and it is not potentially scrollable, return the body element and abort these steps. // and it is not potentially scrollable, return the body element and abort these steps.
// For this purpose, a value of overflow:clip on the the body elements parent element // For this purpose, a value of overflow:clip on the the body elements parent element
// must be treated as overflow:hidden. // must be treated as overflow:hidden.
if !e.is_potentially_scrollable_body_for_scrolling_element(can_gc) { if !e.is_potentially_scrollable_body_for_scrolling_element() {
return Some(DomRoot::from_ref(e)); return Some(DomRoot::from_ref(e));
} }
} }

View file

@ -35,7 +35,6 @@ use crate::dom::shadowroot::ShadowRoot;
use crate::dom::stylesheetlist::StyleSheetListOwner; use crate::dom::stylesheetlist::StyleSheetListOwner;
use crate::dom::types::CSSStyleSheet; use crate::dom::types::CSSStyleSheet;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::script_runtime::CanGc;
use crate::stylesheet_set::StylesheetSetRef; use crate::stylesheet_set::StylesheetSetRef;
/// Stylesheet could be constructed by a CSSOM object CSSStylesheet or parsed /// Stylesheet could be constructed by a CSSOM object CSSStylesheet or parsed
@ -141,10 +140,8 @@ impl DocumentOrShadowRoot {
&self, &self,
client_point: &Point2D<f32>, client_point: &Point2D<f32>,
query_type: NodesFromPointQueryType, query_type: NodesFromPointQueryType,
can_gc: CanGc,
) -> Vec<UntrustedNodeAddress> { ) -> Vec<UntrustedNodeAddress> {
self.window self.window.layout_reflow(QueryMsg::NodesFromPointQuery);
.layout_reflow(QueryMsg::NodesFromPointQuery, can_gc);
self.window self.window
.layout() .layout()
.query_nodes_from_point(*client_point, query_type) .query_nodes_from_point(*client_point, query_type)
@ -158,7 +155,6 @@ impl DocumentOrShadowRoot {
y: Finite<f64>, y: Finite<f64>,
document_element: Option<DomRoot<Element>>, document_element: Option<DomRoot<Element>>,
has_browsing_context: bool, has_browsing_context: bool,
can_gc: CanGc,
) -> Option<DomRoot<Element>> { ) -> Option<DomRoot<Element>> {
let x = *x as f32; let x = *x as f32;
let y = *y as f32; let y = *y as f32;
@ -174,7 +170,7 @@ impl DocumentOrShadowRoot {
} }
match self match self
.nodes_from_point(point, NodesFromPointQueryType::Topmost, can_gc) .nodes_from_point(point, NodesFromPointQueryType::Topmost)
.first() .first()
{ {
Some(address) => { Some(address) => {
@ -206,7 +202,6 @@ impl DocumentOrShadowRoot {
y: Finite<f64>, y: Finite<f64>,
document_element: Option<DomRoot<Element>>, document_element: Option<DomRoot<Element>>,
has_browsing_context: bool, has_browsing_context: bool,
can_gc: CanGc,
) -> Vec<DomRoot<Element>> { ) -> Vec<DomRoot<Element>> {
let x = *x as f32; let x = *x as f32;
let y = *y as f32; let y = *y as f32;
@ -223,7 +218,7 @@ impl DocumentOrShadowRoot {
} }
// Step 1 and Step 3 // Step 1 and Step 3
let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All, can_gc); let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
let mut elements: Vec<DomRoot<Element>> = nodes let mut elements: Vec<DomRoot<Element>> = nodes
.iter() .iter()
.flat_map(|&untrusted_node_address| { .flat_map(|&untrusted_node_address| {

View file

@ -464,34 +464,30 @@ impl Element {
/// style will be `None` for elements in a `display: none` subtree. otherwise, the element has a /// style will be `None` for elements in a `display: none` subtree. otherwise, the element has a
/// layout box iff it doesn't have `display: none`. /// layout box iff it doesn't have `display: none`.
pub(crate) fn style(&self, can_gc: CanGc) -> Option<Arc<ComputedValues>> { pub(crate) fn style(&self) -> Option<Arc<ComputedValues>> {
self.upcast::<Node>().style(can_gc) self.upcast::<Node>().style()
} }
// https://drafts.csswg.org/cssom-view/#css-layout-box // https://drafts.csswg.org/cssom-view/#css-layout-box
pub(crate) fn has_css_layout_box(&self, can_gc: CanGc) -> bool { pub(crate) fn has_css_layout_box(&self) -> bool {
self.style(can_gc) self.style()
.is_some_and(|s| !s.get_box().clone_display().is_none()) .is_some_and(|s| !s.get_box().clone_display().is_none())
} }
/// <https://drafts.csswg.org/cssom-view/#potentially-scrollable> /// <https://drafts.csswg.org/cssom-view/#potentially-scrollable>
pub(crate) fn is_potentially_scrollable_body(&self, can_gc: CanGc) -> bool { pub(crate) fn is_potentially_scrollable_body(&self) -> bool {
self.is_potentially_scrollable_body_shared_logic(false, can_gc) self.is_potentially_scrollable_body_shared_logic(false)
} }
/// <https://drafts.csswg.org/cssom-view/#potentially-scrollable> /// <https://drafts.csswg.org/cssom-view/#potentially-scrollable>
pub(crate) fn is_potentially_scrollable_body_for_scrolling_element( pub(crate) fn is_potentially_scrollable_body_for_scrolling_element(&self) -> bool {
&self, self.is_potentially_scrollable_body_shared_logic(true)
can_gc: CanGc,
) -> bool {
self.is_potentially_scrollable_body_shared_logic(true, can_gc)
} }
/// <https://drafts.csswg.org/cssom-view/#potentially-scrollable> /// <https://drafts.csswg.org/cssom-view/#potentially-scrollable>
fn is_potentially_scrollable_body_shared_logic( fn is_potentially_scrollable_body_shared_logic(
&self, &self,
treat_overflow_clip_on_parent_as_hidden: bool, treat_overflow_clip_on_parent_as_hidden: bool,
can_gc: CanGc,
) -> bool { ) -> bool {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
debug_assert!( debug_assert!(
@ -502,14 +498,14 @@ impl Element {
// "An element body (which will be the body element) is potentially // "An element body (which will be the body element) is potentially
// scrollable if all of the following conditions are true: // scrollable if all of the following conditions are true:
// - body has an associated box." // - body has an associated box."
if !self.has_css_layout_box(can_gc) { if !self.has_css_layout_box() {
return false; return false;
} }
// " - bodys parent elements computed value of the overflow-x or // " - bodys parent elements computed value of the overflow-x or
// overflow-y properties is neither visible nor clip." // overflow-y properties is neither visible nor clip."
if let Some(parent) = node.GetParentElement() { if let Some(parent) = node.GetParentElement() {
if let Some(style) = parent.style(can_gc) { if let Some(style) = parent.style() {
let mut overflow_x = style.get_box().clone_overflow_x(); let mut overflow_x = style.get_box().clone_overflow_x();
let mut overflow_y = style.get_box().clone_overflow_y(); let mut overflow_y = style.get_box().clone_overflow_y();
@ -532,7 +528,7 @@ impl Element {
// " - bodys computed value of the overflow-x or overflow-y properties // " - bodys computed value of the overflow-x or overflow-y properties
// is neither visible nor clip." // is neither visible nor clip."
if let Some(style) = self.style(can_gc) { if let Some(style) = self.style() {
if !style.get_box().clone_overflow_x().is_scrollable() && if !style.get_box().clone_overflow_x().is_scrollable() &&
!style.get_box().clone_overflow_y().is_scrollable() !style.get_box().clone_overflow_y().is_scrollable()
{ {
@ -544,18 +540,17 @@ impl Element {
} }
// https://drafts.csswg.org/cssom-view/#scrolling-box // https://drafts.csswg.org/cssom-view/#scrolling-box
fn has_scrolling_box(&self, can_gc: CanGc) -> bool { fn has_scrolling_box(&self) -> bool {
// TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet) // TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet)
// self.has_scrolling_mechanism() // self.has_scrolling_mechanism()
self.style(can_gc).is_some_and(|style| { self.style().is_some_and(|style| {
style.get_box().clone_overflow_x().is_scrollable() || style.get_box().clone_overflow_x().is_scrollable() ||
style.get_box().clone_overflow_y().is_scrollable() style.get_box().clone_overflow_y().is_scrollable()
}) })
} }
fn has_overflow(&self, can_gc: CanGc) -> bool { fn has_overflow(&self) -> bool {
self.ScrollHeight(can_gc) > self.ClientHeight(can_gc) || self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth()
self.ScrollWidth(can_gc) > self.ClientWidth(can_gc)
} }
pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> { pub(crate) fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
@ -2477,7 +2472,7 @@ impl Element {
// https://drafts.csswg.org/cssom-view/#dom-element-scroll // https://drafts.csswg.org/cssom-view/#dom-element-scroll
// TODO(stevennovaryo): Need to update the scroll API to follow the spec since it is quite outdated. // TODO(stevennovaryo): Need to update the scroll API to follow the spec since it is quite outdated.
pub(crate) fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior, can_gc: CanGc) { pub(crate) fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior) {
// Step 1.2 or 2.3 // Step 1.2 or 2.3
let x = if x_.is_finite() { x_ } else { 0.0f64 }; let x = if x_.is_finite() { x_ } else { 0.0f64 };
let y = if y_.is_finite() { y_ } else { 0.0f64 }; let y = if y_.is_finite() { y_ } else { 0.0f64 };
@ -2501,7 +2496,7 @@ impl Element {
// Step 7 // Step 7
if *self.root_element() == *self { if *self.root_element() == *self {
if doc.quirks_mode() != QuirksMode::Quirks { if doc.quirks_mode() != QuirksMode::Quirks {
win.scroll(x, y, behavior, can_gc); win.scroll(x, y, behavior);
} }
return; return;
@ -2510,22 +2505,19 @@ impl Element {
// Step 9 // Step 9
if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() && if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == QuirksMode::Quirks && doc.quirks_mode() == QuirksMode::Quirks &&
!self.is_potentially_scrollable_body(can_gc) !self.is_potentially_scrollable_body()
{ {
win.scroll(x, y, behavior, can_gc); win.scroll(x, y, behavior);
return; return;
} }
// Step 10 // Step 10
if !self.has_css_layout_box(can_gc) || if !self.has_css_layout_box() || !self.has_scrolling_box() || !self.has_overflow() {
!self.has_scrolling_box(can_gc) ||
!self.has_overflow(can_gc)
{
return; return;
} }
// Step 11 // Step 11
win.scroll_an_element(self, x, y, behavior, can_gc); win.scroll_an_element(self, x, y, behavior);
} }
/// <https://html.spec.whatwg.org/multipage/#fragment-parsing-algorithm-steps> /// <https://html.spec.whatwg.org/multipage/#fragment-parsing-algorithm-steps>
@ -3058,7 +3050,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// https://drafts.csswg.org/cssom-view/#dom-element-getclientrects // https://drafts.csswg.org/cssom-view/#dom-element-getclientrects
fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> { fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> {
let win = self.owner_window(); let win = self.owner_window();
let raw_rects = self.upcast::<Node>().content_boxes(can_gc); let raw_rects = self.upcast::<Node>().content_boxes();
let rects: Vec<DomRoot<DOMRect>> = raw_rects let rects: Vec<DomRoot<DOMRect>> = raw_rects
.iter() .iter()
.map(|rect| { .map(|rect| {
@ -3078,7 +3070,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect // https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect
fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> { fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
let win = self.owner_window(); let win = self.owner_window();
let rect = self.upcast::<Node>().bounding_content_box_or_zero(can_gc); let rect = self.upcast::<Node>().bounding_content_box_or_zero();
DOMRect::new( DOMRect::new(
win.upcast(), win.upcast(),
rect.origin.x.to_f64_px(), rect.origin.x.to_f64_px(),
@ -3090,52 +3082,47 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scroll // https://drafts.csswg.org/cssom-view/#dom-element-scroll
fn Scroll(&self, options: &ScrollToOptions, can_gc: CanGc) { fn Scroll(&self, options: &ScrollToOptions) {
// Step 1 // Step 1
let left = options.left.unwrap_or(self.ScrollLeft(can_gc)); let left = options.left.unwrap_or(self.ScrollLeft());
let top = options.top.unwrap_or(self.ScrollTop(can_gc)); let top = options.top.unwrap_or(self.ScrollTop());
self.scroll(left, top, options.parent.behavior, can_gc); self.scroll(left, top, options.parent.behavior);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scroll // https://drafts.csswg.org/cssom-view/#dom-element-scroll
fn Scroll_(&self, x: f64, y: f64, can_gc: CanGc) { fn Scroll_(&self, x: f64, y: f64) {
self.scroll(x, y, ScrollBehavior::Auto, can_gc); self.scroll(x, y, ScrollBehavior::Auto);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollto // https://drafts.csswg.org/cssom-view/#dom-element-scrollto
fn ScrollTo(&self, options: &ScrollToOptions, can_gc: CanGc) { fn ScrollTo(&self, options: &ScrollToOptions) {
self.Scroll(options, can_gc); self.Scroll(options);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollto // https://drafts.csswg.org/cssom-view/#dom-element-scrollto
fn ScrollTo_(&self, x: f64, y: f64, can_gc: CanGc) { fn ScrollTo_(&self, x: f64, y: f64) {
self.Scroll_(x, y, can_gc); self.Scroll_(x, y);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollby // https://drafts.csswg.org/cssom-view/#dom-element-scrollby
fn ScrollBy(&self, options: &ScrollToOptions, can_gc: CanGc) { fn ScrollBy(&self, options: &ScrollToOptions) {
// Step 2 // Step 2
let delta_left = options.left.unwrap_or(0.0f64); let delta_left = options.left.unwrap_or(0.0f64);
let delta_top = options.top.unwrap_or(0.0f64); let delta_top = options.top.unwrap_or(0.0f64);
let left = self.ScrollLeft(can_gc); let left = self.ScrollLeft();
let top = self.ScrollTop(can_gc); let top = self.ScrollTop();
self.scroll( self.scroll(left + delta_left, top + delta_top, options.parent.behavior);
left + delta_left,
top + delta_top,
options.parent.behavior,
can_gc,
);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollby // https://drafts.csswg.org/cssom-view/#dom-element-scrollby
fn ScrollBy_(&self, x: f64, y: f64, can_gc: CanGc) { fn ScrollBy_(&self, x: f64, y: f64) {
let left = self.ScrollLeft(can_gc); let left = self.ScrollLeft();
let top = self.ScrollTop(can_gc); let top = self.ScrollTop();
self.scroll(left + x, top + y, ScrollBehavior::Auto, can_gc); self.scroll(left + x, top + y, ScrollBehavior::Auto);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
fn ScrollTop(&self, can_gc: CanGc) -> f64 { fn ScrollTop(&self) -> f64 {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
// Step 1 // Step 1
@ -3165,24 +3152,24 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// Step 7 // Step 7
if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() && if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == QuirksMode::Quirks && doc.quirks_mode() == QuirksMode::Quirks &&
!self.is_potentially_scrollable_body(can_gc) !self.is_potentially_scrollable_body()
{ {
return win.ScrollY() as f64; return win.ScrollY() as f64;
} }
// Step 8 // Step 8
if !self.has_css_layout_box(can_gc) { if !self.has_css_layout_box() {
return 0.0; return 0.0;
} }
// Step 9 // Step 9
let point = win.scroll_offset_query(node, can_gc); let point = win.scroll_offset_query(node);
point.y.abs() as f64 point.y.abs() as f64
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
// TODO(stevennovaryo): Need to update the scroll API to follow the spec since it is quite outdated. // TODO(stevennovaryo): Need to update the scroll API to follow the spec since it is quite outdated.
fn SetScrollTop(&self, y_: f64, can_gc: CanGc) { fn SetScrollTop(&self, y_: f64) {
let behavior = ScrollBehavior::Auto; let behavior = ScrollBehavior::Auto;
// Step 1, 2 // Step 1, 2
@ -3207,7 +3194,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// Step 7 // Step 7
if *self.root_element() == *self { if *self.root_element() == *self {
if doc.quirks_mode() != QuirksMode::Quirks { if doc.quirks_mode() != QuirksMode::Quirks {
win.scroll(win.ScrollX() as f64, y, behavior, can_gc); win.scroll(win.ScrollX() as f64, y, behavior);
} }
return; return;
@ -3216,26 +3203,23 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// Step 9 // Step 9
if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() && if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == QuirksMode::Quirks && doc.quirks_mode() == QuirksMode::Quirks &&
!self.is_potentially_scrollable_body(can_gc) !self.is_potentially_scrollable_body()
{ {
win.scroll(win.ScrollX() as f64, y, behavior, can_gc); win.scroll(win.ScrollX() as f64, y, behavior);
return; return;
} }
// Step 10 // Step 10
if !self.has_css_layout_box(can_gc) || if !self.has_css_layout_box() || !self.has_scrolling_box() || !self.has_overflow() {
!self.has_scrolling_box(can_gc) ||
!self.has_overflow(can_gc)
{
return; return;
} }
// Step 11 // Step 11
win.scroll_an_element(self, self.ScrollLeft(can_gc), y, behavior, can_gc); win.scroll_an_element(self, self.ScrollLeft(), y, behavior);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
fn ScrollLeft(&self, can_gc: CanGc) -> f64 { fn ScrollLeft(&self) -> f64 {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
// Step 1 // Step 1
@ -3265,23 +3249,23 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// Step 7 // Step 7
if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() && if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == QuirksMode::Quirks && doc.quirks_mode() == QuirksMode::Quirks &&
!self.is_potentially_scrollable_body(can_gc) !self.is_potentially_scrollable_body()
{ {
return win.ScrollX() as f64; return win.ScrollX() as f64;
} }
// Step 8 // Step 8
if !self.has_css_layout_box(can_gc) { if !self.has_css_layout_box() {
return 0.0; return 0.0;
} }
// Step 9 // Step 9
let point = win.scroll_offset_query(node, can_gc); let point = win.scroll_offset_query(node);
point.x.abs() as f64 point.x.abs() as f64
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollleft // https://drafts.csswg.org/cssom-view/#dom-element-scrollleft
fn SetScrollLeft(&self, x_: f64, can_gc: CanGc) { fn SetScrollLeft(&self, x_: f64) {
let behavior = ScrollBehavior::Auto; let behavior = ScrollBehavior::Auto;
// Step 1, 2 // Step 1, 2
@ -3309,59 +3293,56 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
return; return;
} }
win.scroll(x, win.ScrollY() as f64, behavior, can_gc); win.scroll(x, win.ScrollY() as f64, behavior);
return; return;
} }
// Step 9 // Step 9
if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() && if doc.GetBody().as_deref() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == QuirksMode::Quirks && doc.quirks_mode() == QuirksMode::Quirks &&
!self.is_potentially_scrollable_body(can_gc) !self.is_potentially_scrollable_body()
{ {
win.scroll(x, win.ScrollY() as f64, behavior, can_gc); win.scroll(x, win.ScrollY() as f64, behavior);
return; return;
} }
// Step 10 // Step 10
if !self.has_css_layout_box(can_gc) || if !self.has_css_layout_box() || !self.has_scrolling_box() || !self.has_overflow() {
!self.has_scrolling_box(can_gc) ||
!self.has_overflow(can_gc)
{
return; return;
} }
// Step 11 // Step 11
win.scroll_an_element(self, x, self.ScrollTop(can_gc), behavior, can_gc); win.scroll_an_element(self, x, self.ScrollTop(), behavior);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth // https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth
fn ScrollWidth(&self, can_gc: CanGc) -> i32 { fn ScrollWidth(&self) -> i32 {
self.upcast::<Node>().scroll_area(can_gc).size.width self.upcast::<Node>().scroll_area().size.width
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollheight // https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
fn ScrollHeight(&self, can_gc: CanGc) -> i32 { fn ScrollHeight(&self) -> i32 {
self.upcast::<Node>().scroll_area(can_gc).size.height self.upcast::<Node>().scroll_area().size.height
} }
// https://drafts.csswg.org/cssom-view/#dom-element-clienttop // https://drafts.csswg.org/cssom-view/#dom-element-clienttop
fn ClientTop(&self, can_gc: CanGc) -> i32 { fn ClientTop(&self) -> i32 {
self.client_rect(can_gc).origin.y self.client_rect().origin.y
} }
// https://drafts.csswg.org/cssom-view/#dom-element-clientleft // https://drafts.csswg.org/cssom-view/#dom-element-clientleft
fn ClientLeft(&self, can_gc: CanGc) -> i32 { fn ClientLeft(&self) -> i32 {
self.client_rect(can_gc).origin.x self.client_rect().origin.x
} }
// https://drafts.csswg.org/cssom-view/#dom-element-clientwidth // https://drafts.csswg.org/cssom-view/#dom-element-clientwidth
fn ClientWidth(&self, can_gc: CanGc) -> i32 { fn ClientWidth(&self) -> i32 {
self.client_rect(can_gc).size.width self.client_rect().size.width
} }
// https://drafts.csswg.org/cssom-view/#dom-element-clientheight // https://drafts.csswg.org/cssom-view/#dom-element-clientheight
fn ClientHeight(&self, can_gc: CanGc) -> i32 { fn ClientHeight(&self) -> i32 {
self.client_rect(can_gc).size.height self.client_rect().size.height
} }
/// <https://html.spec.whatwg.org/multipage/#dom-element-sethtmlunsafe> /// <https://html.spec.whatwg.org/multipage/#dom-element-sethtmlunsafe>
@ -4777,7 +4758,7 @@ impl SelectorsElement for SelectorWrapper<'_> {
} }
impl Element { impl Element {
fn client_rect(&self, can_gc: CanGc) -> Rect<i32> { fn client_rect(&self) -> Rect<i32> {
let doc = self.node.owner_doc(); let doc = self.node.owner_doc();
if let Some(rect) = self if let Some(rect) = self
@ -4791,7 +4772,7 @@ impl Element {
} }
} }
let mut rect = self.upcast::<Node>().client_rect(can_gc); let mut rect = self.upcast::<Node>().client_rect();
let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks; let in_quirks_mode = doc.quirks_mode() == QuirksMode::Quirks;
if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) || if (in_quirks_mode && doc.GetBody().as_deref() == self.downcast::<HTMLElement>()) ||

View file

@ -69,10 +69,17 @@ impl FontFaceSet {
} }
} }
pub(crate) fn fulfill_ready_promise_if_needed(&self, can_gc: CanGc) { /// Fulfill the font ready promise, returning true if it was not already fulfilled beforehand.
if !self.promise.is_fulfilled() { pub(crate) fn fulfill_ready_promise_if_needed(&self, can_gc: CanGc) -> bool {
self.promise.resolve_native(self, can_gc); if self.promise.is_fulfilled() {
return false;
} }
self.promise.resolve_native(self, can_gc);
true
}
pub(crate) fn waiting_to_fullfill_promise(&self) -> bool {
!self.promise.is_fulfilled()
} }
} }

View file

@ -101,7 +101,7 @@ impl History {
// Step 8 // Step 8
if let Some(fragment) = url.fragment() { if let Some(fragment) = url.fragment() {
document.check_and_scroll_fragment(fragment, can_gc); document.check_and_scroll_fragment(fragment);
} }
// Step 11 // Step 11

View file

@ -319,7 +319,7 @@ impl Activatable for HTMLAnchorElement {
} }
//https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour //https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour
fn activation_behavior(&self, event: &Event, target: &EventTarget, can_gc: CanGc) { fn activation_behavior(&self, event: &Event, target: &EventTarget, _: CanGc) {
let element = self.as_element(); let element = self.as_element();
let mouse_event = event.downcast::<MouseEvent>().unwrap(); let mouse_event = event.downcast::<MouseEvent>().unwrap();
let mut ismap_suffix = None; let mut ismap_suffix = None;
@ -329,7 +329,7 @@ impl Activatable for HTMLAnchorElement {
if let Some(element) = target.downcast::<Element>() { if let Some(element) = target.downcast::<Element>() {
if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) { if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) {
let target_node = element.upcast::<Node>(); let target_node = element.upcast::<Node>();
let rect = target_node.bounding_content_box_or_zero(can_gc); let rect = target_node.bounding_content_box_or_zero();
ismap_suffix = Some(format!( ismap_suffix = Some(format!(
"?{},{}", "?{},{}",
mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(), mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),

View file

@ -116,18 +116,18 @@ impl HTMLElement {
/// `.outerText` in JavaScript.` /// `.outerText` in JavaScript.`
/// ///
/// <https://html.spec.whatwg.org/multipage/#get-the-text-steps> /// <https://html.spec.whatwg.org/multipage/#get-the-text-steps>
pub(crate) fn get_inner_outer_text(&self, can_gc: CanGc) -> DOMString { pub(crate) fn get_inner_outer_text(&self) -> DOMString {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
let window = node.owner_window(); let window = node.owner_window();
let element = self.as_element(); let element = self.as_element();
// Step 1. // Step 1.
let element_not_rendered = !node.is_connected() || !element.has_css_layout_box(can_gc); let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
if element_not_rendered { if element_not_rendered {
return node.GetTextContent().unwrap(); return node.GetTextContent().unwrap();
} }
window.layout_reflow(QueryMsg::ElementInnerOuterTextQuery, can_gc); window.layout_reflow(QueryMsg::ElementInnerOuterTextQuery);
let text = window let text = window
.layout() .layout()
.query_element_inner_outer_text(node.to_trusted_node_address()); .query_element_inner_outer_text(node.to_trusted_node_address());
@ -438,65 +438,65 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
} }
/// <https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent> /// <https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent>
fn GetOffsetParent(&self, can_gc: CanGc) -> Option<DomRoot<Element>> { fn GetOffsetParent(&self) -> Option<DomRoot<Element>> {
if self.is_body_element() || self.is::<HTMLHtmlElement>() { if self.is_body_element() || self.is::<HTMLHtmlElement>() {
return None; return None;
} }
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
let window = self.owner_window(); let window = self.owner_window();
let (element, _) = window.offset_parent_query(node, can_gc); let (element, _) = window.offset_parent_query(node);
element element
} }
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsettop // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsettop
fn OffsetTop(&self, can_gc: CanGc) -> i32 { fn OffsetTop(&self) -> i32 {
if self.is_body_element() { if self.is_body_element() {
return 0; return 0;
} }
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
let window = self.owner_window(); let window = self.owner_window();
let (_, rect) = window.offset_parent_query(node, can_gc); let (_, rect) = window.offset_parent_query(node);
rect.origin.y.to_nearest_px() rect.origin.y.to_nearest_px()
} }
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetleft // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetleft
fn OffsetLeft(&self, can_gc: CanGc) -> i32 { fn OffsetLeft(&self) -> i32 {
if self.is_body_element() { if self.is_body_element() {
return 0; return 0;
} }
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
let window = self.owner_window(); let window = self.owner_window();
let (_, rect) = window.offset_parent_query(node, can_gc); let (_, rect) = window.offset_parent_query(node);
rect.origin.x.to_nearest_px() rect.origin.x.to_nearest_px()
} }
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetwidth // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetwidth
fn OffsetWidth(&self, can_gc: CanGc) -> i32 { fn OffsetWidth(&self) -> i32 {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
let window = self.owner_window(); let window = self.owner_window();
let (_, rect) = window.offset_parent_query(node, can_gc); let (_, rect) = window.offset_parent_query(node);
rect.size.width.to_nearest_px() rect.size.width.to_nearest_px()
} }
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetheight // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetheight
fn OffsetHeight(&self, can_gc: CanGc) -> i32 { fn OffsetHeight(&self) -> i32 {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
let window = self.owner_window(); let window = self.owner_window();
let (_, rect) = window.offset_parent_query(node, can_gc); let (_, rect) = window.offset_parent_query(node);
rect.size.height.to_nearest_px() rect.size.height.to_nearest_px()
} }
/// <https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute> /// <https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute>
fn InnerText(&self, can_gc: CanGc) -> DOMString { fn InnerText(&self) -> DOMString {
self.get_inner_outer_text(can_gc) self.get_inner_outer_text()
} }
/// <https://html.spec.whatwg.org/multipage/#set-the-inner-text-steps> /// <https://html.spec.whatwg.org/multipage/#set-the-inner-text-steps>
@ -505,8 +505,8 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
} }
/// <https://html.spec.whatwg.org/multipage/#dom-outertext> /// <https://html.spec.whatwg.org/multipage/#dom-outertext>
fn GetOuterText(&self, can_gc: CanGc) -> Fallible<DOMString> { fn GetOuterText(&self) -> Fallible<DOMString> {
Ok(self.get_inner_outer_text(can_gc)) Ok(self.get_inner_outer_text())
} }
/// <https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute:dom-outertext-2> /// <https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute:dom-outertext-2>

View file

@ -210,7 +210,7 @@ impl HTMLIFrameElement {
}; };
let viewport_details = window let viewport_details = window
.get_iframe_viewport_details_if_known(browsing_context_id, can_gc) .get_iframe_viewport_details_if_known(browsing_context_id)
.unwrap_or_else(|| ViewportDetails { .unwrap_or_else(|| ViewportDetails {
hidpi_scale_factor: window.device_pixel_ratio(), hidpi_scale_factor: window.device_pixel_ratio(),
..Default::default() ..Default::default()

View file

@ -1655,9 +1655,9 @@ impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
make_bool_setter!(SetIsMap, "ismap"); make_bool_setter!(SetIsMap, "ismap");
// https://html.spec.whatwg.org/multipage/#dom-img-width // https://html.spec.whatwg.org/multipage/#dom-img-width
fn Width(&self, can_gc: CanGc) -> u32 { fn Width(&self) -> u32 {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
match node.bounding_content_box(can_gc) { match node.bounding_content_box() {
Some(rect) => rect.size.width.to_px() as u32, Some(rect) => rect.size.width.to_px() as u32,
None => self.NaturalWidth(), None => self.NaturalWidth(),
} }
@ -1669,9 +1669,9 @@ impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
} }
// https://html.spec.whatwg.org/multipage/#dom-img-height // https://html.spec.whatwg.org/multipage/#dom-img-height
fn Height(&self, can_gc: CanGc) -> u32 { fn Height(&self) -> u32 {
let node = self.upcast::<Node>(); let node = self.upcast::<Node>();
match node.bounding_content_box(can_gc) { match node.bounding_content_box() {
Some(rect) => rect.size.height.to_px() as u32, Some(rect) => rect.size.height.to_px() as u32,
None => self.NaturalHeight(), None => self.NaturalHeight(),
} }

View file

@ -2736,7 +2736,7 @@ impl HTMLInputElement {
let (ipc_sender, ipc_receiver) = let (ipc_sender, ipc_receiver) =
ipc::channel::<Option<RgbColor>>().expect("Failed to create IPC channel!"); ipc::channel::<Option<RgbColor>>().expect("Failed to create IPC channel!");
let document = self.owner_document(); let document = self.owner_document();
let rect = self.upcast::<Node>().bounding_content_box_or_zero(can_gc); let rect = self.upcast::<Node>().bounding_content_box_or_zero();
let rect = Rect::new( let rect = Rect::new(
Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()), Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()),
Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()), Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()),
@ -3074,11 +3074,8 @@ impl VirtualMethods for HTMLInputElement {
// now. // now.
if let Some(point_in_target) = mouse_event.point_in_target() { if let Some(point_in_target) = mouse_event.point_in_target() {
let window = self.owner_window(); let window = self.owner_window();
let index = window.text_index_query( let index = window
self.upcast::<Node>(), .text_index_query(self.upcast::<Node>(), point_in_target.to_untyped());
point_in_target.to_untyped(),
can_gc,
);
// Position the caret at the click position or at the end of the current // Position the caret at the click position or at the end of the current
// value. // value.
let edit_point_index = match index { let edit_point_index = match index {

View file

@ -1551,9 +1551,9 @@ impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement {
make_setter!(SetReferrerPolicy, "referrerpolicy"); make_setter!(SetReferrerPolicy, "referrerpolicy");
/// <https://w3c.github.io/trusted-types/dist/spec/#dom-htmlscriptelement-innertext> /// <https://w3c.github.io/trusted-types/dist/spec/#dom-htmlscriptelement-innertext>
fn InnerText(&self, can_gc: CanGc) -> TrustedScriptOrString { fn InnerText(&self) -> TrustedScriptOrString {
// Step 1: Return the result of running get the text steps with this. // Step 1: Return the result of running get the text steps with this.
TrustedScriptOrString::String(self.upcast::<HTMLElement>().get_inner_outer_text(can_gc)) TrustedScriptOrString::String(self.upcast::<HTMLElement>().get_inner_outer_text())
} }
/// <https://w3c.github.io/trusted-types/dist/spec/#the-innerText-idl-attribute> /// <https://w3c.github.io/trusted-types/dist/spec/#the-innerText-idl-attribute>

View file

@ -339,7 +339,7 @@ impl HTMLSelectElement {
.or_else(|| self.list_of_options().next()) .or_else(|| self.list_of_options().next())
} }
pub(crate) fn show_menu(&self, can_gc: CanGc) -> Option<usize> { pub(crate) fn show_menu(&self) -> Option<usize> {
let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!"); let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!");
// Collect list of optgroups and options // Collect list of optgroups and options
@ -377,7 +377,7 @@ impl HTMLSelectElement {
}) })
.collect(); .collect();
let rect = self.upcast::<Node>().bounding_content_box_or_zero(can_gc); let rect = self.upcast::<Node>().bounding_content_box_or_zero();
let rect = Rect::new( let rect = Rect::new(
Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()), Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()),
Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()), Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()),
@ -782,7 +782,7 @@ impl Activatable for HTMLSelectElement {
} }
fn activation_behavior(&self, _event: &Event, _target: &EventTarget, can_gc: CanGc) { fn activation_behavior(&self, _event: &Event, _target: &EventTarget, can_gc: CanGc) {
let Some(selected_value) = self.show_menu(can_gc) else { let Some(selected_value) = self.show_menu() else {
// The user did not select a value // The user did not select a value
return; return;
}; };

View file

@ -278,7 +278,7 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
) -> Fallible<DomRoot<MouseEvent>> { ) -> Fallible<DomRoot<MouseEvent>> {
let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles); let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable); let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable);
let scroll_offset = window.scroll_offset(can_gc); let scroll_offset = window.scroll_offset();
let page_point = Point2D::new( let page_point = Point2D::new(
scroll_offset.x as i32 + init.clientX, scroll_offset.x as i32 + init.clientX,
scroll_offset.y as i32 + init.clientY, scroll_offset.y as i32 + init.clientY,
@ -370,7 +370,7 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx>
fn OffsetX(&self, can_gc: CanGc) -> i32 { fn OffsetX(&self) -> i32 {
// > The offsetX attribute must follow these steps: // > The offsetX attribute must follow these steps:
// > 1. If the events dispatch flag is set, return the x-coordinate of the position // > 1. If the events dispatch flag is set, return the x-coordinate of the position
// > where the event occurred relative to the origin of the padding edge of the // > where the event occurred relative to the origin of the padding edge of the
@ -384,7 +384,7 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
let Some(node) = target.downcast::<Node>() else { let Some(node) = target.downcast::<Node>() else {
return 0; return 0;
}; };
return self.ClientX() - node.client_rect(can_gc).origin.x; return self.ClientX() - node.client_rect().origin.x;
} }
// > 2. Return the value of the events pageX attribute. // > 2. Return the value of the events pageX attribute.
@ -392,7 +392,7 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
} }
/// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsety> /// <https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsety>
fn OffsetY(&self, can_gc: CanGc) -> i32 { fn OffsetY(&self) -> i32 {
// > The offsetY attribute must follow these steps: // > The offsetY attribute must follow these steps:
// > 1. If the events dispatch flag is set, return the y-coordinate of the // > 1. If the events dispatch flag is set, return the y-coordinate of the
// > position where the event occurred relative to the origin of the padding edge of // > position where the event occurred relative to the origin of the padding edge of
@ -406,7 +406,7 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
let Some(node) = target.downcast::<Node>() else { let Some(node) = target.downcast::<Node>() else {
return 0; return 0;
}; };
return self.ClientY() - node.client_rect(can_gc).origin.y; return self.ClientY() - node.client_rect().origin.y;
} }
// 2. Return the value of the events pageY attribute. // 2. Return the value of the events pageY attribute.
@ -479,7 +479,6 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
meta_key_arg: bool, meta_key_arg: bool,
button_arg: i16, button_arg: i16,
related_target_arg: Option<&EventTarget>, related_target_arg: Option<&EventTarget>,
can_gc: CanGc,
) { ) {
if self.upcast::<Event>().dispatching() { if self.upcast::<Event>().dispatching() {
return; return;
@ -498,7 +497,7 @@ impl MouseEventMethods<crate::DomTypeHolder> for MouseEvent {
.set(Point2D::new(client_x_arg, client_y_arg)); .set(Point2D::new(client_x_arg, client_y_arg));
let global = self.global(); let global = self.global();
let scroll_offset = global.as_window().scroll_offset(can_gc); let scroll_offset = global.as_window().scroll_offset();
self.page_point.set(Point2D::new( self.page_point.set(Point2D::new(
scroll_offset.x as i32 + client_x_arg, scroll_offset.x as i32 + client_x_arg,
scroll_offset.y as i32 + client_y_arg, scroll_offset.y as i32 + client_y_arg,

View file

@ -944,29 +944,29 @@ impl Node {
/// Returns the rendered bounding content box if the element is rendered, /// Returns the rendered bounding content box if the element is rendered,
/// and none otherwise. /// and none otherwise.
pub(crate) fn bounding_content_box(&self, can_gc: CanGc) -> Option<Rect<Au>> { pub(crate) fn bounding_content_box(&self) -> Option<Rect<Au>> {
self.owner_window().content_box_query(self, can_gc) self.owner_window().content_box_query(self)
} }
pub(crate) fn bounding_content_box_or_zero(&self, can_gc: CanGc) -> Rect<Au> { pub(crate) fn bounding_content_box_or_zero(&self) -> Rect<Au> {
self.bounding_content_box(can_gc).unwrap_or_else(Rect::zero) self.bounding_content_box().unwrap_or_else(Rect::zero)
} }
pub(crate) fn bounding_content_box_no_reflow(&self) -> Option<Rect<Au>> { pub(crate) fn bounding_content_box_no_reflow(&self) -> Option<Rect<Au>> {
self.owner_window().content_box_query_unchecked(self) self.owner_window().content_box_query_unchecked(self)
} }
pub(crate) fn content_boxes(&self, can_gc: CanGc) -> Vec<Rect<Au>> { pub(crate) fn content_boxes(&self) -> Vec<Rect<Au>> {
self.owner_window().content_boxes_query(self, can_gc) self.owner_window().content_boxes_query(self)
} }
pub(crate) fn client_rect(&self, can_gc: CanGc) -> Rect<i32> { pub(crate) fn client_rect(&self) -> Rect<i32> {
self.owner_window().client_rect_query(self, can_gc) self.owner_window().client_rect_query(self)
} }
/// <https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth> /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth>
/// <https://drafts.csswg.org/cssom-view/#dom-element-scrollheight> /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollheight>
pub(crate) fn scroll_area(&self, can_gc: CanGc) -> Rect<i32> { pub(crate) fn scroll_area(&self) -> Rect<i32> {
// "1. Let document be the elements node document."" // "1. Let document be the elements node document.""
let document = self.owner_doc(); let document = self.owner_doc();
@ -992,7 +992,7 @@ impl Node {
// element is not potentially scrollable, return max(viewport scrolling area // element is not potentially scrollable, return max(viewport scrolling area
// width, viewport width)." // width, viewport width)."
if (is_root && !in_quirks_mode) || (is_body_element && in_quirks_mode) { if (is_root && !in_quirks_mode) || (is_body_element && in_quirks_mode) {
let viewport_scrolling_area = window.scrolling_area_query(None, can_gc); let viewport_scrolling_area = window.scrolling_area_query(None);
return Rect::new( return Rect::new(
viewport_scrolling_area.origin, viewport_scrolling_area.origin,
viewport_scrolling_area.size.max(viewport), viewport_scrolling_area.size.max(viewport),
@ -1002,7 +1002,7 @@ impl Node {
// "6. If the element does not have any associated box return zero and terminate // "6. If the element does not have any associated box return zero and terminate
// these steps." // these steps."
// "7. Return the width of the elements scrolling area." // "7. Return the width of the elements scrolling area."
window.scrolling_area_query(Some(self), can_gc) window.scrolling_area_query(Some(self))
} }
/// <https://dom.spec.whatwg.org/#dom-childnode-before> /// <https://dom.spec.whatwg.org/#dom-childnode-before>
@ -1461,9 +1461,8 @@ impl Node {
}) })
} }
pub(crate) fn style(&self, can_gc: CanGc) -> Option<Arc<ComputedValues>> { pub(crate) fn style(&self) -> Option<Arc<ComputedValues>> {
self.owner_window() self.owner_window().layout_reflow(QueryMsg::StyleQuery);
.layout_reflow(QueryMsg::StyleQuery, can_gc);
self.style_data self.style_data
.borrow() .borrow()
.as_ref() .as_ref()

View file

@ -161,8 +161,8 @@ impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder>
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
fn SetShadowColor(&self, value: DOMString, can_gc: CanGc) { fn SetShadowColor(&self, value: DOMString) {
self.context.SetShadowColor(value, can_gc) self.context.SetShadowColor(value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
@ -171,8 +171,8 @@ impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder>
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern, can_gc: CanGc) { fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
self.context.SetStrokeStyle(value, can_gc) self.context.SetStrokeStyle(value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
@ -181,8 +181,8 @@ impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder>
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern, can_gc: CanGc) { fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
self.context.SetFillStyle(value, can_gc) self.context.SetFillStyle(value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient
@ -269,8 +269,8 @@ impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder>
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext
fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>, can_gc: CanGc) { fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
self.context.FillText(text, x, y, max_width, can_gc) self.context.FillText(text, x, y, max_width)
} }
// https://html.spec.whatwg.org/multipage/#textmetrics // https://html.spec.whatwg.org/multipage/#textmetrics
@ -284,8 +284,8 @@ impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder>
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-font // https://html.spec.whatwg.org/multipage/#dom-context-2d-font
fn SetFont(&self, value: DOMString, can_gc: CanGc) { fn SetFont(&self, value: DOMString) {
self.context.SetFont(value, can_gc) self.context.SetFont(value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign

View file

@ -343,8 +343,8 @@ impl PaintRenderingContext2DMethods<crate::DomTypeHolder> for PaintRenderingCont
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern, can_gc: CanGc) { fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
self.canvas_state.set_stroke_style(None, value, can_gc) self.canvas_state.set_stroke_style(None, value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
@ -353,8 +353,8 @@ impl PaintRenderingContext2DMethods<crate::DomTypeHolder> for PaintRenderingCont
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern, can_gc: CanGc) { fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
self.canvas_state.set_fill_style(None, value, can_gc) self.canvas_state.set_fill_style(None, value)
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient
@ -497,7 +497,7 @@ impl PaintRenderingContext2DMethods<crate::DomTypeHolder> for PaintRenderingCont
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
fn SetShadowColor(&self, value: DOMString, can_gc: CanGc) { fn SetShadowColor(&self, value: DOMString) {
self.canvas_state.set_shadow_color(None, value, can_gc) self.canvas_state.set_shadow_color(None, value)
} }
} }

View file

@ -233,7 +233,7 @@ impl PointerEventMethods<crate::DomTypeHolder> for PointerEvent {
) -> DomRoot<PointerEvent> { ) -> DomRoot<PointerEvent> {
let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles); let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable); let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable);
let scroll_offset = window.scroll_offset(can_gc); let scroll_offset = window.scroll_offset();
let page_point = Point2D::new( let page_point = Point2D::new(
scroll_offset.x as i32 + init.parent.clientX, scroll_offset.x as i32 + init.parent.clientX,
scroll_offset.y as i32 + init.parent.clientY, scroll_offset.y as i32 + init.parent.clientY,

View file

@ -329,7 +329,6 @@ impl Range {
fn client_rects( fn client_rects(
&self, &self,
can_gc: CanGc,
) -> impl Iterator<Item = euclid::Rect<app_units::Au, euclid::UnknownUnit>> { ) -> impl Iterator<Item = euclid::Rect<app_units::Au, euclid::UnknownUnit>> {
// FIXME: For text nodes that are only partially selected, this should return the client // FIXME: For text nodes that are only partially selected, this should return the client
// rect of the selected part, not the whole text node. // rect of the selected part, not the whole text node.
@ -341,7 +340,7 @@ impl Range {
.following_nodes(document.upcast::<Node>()) .following_nodes(document.upcast::<Node>())
.take_while(move |node| node != &end) .take_while(move |node| node != &end)
.chain(iter::once(end_clone)) .chain(iter::once(end_clone))
.flat_map(move |node| node.content_boxes(can_gc)) .flat_map(move |node| node.content_boxes())
} }
/// <https://dom.spec.whatwg.org/#concept-range-bp-set> /// <https://dom.spec.whatwg.org/#concept-range-bp-set>
@ -1153,7 +1152,7 @@ impl RangeMethods<crate::DomTypeHolder> for Range {
let window = start.owner_window(); let window = start.owner_window();
let client_rects = self let client_rects = self
.client_rects(can_gc) .client_rects()
.map(|rect| { .map(|rect| {
DOMRect::new( DOMRect::new(
window.upcast(), window.upcast(),
@ -1174,7 +1173,7 @@ impl RangeMethods<crate::DomTypeHolder> for Range {
let window = self.start_container().owner_window(); let window = self.start_container().owner_window();
// Step 1. Let list be the result of invoking getClientRects() on the same range this method was invoked on. // Step 1. Let list be the result of invoking getClientRects() on the same range this method was invoked on.
let list = self.client_rects(can_gc); let list = self.client_rects();
// Step 2. If list is empty return a DOMRect object whose x, y, width and height members are zero. // Step 2. If list is empty return a DOMRect object whose x, y, width and height members are zero.
// Step 3. If all rectangles in list have zero width or height, return the first rectangle in list. // Step 3. If all rectangles in list have zero width or height, return the first rectangle in list.

View file

@ -81,7 +81,6 @@ impl ResizeObserver {
&self, &self,
depth: &ResizeObservationDepth, depth: &ResizeObservationDepth,
has_active: &mut bool, has_active: &mut bool,
can_gc: CanGc,
) { ) {
// Step 2.1 Clear observers [[activeTargets]], and [[skippedTargets]]. // Step 2.1 Clear observers [[activeTargets]], and [[skippedTargets]].
// NOTE: This happens as part of Step 2.2 // NOTE: This happens as part of Step 2.2
@ -91,7 +90,7 @@ impl ResizeObserver {
observation.state = Default::default(); observation.state = Default::default();
// Step 2.2.1 If observation.isActive() is true // Step 2.2.1 If observation.isActive() is true
if let Some(size) = observation.is_active(target, can_gc) { if let Some(size) = observation.is_active(target) {
// Step 2.2.1.1 Let targetDepth be result of calculate depth for node for observation.target. // Step 2.2.1.1 Let targetDepth be result of calculate depth for node for observation.target.
let target_depth = calculate_depth_for_node(target); let target_depth = calculate_depth_for_node(target);
@ -284,9 +283,9 @@ impl ResizeObservation {
/// <https://drafts.csswg.org/resize-observer/#dom-resizeobservation-isactive> /// <https://drafts.csswg.org/resize-observer/#dom-resizeobservation-isactive>
/// Returning an optional calculated size, instead of a boolean, /// Returning an optional calculated size, instead of a boolean,
/// to avoid recalculating the size in the subsequent broadcast. /// to avoid recalculating the size in the subsequent broadcast.
fn is_active(&self, target: &Element, can_gc: CanGc) -> Option<Rect<Au>> { fn is_active(&self, target: &Element) -> Option<Rect<Au>> {
let last_reported_size = self.last_reported_sizes[0]; let last_reported_size = self.last_reported_sizes[0];
let box_size = calculate_box_size(target, &self.observed_box, can_gc); let box_size = calculate_box_size(target, &self.observed_box);
let is_active = box_size.width().to_f64_px() != last_reported_size.inline_size() || let is_active = box_size.width().to_f64_px() != last_reported_size.inline_size() ||
box_size.height().to_f64_px() != last_reported_size.block_size(); box_size.height().to_f64_px() != last_reported_size.block_size();
if is_active { Some(box_size) } else { None } if is_active { Some(box_size) } else { None }
@ -301,18 +300,14 @@ fn calculate_depth_for_node(target: &Element) -> ResizeObservationDepth {
} }
/// <https://drafts.csswg.org/resize-observer/#calculate-box-size> /// <https://drafts.csswg.org/resize-observer/#calculate-box-size>
fn calculate_box_size( fn calculate_box_size(target: &Element, observed_box: &ResizeObserverBoxOptions) -> Rect<Au> {
target: &Element,
observed_box: &ResizeObserverBoxOptions,
can_gc: CanGc,
) -> Rect<Au> {
match observed_box { match observed_box {
ResizeObserverBoxOptions::Content_box => { ResizeObserverBoxOptions::Content_box => {
// Note: only taking first fragment, // Note: only taking first fragment,
// but the spec will expand to cover all fragments. // but the spec will expand to cover all fragments.
target target
.upcast::<Node>() .upcast::<Node>()
.content_boxes(can_gc) .content_boxes()
.pop() .pop()
.unwrap_or_else(Rect::zero) .unwrap_or_else(Rect::zero)
}, },

View file

@ -659,9 +659,7 @@ impl ServoParser {
assert!(!self.suspended.get()); assert!(!self.suspended.get());
assert!(!self.aborted.get()); assert!(!self.aborted.get());
self.document self.document.window().reflow_if_reflow_timer_expired();
.window()
.reflow_if_reflow_timer_expired(can_gc);
let script = match feed(&self.tokenizer) { let script = match feed(&self.tokenizer) {
TokenizerResult::Done => return, TokenizerResult::Done => return,
TokenizerResult::Script(script) => script, TokenizerResult::Script(script) => script,

View file

@ -370,12 +370,7 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
} }
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
fn ElementFromPoint( fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
&self,
x: Finite<f64>,
y: Finite<f64>,
can_gc: CanGc,
) -> Option<DomRoot<Element>> {
// Return the result of running the retargeting algorithm with context object // Return the result of running the retargeting algorithm with context object
// and the original result as input. // and the original result as input.
match self.document_or_shadow_root.element_from_point( match self.document_or_shadow_root.element_from_point(
@ -383,7 +378,6 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
y, y,
None, None,
self.document.has_browsing_context(), self.document.has_browsing_context(),
can_gc,
) { ) {
Some(e) => { Some(e) => {
let retargeted_node = self let retargeted_node = self
@ -396,18 +390,13 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
} }
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
fn ElementsFromPoint( fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
&self,
x: Finite<f64>,
y: Finite<f64>,
can_gc: CanGc,
) -> Vec<DomRoot<Element>> {
// Return the result of running the retargeting algorithm with context object // Return the result of running the retargeting algorithm with context object
// and the original result as input // and the original result as input
let mut elements = Vec::new(); let mut elements = Vec::new();
for e in self for e in self
.document_or_shadow_root .document_or_shadow_root
.elements_from_point(x, y, None, self.document.has_browsing_context(), can_gc) .elements_from_point(x, y, None, self.document.has_browsing_context())
.iter() .iter()
{ {
let retargeted_node = self let retargeted_node = self

View file

@ -204,7 +204,7 @@ impl WheelEventMethods<crate::DomTypeHolder> for WheelEvent {
) -> Fallible<DomRoot<WheelEvent>> { ) -> Fallible<DomRoot<WheelEvent>> {
let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles); let bubbles = EventBubbles::from(init.parent.parent.parent.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable); let cancelable = EventCancelable::from(init.parent.parent.parent.parent.cancelable);
let scroll_offset = window.scroll_offset(can_gc); let scroll_offset = window.scroll_offset();
let page_point = Point2D::<i32, CSSPixel>::new( let page_point = Point2D::<i32, CSSPixel>::new(
scroll_offset.x as i32 + init.parent.clientX, scroll_offset.x as i32 + init.parent.clientX,
@ -269,7 +269,6 @@ impl WheelEventMethods<crate::DomTypeHolder> for WheelEvent {
delta_y_arg: Finite<f64>, delta_y_arg: Finite<f64>,
delta_z_arg: Finite<f64>, delta_z_arg: Finite<f64>,
delta_mode_arg: u32, delta_mode_arg: u32,
can_gc: CanGc,
) { ) {
if self.upcast::<Event>().dispatching() { if self.upcast::<Event>().dispatching() {
return; return;
@ -291,7 +290,6 @@ impl WheelEventMethods<crate::DomTypeHolder> for WheelEvent {
self.mouseevent.MetaKey(), self.mouseevent.MetaKey(),
self.mouseevent.Button(), self.mouseevent.Button(),
self.mouseevent.GetRelatedTarget().as_deref(), self.mouseevent.GetRelatedTarget().as_deref(),
can_gc,
); );
self.delta_x.set(delta_x_arg); self.delta_x.set(delta_x_arg);
self.delta_y.set(delta_y_arg); self.delta_y.set(delta_y_arg);

View file

@ -1634,7 +1634,7 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
/// <https://drafts.csswg.org/cssom-view/#dom-window-scrollx> /// <https://drafts.csswg.org/cssom-view/#dom-window-scrollx>
fn ScrollX(&self) -> i32 { fn ScrollX(&self) -> i32 {
self.scroll_offset(CanGc::note()).x as i32 self.scroll_offset().x as i32
} }
// https://drafts.csswg.org/cssom-view/#dom-window-pagexoffset // https://drafts.csswg.org/cssom-view/#dom-window-pagexoffset
@ -1644,7 +1644,7 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
/// <https://drafts.csswg.org/cssom-view/#dom-window-scrolly> /// <https://drafts.csswg.org/cssom-view/#dom-window-scrolly>
fn ScrollY(&self) -> i32 { fn ScrollY(&self) -> i32 {
self.scroll_offset(CanGc::note()).y as i32 self.scroll_offset().y as i32
} }
// https://drafts.csswg.org/cssom-view/#dom-window-pageyoffset // https://drafts.csswg.org/cssom-view/#dom-window-pageyoffset
@ -1653,47 +1653,47 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
} }
// https://drafts.csswg.org/cssom-view/#dom-window-scroll // https://drafts.csswg.org/cssom-view/#dom-window-scroll
fn Scroll(&self, options: &ScrollToOptions, can_gc: CanGc) { fn Scroll(&self, options: &ScrollToOptions) {
// Step 1 // Step 1
let left = options.left.unwrap_or(0.0f64); let left = options.left.unwrap_or(0.0f64);
let top = options.top.unwrap_or(0.0f64); let top = options.top.unwrap_or(0.0f64);
self.scroll(left, top, options.parent.behavior, can_gc); self.scroll(left, top, options.parent.behavior);
} }
// https://drafts.csswg.org/cssom-view/#dom-window-scroll // https://drafts.csswg.org/cssom-view/#dom-window-scroll
fn Scroll_(&self, x: f64, y: f64, can_gc: CanGc) { fn Scroll_(&self, x: f64, y: f64) {
self.scroll(x, y, ScrollBehavior::Auto, can_gc); self.scroll(x, y, ScrollBehavior::Auto);
} }
// https://drafts.csswg.org/cssom-view/#dom-window-scrollto // https://drafts.csswg.org/cssom-view/#dom-window-scrollto
fn ScrollTo(&self, options: &ScrollToOptions) { fn ScrollTo(&self, options: &ScrollToOptions) {
self.Scroll(options, CanGc::note()); self.Scroll(options);
} }
// https://drafts.csswg.org/cssom-view/#dom-window-scrollto // https://drafts.csswg.org/cssom-view/#dom-window-scrollto
fn ScrollTo_(&self, x: f64, y: f64) { fn ScrollTo_(&self, x: f64, y: f64) {
self.scroll(x, y, ScrollBehavior::Auto, CanGc::note()); self.scroll(x, y, ScrollBehavior::Auto);
} }
// https://drafts.csswg.org/cssom-view/#dom-window-scrollby // https://drafts.csswg.org/cssom-view/#dom-window-scrollby
fn ScrollBy(&self, options: &ScrollToOptions, can_gc: CanGc) { fn ScrollBy(&self, options: &ScrollToOptions) {
// Step 1 // Step 1
let x = options.left.unwrap_or(0.0f64); let x = options.left.unwrap_or(0.0f64);
let y = options.top.unwrap_or(0.0f64); let y = options.top.unwrap_or(0.0f64);
self.ScrollBy_(x, y, can_gc); self.ScrollBy_(x, y);
self.scroll(x, y, options.parent.behavior, can_gc); self.scroll(x, y, options.parent.behavior);
} }
// https://drafts.csswg.org/cssom-view/#dom-window-scrollby // https://drafts.csswg.org/cssom-view/#dom-window-scrollby
fn ScrollBy_(&self, x: f64, y: f64, can_gc: CanGc) { fn ScrollBy_(&self, x: f64, y: f64) {
let scroll_offset = self.scroll_offset(can_gc); let scroll_offset = self.scroll_offset();
// Step 3 // Step 3
let left = x + scroll_offset.x as f64; let left = x + scroll_offset.x as f64;
// Step 4 // Step 4
let top = y + scroll_offset.y as f64; let top = y + scroll_offset.y as f64;
// Step 5 // Step 5
self.scroll(left, top, ScrollBehavior::Auto, can_gc); self.scroll(left, top, ScrollBehavior::Auto);
} }
// https://drafts.csswg.org/cssom-view/#dom-window-resizeto // https://drafts.csswg.org/cssom-view/#dom-window-resizeto
@ -2012,11 +2012,8 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
} }
impl Window { impl Window {
pub(crate) fn scroll_offset(&self, can_gc: CanGc) -> Vector2D<f32, LayoutPixel> { pub(crate) fn scroll_offset(&self) -> Vector2D<f32, LayoutPixel> {
self.scroll_offset_query_with_external_scroll_id( self.scroll_offset_query_with_external_scroll_id(self.pipeline_id().root_scroll_id())
self.pipeline_id().root_scroll_id(),
can_gc,
)
} }
// https://heycam.github.io/webidl/#named-properties-object // https://heycam.github.io/webidl/#named-properties-object
@ -2114,7 +2111,7 @@ impl Window {
} }
/// <https://drafts.csswg.org/cssom-view/#dom-window-scroll> /// <https://drafts.csswg.org/cssom-view/#dom-window-scroll>
pub(crate) fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior, can_gc: CanGc) { pub(crate) fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior) {
// Step 3 // Step 3
let xfinite = if x_.is_finite() { x_ } else { 0.0f64 }; let xfinite = if x_.is_finite() { x_ } else { 0.0f64 };
let yfinite = if y_.is_finite() { y_ } else { 0.0f64 }; let yfinite = if y_.is_finite() { y_ } else { 0.0f64 };
@ -2127,7 +2124,7 @@ impl Window {
// Step 7 & 8 // Step 7 & 8
// TODO: Consider `block-end` and `inline-end` overflow direction. // TODO: Consider `block-end` and `inline-end` overflow direction.
let scrolling_area = self.scrolling_area_query(None, can_gc); let scrolling_area = self.scrolling_area_query(None);
let x = xfinite let x = xfinite
.min(scrolling_area.width() as f64 - viewport.width as f64) .min(scrolling_area.width() as f64 - viewport.width as f64)
.max(0.0f64); .max(0.0f64);
@ -2137,7 +2134,7 @@ impl Window {
// Step 10 // Step 10
//TODO handling ongoing smooth scrolling //TODO handling ongoing smooth scrolling
let scroll_offset = self.scroll_offset(can_gc); let scroll_offset = self.scroll_offset();
if x == scroll_offset.x as f64 && y == scroll_offset.y as f64 { if x == scroll_offset.x as f64 && y == scroll_offset.y as f64 {
return; return;
} }
@ -2153,7 +2150,6 @@ impl Window {
self.pipeline_id().root_scroll_id(), self.pipeline_id().root_scroll_id(),
behavior, behavior,
None, None,
can_gc,
); );
} }
@ -2165,7 +2161,6 @@ impl Window {
scroll_id: ExternalScrollId, scroll_id: ExternalScrollId,
_behavior: ScrollBehavior, _behavior: ScrollBehavior,
element: Option<&Element>, element: Option<&Element>,
can_gc: CanGc,
) { ) {
// TODO Step 1 // TODO Step 1
// TODO(mrobinson, #18709): Add smooth scrolling support to WebRender so that we can // TODO(mrobinson, #18709): Add smooth scrolling support to WebRender so that we can
@ -2173,10 +2168,7 @@ impl Window {
let WindowReflowResult { let WindowReflowResult {
update_scroll_reflow_target_scrolled, update_scroll_reflow_target_scrolled,
.. ..
} = self.reflow( } = self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)),
can_gc,
);
// > If the scroll position did not change as a result of the user interaction or programmatic // > If the scroll position did not change as a result of the user interaction or programmatic
// > invocation, where no translations were applied as a result, then no scrollend event fires // > invocation, where no translations were applied as a result, then no scrollend event fires
@ -2218,9 +2210,15 @@ impl Window {
/// ///
/// NOTE: This method should almost never be called directly! Layout and rendering updates should /// NOTE: This method should almost never be called directly! Layout and rendering updates should
/// happen as part of the HTML event loop via *update the rendering*. /// happen as part of the HTML event loop via *update the rendering*.
fn force_reflow(&self, reflow_goal: ReflowGoal) -> WindowReflowResult { pub(crate) fn reflow(&self, reflow_goal: ReflowGoal) -> WindowReflowResult {
let document = self.Document(); let document = self.Document();
document.ensure_safe_to_run_script_or_layout();
// Never reflow inactive Documents.
if !document.is_fully_active() {
return WindowReflowResult::new_empty();
}
self.Document().ensure_safe_to_run_script_or_layout();
// If layouts are blocked, we block all layouts that are for display only. Other // If layouts are blocked, we block all layouts that are for display only. Other
// layouts (for queries and scrolling) are not blocked, as they do not display // layouts (for queries and scrolling) are not blocked, as they do not display
@ -2314,88 +2312,60 @@ impl Window {
} }
} }
/// Reflows the page if it's possible to do so and the page is dirty. Returns true if layout pub(crate) fn maybe_send_idle_document_state_to_constellation(&self) {
/// actually happened and produced a new display list, false otherwise. if !opts::get().wait_for_stable_image {
/// return;
/// NOTE: This method should almost never be called directly! Layout and rendering updates
/// should happen as part of the HTML event loop via *update the rendering*. Currerntly, the
/// only exceptions are script queries and scroll requests.
pub(crate) fn reflow(&self, reflow_goal: ReflowGoal, can_gc: CanGc) -> WindowReflowResult {
// Never reflow inactive Documents.
if !self.Document().is_fully_active() {
return WindowReflowResult::new_empty();
} }
// Count the pending web fonts before layout, in case a font loads during the layout. if self.has_sent_idle_message.get() {
let waiting_for_web_fonts_to_load = self.font_context.web_fonts_still_loading() != 0; return;
}
self.Document().ensure_safe_to_run_script_or_layout();
let updating_the_rendering = reflow_goal == ReflowGoal::UpdateTheRendering;
let reflow_result = self.force_reflow(reflow_goal);
let document = self.Document(); let document = self.Document();
let font_face_set = document.Fonts(can_gc); if document.ReadyState() != DocumentReadyState::Complete {
let is_ready_state_complete = document.ReadyState() == DocumentReadyState::Complete; return;
// From https://drafts.csswg.org/css-font-loading/#font-face-set-ready:
// > A FontFaceSet is pending on the environment if any of the following are true:
// > - the document is still loading
// > - the document has pending stylesheet requests
// > - the document has pending layout operations which might cause the user agent to request
// > a font, or which depend on recently-loaded fonts
//
// Thus, we are queueing promise resolution here. This reflow should have been triggered by
// a "rendering opportunity" in `ScriptThread::handle_web_font_loaded, which should also
// make sure a microtask checkpoint happens, triggering the promise callback.
if !waiting_for_web_fonts_to_load && is_ready_state_complete {
font_face_set.fulfill_ready_promise_if_needed(can_gc);
} }
// If writing a screenshot, check if the script has reached a state
// where it's safe to write the image. This means that:
// 1) The reflow is for display (otherwise it could be a query)
// 2) The html element doesn't contain the 'reftest-wait' class
// 3) The load event has fired.
// When all these conditions are met, notify the constellation
// that this pipeline is ready to write the image (from the script thread
// perspective at least).
if opts::get().wait_for_stable_image && updating_the_rendering {
// Checks if the html element has reftest-wait attribute present. // Checks if the html element has reftest-wait attribute present.
// See http://testthewebforward.org/docs/reftests.html // See http://testthewebforward.org/docs/reftests.html
// and https://web-platform-tests.org/writing-tests/crashtest.html // and https://web-platform-tests.org/writing-tests/crashtest.html
let html_element = document.GetDocumentElement(); if document.GetDocumentElement().is_some_and(|elem| {
let reftest_wait = html_element.is_some_and(|elem| {
elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) || elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive) elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
}); }) {
return;
}
let has_sent_idle_message = self.has_sent_idle_message.get(); if self.font_context.web_fonts_still_loading() != 0 {
let no_pending_images = self.pending_layout_images.borrow().is_empty() && return;
self.pending_images_for_rasterization.borrow().is_empty(); }
if !has_sent_idle_message && if !self.pending_layout_images.borrow().is_empty() ||
is_ready_state_complete && !self.pending_images_for_rasterization.borrow().is_empty()
!reftest_wait &&
no_pending_images &&
!waiting_for_web_fonts_to_load
{ {
return;
}
if self.Document().needs_rendering_update() {
return;
}
// When all these conditions are met, notify the constellation
// that this pipeline is ready to write the image (from the script thread
// perspective at least).
debug!( debug!(
"{:?}: Sending DocumentState::Idle to Constellation", "{:?}: Sending DocumentState::Idle to Constellation",
self.pipeline_id() self.pipeline_id()
); );
let event = ScriptToConstellationMessage::SetDocumentState(DocumentState::Idle); self.send_to_constellation(ScriptToConstellationMessage::SetDocumentState(
self.send_to_constellation(event); DocumentState::Idle,
));
self.has_sent_idle_message.set(true); self.has_sent_idle_message.set(true);
} }
}
reflow_result
}
/// If parsing has taken a long time and reflows are still waiting for the `load` event, /// If parsing has taken a long time and reflows are still waiting for the `load` event,
/// start allowing them. See <https://github.com/servo/servo/pull/6028>. /// start allowing them. See <https://github.com/servo/servo/pull/6028>.
pub(crate) fn reflow_if_reflow_timer_expired(&self, can_gc: CanGc) { pub(crate) fn reflow_if_reflow_timer_expired(&self) {
// Only trigger a long parsing time reflow if we are in the first parse of `<body>` // Only trigger a long parsing time reflow if we are in the first parse of `<body>`
// and it started more than `INITIAL_REFLOW_DELAY` ago. // and it started more than `INITIAL_REFLOW_DELAY` ago.
if !matches!( if !matches!(
@ -2404,7 +2374,7 @@ impl Window {
) { ) {
return; return;
} }
self.allow_layout_if_necessary(can_gc); self.allow_layout_if_necessary();
} }
/// Block layout for this `Window` until parsing is done. If parsing takes a long time, /// Block layout for this `Window` until parsing is done. If parsing takes a long time,
@ -2423,7 +2393,7 @@ impl Window {
/// Inform the [`Window`] that layout is allowed either because `load` has happened /// Inform the [`Window`] that layout is allowed either because `load` has happened
/// or because parsing the `<body>` took so long that we cannot wait any longer. /// or because parsing the `<body>` took so long that we cannot wait any longer.
pub(crate) fn allow_layout_if_necessary(&self, can_gc: CanGc) { pub(crate) fn allow_layout_if_necessary(&self) {
if matches!( if matches!(
self.layout_blocker.get(), self.layout_blocker.get(),
LayoutBlocker::FiredLoadEventOrParsingTimerExpired LayoutBlocker::FiredLoadEventOrParsingTimerExpired
@ -2445,7 +2415,7 @@ impl Window {
// iframe size updates. // iframe size updates.
// //
// See <https://github.com/servo/servo/issues/14719> // See <https://github.com/servo/servo/issues/14719>
self.Document().update_the_rendering(can_gc); self.Document().update_the_rendering();
} }
pub(crate) fn layout_blocked(&self) -> bool { pub(crate) fn layout_blocked(&self) -> bool {
@ -2471,17 +2441,16 @@ impl Window {
} }
/// Trigger a reflow that is required by a certain queries. /// Trigger a reflow that is required by a certain queries.
pub(crate) fn layout_reflow(&self, query_msg: QueryMsg, can_gc: CanGc) { pub(crate) fn layout_reflow(&self, query_msg: QueryMsg) {
self.reflow(ReflowGoal::LayoutQuery(query_msg), can_gc); self.reflow(ReflowGoal::LayoutQuery(query_msg));
} }
pub(crate) fn resolved_font_style_query( pub(crate) fn resolved_font_style_query(
&self, &self,
node: &Node, node: &Node,
value: String, value: String,
can_gc: CanGc,
) -> Option<ServoArc<Font>> { ) -> Option<ServoArc<Font>> {
self.layout_reflow(QueryMsg::ResolvedFontStyleQuery, can_gc); self.layout_reflow(QueryMsg::ResolvedFontStyleQuery);
let document = self.Document(); let document = self.Document();
let animations = document.animations().sets.clone(); let animations = document.animations().sets.clone();
@ -2500,20 +2469,20 @@ impl Window {
.query_content_box(node.to_trusted_node_address()) .query_content_box(node.to_trusted_node_address())
} }
pub(crate) fn content_box_query(&self, node: &Node, can_gc: CanGc) -> Option<UntypedRect<Au>> { pub(crate) fn content_box_query(&self, node: &Node) -> Option<UntypedRect<Au>> {
self.layout_reflow(QueryMsg::ContentBox, can_gc); self.layout_reflow(QueryMsg::ContentBox);
self.content_box_query_unchecked(node) self.content_box_query_unchecked(node)
} }
pub(crate) fn content_boxes_query(&self, node: &Node, can_gc: CanGc) -> Vec<UntypedRect<Au>> { pub(crate) fn content_boxes_query(&self, node: &Node) -> Vec<UntypedRect<Au>> {
self.layout_reflow(QueryMsg::ContentBoxes, can_gc); self.layout_reflow(QueryMsg::ContentBoxes);
self.layout self.layout
.borrow() .borrow()
.query_content_boxes(node.to_trusted_node_address()) .query_content_boxes(node.to_trusted_node_address())
} }
pub(crate) fn client_rect_query(&self, node: &Node, can_gc: CanGc) -> UntypedRect<i32> { pub(crate) fn client_rect_query(&self, node: &Node) -> UntypedRect<i32> {
self.layout_reflow(QueryMsg::ClientRectQuery, can_gc); self.layout_reflow(QueryMsg::ClientRectQuery);
self.layout self.layout
.borrow() .borrow()
.query_client_rect(node.to_trusted_node_address()) .query_client_rect(node.to_trusted_node_address())
@ -2521,35 +2490,26 @@ impl Window {
/// Find the scroll area of the given node, if it is not None. If the node /// Find the scroll area of the given node, if it is not None. If the node
/// is None, find the scroll area of the viewport. /// is None, find the scroll area of the viewport.
pub(crate) fn scrolling_area_query( pub(crate) fn scrolling_area_query(&self, node: Option<&Node>) -> UntypedRect<i32> {
&self, self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
node: Option<&Node>,
can_gc: CanGc,
) -> UntypedRect<i32> {
self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery, can_gc);
self.layout self.layout
.borrow() .borrow()
.query_scrolling_area(node.map(Node::to_trusted_node_address)) .query_scrolling_area(node.map(Node::to_trusted_node_address))
} }
pub(crate) fn scroll_offset_query( pub(crate) fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> {
&self,
node: &Node,
can_gc: CanGc,
) -> Vector2D<f32, LayoutPixel> {
let external_scroll_id = ExternalScrollId( let external_scroll_id = ExternalScrollId(
combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody), combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
self.pipeline_id().into(), self.pipeline_id().into(),
); );
self.scroll_offset_query_with_external_scroll_id(external_scroll_id, can_gc) self.scroll_offset_query_with_external_scroll_id(external_scroll_id)
} }
fn scroll_offset_query_with_external_scroll_id( fn scroll_offset_query_with_external_scroll_id(
&self, &self,
external_scroll_id: ExternalScrollId, external_scroll_id: ExternalScrollId,
can_gc: CanGc,
) -> Vector2D<f32, LayoutPixel> { ) -> Vector2D<f32, LayoutPixel> {
self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery, can_gc); self.layout_reflow(QueryMsg::ScrollingAreaOrOffsetQuery);
self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id) self.scroll_offset_query_with_external_scroll_id_no_reflow(external_scroll_id)
} }
@ -2571,7 +2531,6 @@ impl Window {
x_: f64, x_: f64,
y_: f64, y_: f64,
behavior: ScrollBehavior, behavior: ScrollBehavior,
can_gc: CanGc,
) { ) {
let scroll_id = ExternalScrollId( let scroll_id = ExternalScrollId(
combine_id_with_fragment_type( combine_id_with_fragment_type(
@ -2590,7 +2549,6 @@ impl Window {
scroll_id, scroll_id,
behavior, behavior,
Some(element), Some(element),
can_gc,
); );
} }
@ -2599,9 +2557,8 @@ impl Window {
element: TrustedNodeAddress, element: TrustedNodeAddress,
pseudo: Option<PseudoElement>, pseudo: Option<PseudoElement>,
property: PropertyId, property: PropertyId,
can_gc: CanGc,
) -> DOMString { ) -> DOMString {
self.layout_reflow(QueryMsg::ResolvedStyleQuery, can_gc); self.layout_reflow(QueryMsg::ResolvedStyleQuery);
let document = self.Document(); let document = self.Document();
let animations = document.animations().sets.clone(); let animations = document.animations().sets.clone();
@ -2620,10 +2577,9 @@ impl Window {
pub(crate) fn get_iframe_viewport_details_if_known( pub(crate) fn get_iframe_viewport_details_if_known(
&self, &self,
browsing_context_id: BrowsingContextId, browsing_context_id: BrowsingContextId,
can_gc: CanGc,
) -> Option<ViewportDetails> { ) -> Option<ViewportDetails> {
// Reflow might fail, but do a best effort to return the right size. // Reflow might fail, but do a best effort to return the right size.
self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery, can_gc); self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery);
self.Document() self.Document()
.iframes() .iframes()
.get(browsing_context_id) .get(browsing_context_id)
@ -2634,9 +2590,8 @@ impl Window {
pub(crate) fn offset_parent_query( pub(crate) fn offset_parent_query(
&self, &self,
node: &Node, node: &Node,
can_gc: CanGc,
) -> (Option<DomRoot<Element>>, UntypedRect<Au>) { ) -> (Option<DomRoot<Element>>, UntypedRect<Au>) {
self.layout_reflow(QueryMsg::OffsetParentQuery, can_gc); self.layout_reflow(QueryMsg::OffsetParentQuery);
let response = self let response = self
.layout .layout
.borrow() .borrow()
@ -2652,9 +2607,8 @@ impl Window {
&self, &self,
node: &Node, node: &Node,
point_in_node: UntypedPoint2D<f32>, point_in_node: UntypedPoint2D<f32>,
can_gc: CanGc,
) -> Option<usize> { ) -> Option<usize> {
self.layout_reflow(QueryMsg::TextIndexQuery, can_gc); self.layout_reflow(QueryMsg::TextIndexQuery);
self.layout self.layout
.borrow() .borrow()
.query_text_indext(node.to_opaque(), point_in_node) .query_text_indext(node.to_opaque(), point_in_node)
@ -2710,7 +2664,7 @@ impl Window {
load_data.url.clone(), load_data.url.clone(),
history_handling, history_handling,
)); ));
doc.check_and_scroll_fragment(fragment, can_gc); doc.check_and_scroll_fragment(fragment);
let this = Trusted::new(self); let this = Trusted::new(self);
let old_url = doc.url().into_string(); let old_url = doc.url().into_string();
let new_url = load_data.url.clone().into_string(); let new_url = load_data.url.clone().into_string();
@ -2832,6 +2786,11 @@ impl Window {
self.unhandled_resize_event.borrow_mut().take() self.unhandled_resize_event.borrow_mut().take()
} }
/// Whether or not this [`Window`] has any resize events that have not been processed.
pub(crate) fn has_unhandled_resize_event(&self) -> bool {
self.unhandled_resize_event.borrow().is_some()
}
pub(crate) fn set_viewport_size(&self, new_viewport_size: UntypedSize2D<f32>) { pub(crate) fn set_viewport_size(&self, new_viewport_size: UntypedSize2D<f32>) {
let new_viewport_size = Size2D::new( let new_viewport_size = Size2D::new(
Au::from_f32_px(new_viewport_size.width), Au::from_f32_px(new_viewport_size.width),

View file

@ -100,8 +100,6 @@ impl MicrotaskQueue {
// Step 1 // Step 1
self.performing_a_microtask_checkpoint.set(true); self.performing_a_microtask_checkpoint.set(true);
debug!("Now performing a microtask checkpoint");
// Steps 2 // Steps 2
while !self.microtask_queue.borrow().is_empty() { while !self.microtask_queue.borrow().is_empty() {
rooted_vec!(let mut pending_queue); rooted_vec!(let mut pending_queue);

View file

@ -200,7 +200,8 @@ type NodeIdSet = HashSet<String>;
#[cfg_attr(crown, allow(crown::unrooted_must_root))] #[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub struct ScriptThread { pub struct ScriptThread {
/// <https://html.spec.whatwg.org/multipage/#last-render-opportunity-time> /// <https://html.spec.whatwg.org/multipage/#last-render-opportunity-time>
last_render_opportunity_time: DomRefCell<Option<Instant>>, last_render_opportunity_time: Cell<Option<Instant>>,
/// The documents for pipelines managed by this thread /// The documents for pipelines managed by this thread
documents: DomRefCell<DocumentCollection>, documents: DomRefCell<DocumentCollection>,
/// The window proxies known by this thread /// The window proxies known by this thread
@ -348,7 +349,7 @@ pub struct ScriptThread {
/// [`ScriptThreadMessage::TickAllAnimations`] message or because the [`ScriptThread`] /// [`ScriptThreadMessage::TickAllAnimations`] message or because the [`ScriptThread`]
/// itself is managing animations the the timer fired triggering a [`ScriptThread`]-based /// itself is managing animations the the timer fired triggering a [`ScriptThread`]-based
/// animation tick. /// animation tick.
has_pending_animation_tick: Arc<AtomicBool>, needs_rendering_update: Arc<AtomicBool>,
debugger_global: Dom<DebuggerGlobalScope>, debugger_global: Dom<DebuggerGlobalScope>,
} }
@ -600,8 +601,7 @@ impl ScriptThread {
} }
pub(crate) fn set_has_pending_animation_tick(&self) { pub(crate) fn set_has_pending_animation_tick(&self) {
self.has_pending_animation_tick self.needs_rendering_update.store(true, Ordering::Relaxed);
.store(true, Ordering::Relaxed);
} }
/// Step 13 of <https://html.spec.whatwg.org/multipage/#navigate> /// Step 13 of <https://html.spec.whatwg.org/multipage/#navigate>
@ -999,7 +999,7 @@ impl ScriptThread {
layout_factory, layout_factory,
relative_mouse_down_point: Cell::new(Point2D::zero()), relative_mouse_down_point: Cell::new(Point2D::zero()),
scheduled_script_thread_animation_timer: Default::default(), scheduled_script_thread_animation_timer: Default::default(),
has_pending_animation_tick: Arc::new(AtomicBool::new(false)), needs_rendering_update: Arc::new(AtomicBool::new(false)),
debugger_global: debugger_global.as_traced(), debugger_global: debugger_global.as_traced(),
} }
} }
@ -1208,20 +1208,7 @@ impl ScriptThread {
})); }));
} }
/// <https://html.spec.whatwg.org/multipage/#update-the-rendering> fn cancel_scheduled_update_the_rendering(&self) {
///
/// Attempt to update the rendering and then do a microtask checkpoint if rendering was actually
/// updated.
pub(crate) fn update_the_rendering(&self, can_gc: CanGc) {
*self.last_render_opportunity_time.borrow_mut() = Some(Instant::now());
let is_animation_tick = self.has_pending_animation_tick.load(Ordering::Relaxed);
if is_animation_tick {
self.has_pending_animation_tick
.store(false, Ordering::Relaxed);
// If this is an animation tick, cancel any upcoming ScriptThread-based animation timer.
// This tick serves the purpose and we to limit animation ticks if some are coming from
// the renderer.
if let Some(timer_id) = self if let Some(timer_id) = self
.scheduled_script_thread_animation_timer .scheduled_script_thread_animation_timer
.borrow_mut() .borrow_mut()
@ -1231,14 +1218,40 @@ impl ScriptThread {
} }
} }
if !self.can_continue_running_inner() { fn schedule_update_the_rendering_timer_if_necessary(&self, delay: Duration) {
if self
.scheduled_script_thread_animation_timer
.borrow()
.is_some()
{
return; return;
} }
let any_animations_running = self.documents.borrow().iter().any(|(_, document)| { debug!("Scheduling ScriptThread animation frame.");
document.is_fully_active() && document.animations().running_animation_count() != 0 let trigger_script_thread_animation = self.needs_rendering_update.clone();
let timer_id = self.schedule_timer(TimerEventRequest {
callback: Box::new(move || {
trigger_script_thread_animation.store(true, Ordering::Relaxed);
}),
duration: delay,
}); });
*self.scheduled_script_thread_animation_timer.borrow_mut() = Some(timer_id);
}
/// <https://html.spec.whatwg.org/multipage/#update-the-rendering>
///
/// Attempt to update the rendering and then do a microtask checkpoint if rendering was actually
/// updated.
pub(crate) fn update_the_rendering(&self, can_gc: CanGc) {
self.last_render_opportunity_time.set(Some(Instant::now()));
self.cancel_scheduled_update_the_rendering();
self.needs_rendering_update.store(false, Ordering::Relaxed);
if !self.can_continue_running_inner() {
return;
}
// TODO: The specification says to filter out non-renderable documents, // TODO: The specification says to filter out non-renderable documents,
// as well as those for which a rendering update would be unnecessary, // as well as those for which a rendering update would be unnecessary,
// but this isn't happening here. // but this isn't happening here.
@ -1247,13 +1260,6 @@ impl ScriptThread {
// has pending initial observation targets // has pending initial observation targets
// https://w3c.github.io/IntersectionObserver/#pending-initial-observation // https://w3c.github.io/IntersectionObserver/#pending-initial-observation
// If we aren't explicitly running rAFs, this update wasn't requested by the compositor,
// and we are running animations, then wait until the compositor tells us it is time to
// update the rendering via a TickAllAnimations message.
if !is_animation_tick && any_animations_running {
return;
}
// > 2. Let docs be all fully active Document objects whose relevant agent's event loop // > 2. Let docs be all fully active Document objects whose relevant agent's event loop
// > is eventLoop, sorted arbitrarily except that the following conditions must be // > is eventLoop, sorted arbitrarily except that the following conditions must be
// > met: // > met:
@ -1326,14 +1332,12 @@ impl ScriptThread {
// > 14. For each doc of docs, run the animation frame callbacks for doc, passing // > 14. For each doc of docs, run the animation frame callbacks for doc, passing
// > in the relative high resolution time given frameTimestamp and doc's // > in the relative high resolution time given frameTimestamp and doc's
// > relevant global object as the timestamp. // > relevant global object as the timestamp.
if is_animation_tick {
document.run_the_animation_frame_callbacks(can_gc); document.run_the_animation_frame_callbacks(can_gc);
}
// Run the resize observer steps. // Run the resize observer steps.
let _realm = enter_realm(&*document); let _realm = enter_realm(&*document);
let mut depth = Default::default(); let mut depth = Default::default();
while document.gather_active_resize_observations_at_depth(&depth, can_gc) { while document.gather_active_resize_observations_at_depth(&depth) {
// Note: this will reflow the doc. // Note: this will reflow the doc.
depth = document.broadcast_active_resize_observations(can_gc); depth = document.broadcast_active_resize_observations(can_gc);
} }
@ -1358,7 +1362,7 @@ impl ScriptThread {
// > Step 22: For each doc of docs, update the rendering or user interface of // > Step 22: For each doc of docs, update the rendering or user interface of
// > doc and its node navigable to reflect the current state. // > doc and its node navigable to reflect the current state.
saw_any_reflows = document.update_the_rendering(can_gc) || saw_any_reflows; saw_any_reflows = document.update_the_rendering() || saw_any_reflows;
// TODO: Process top layer removals according to // TODO: Process top layer removals according to
// https://drafts.csswg.org/css-position-4/#process-top-layer-removals. // https://drafts.csswg.org/css-position-4/#process-top-layer-removals.
@ -1368,106 +1372,90 @@ impl ScriptThread {
// should be run in a task and a microtask checkpoint is always done when running tasks. // should be run in a task and a microtask checkpoint is always done when running tasks.
self.perform_a_microtask_checkpoint(can_gc); self.perform_a_microtask_checkpoint(can_gc);
// If there are pending reflows, they were probably caused by the execution of self.maybe_schedule_rendering_opportunity_after_rendering_update(saw_any_reflows);
// the microtask checkpoint above and we should spin the event loop one more
// time to resolve them.
self.schedule_rendering_opportunity_if_necessary();
// If this was a animation update request, then potentially schedule a new
// animation update in the case that the compositor might not do it due to
// not receiving any display lists.
if is_animation_tick {
self.schedule_script_thread_animation_tick_if_necessary(saw_any_reflows);
}
} }
fn maybe_schedule_rendering_opportunity_after_rendering_update(&self, saw_any_reflows: bool) {
// If there are any pending reflows and we are not having rendering opportunities // If there are any pending reflows and we are not having rendering opportunities
// driven by the compositor, then schedule the next rendering opportunity. // immediately after running "update the rendering," run it one more time.
//
// TODO: This is a workaround until rendering opportunities can be triggered from a
// timer in the script thread.
fn schedule_rendering_opportunity_if_necessary(&self) {
// If any Document has active animations of rAFs, then we should be receiving
// regular rendering opportunities from the compositor (or fake animation frame
// ticks). In this case, don't schedule an opportunity, just wait for the next
// one.
if self.documents.borrow().iter().any(|(_, document)| { if self.documents.borrow().iter().any(|(_, document)| {
document.is_fully_active() && document.needs_rendering_update() ||
(document.animations().running_animation_count() != 0 || (saw_any_reflows && document.has_resize_observers())
document.has_active_request_animation_frame_callbacks())
}) { }) {
return; self.cancel_scheduled_update_the_rendering();
self.schedule_update_the_rendering_timer_if_necessary(Duration::ZERO);
} }
let Some((_, document)) = self.documents.borrow().iter().find(|(_, document)| { if !saw_any_reflows &&
document.is_fully_active() && self.documents.borrow().iter().any(|(_, document)| {
!document.window().layout_blocked() &&
!document.restyle_reason().is_empty()
}) else {
return;
};
// Queues a task to update the rendering.
// <https://html.spec.whatwg.org/multipage/#event-loop-processing-model:queue-a-global-task>
//
// Note: The specification says to queue a task using the navigable's active
// window, but then updates the rendering for all documents.
//
// This task is empty because any new IPC messages in the ScriptThread trigger a
// rendering update when animations are not running.
let _realm = enter_realm(&*document);
document
.owner_global()
.task_manager()
.rendering_task_source()
.queue_unconditionally(task!(update_the_rendering: move || { }));
}
/// The renderer triggers animation ticks based on the arrival and painting of new
/// display lists. In the case that a `WebView` is animating or has a
/// requestAnimationFrame callback, it may be that an animation tick reflow does
/// not change anything and thus does not send a new display list to the renderer.
/// If that's the case, we need to schedule a ScriptThread-based animation update
/// (to avoid waking the renderer up).
fn schedule_script_thread_animation_tick_if_necessary(&self, saw_any_reflows: bool) {
if saw_any_reflows {
return;
}
// Always schedule a ScriptThread-based animation tick, unless none of the
// documents are active and have animations running and/or rAF callbacks.
if !self.documents.borrow().iter().any(|(_, document)| {
document.is_fully_active() && document.is_fully_active() &&
!document.window().throttled() && !document.window().throttled() &&
(document.animations().running_animation_count() != 0 || (document.animations().running_animation_count() != 0 ||
document.has_active_request_animation_frame_callbacks()) document.has_active_request_animation_frame_callbacks())
}) { })
{
const SCRIPT_THREAD_ANIMATION_TICK_DELAY: Duration = Duration::from_millis(30);
self.schedule_update_the_rendering_timer_if_necessary(
SCRIPT_THREAD_ANIMATION_TICK_DELAY,
);
}
}
fn maybe_schedule_rendering_opportunity_after_ipc_message(&self) {
// If no document needs a rendering update, exit early to avoid doing more work.
if !self
.documents
.borrow()
.iter()
.any(|(_, document)| document.needs_rendering_update())
{
return; return;
} }
/// The amount of time between ScriptThread animation ticks when nothing is // Wait 20 milliseconds between frames triggered by the script thread itself. This
/// changing. In order to be more efficient, only tick at around 30 frames a // should, in theory, allow compositor-based ticks to arrive sooner.
/// second, which also gives time for any renderer ticks to come in and cancel const SCRIPT_THREAD_ANIMATION_TICK_DELAY: Duration = Duration::from_millis(20);
/// this tick. A renderer tick might happen for a variety of reasons, such as a let time_since_last_rendering_opportunity = self
/// Pipeline in another ScriptThread producing a display list. .last_render_opportunity_time
const SCRIPT_THREAD_ANIMATION_TICK_DELAY: u64 = 30; .get()
.map(|last_render_opportunity_time| Instant::now() - last_render_opportunity_time)
.unwrap_or(Duration::MAX)
.min(SCRIPT_THREAD_ANIMATION_TICK_DELAY);
debug!("Scheduling ScriptThread animation frame."); //eprintln!(" - scheduling update\n");
let trigger_script_thread_animation = self.has_pending_animation_tick.clone(); //// If it's been more than the time of a single frame the last rendering opportunity,
let timer_id = self.schedule_timer(TimerEventRequest { //// just run it now.
callback: Box::new(move || { //if time_since_last_rendering_opportunity > SCRIPT_THREAD_ANIMATION_TICK_DELAY {
trigger_script_thread_animation.store(true, Ordering::Relaxed); // println!(" - running immediately");
}), // self.update_the_rendering(can_gc);
duration: Duration::from_millis(SCRIPT_THREAD_ANIMATION_TICK_DELAY), // return;
}); //}
let mut scheduled_script_thread_animation_timer = self.schedule_update_the_rendering_timer_if_necessary(
self.scheduled_script_thread_animation_timer.borrow_mut(); SCRIPT_THREAD_ANIMATION_TICK_DELAY - time_since_last_rendering_opportunity,
assert!(
scheduled_script_thread_animation_timer.is_none(),
"Should never schedule a new timer when one is already scheduled."
); );
*scheduled_script_thread_animation_timer = Some(timer_id); }
fn maybe_fulfill_font_ready_promises(&self, can_gc: CanGc) {
let mut sent_message = false;
for (_, document) in self.documents.borrow().iter() {
sent_message = document.maybe_fulfill_font_ready_promise(can_gc) || sent_message;
}
if sent_message {
self.perform_a_microtask_checkpoint(can_gc);
}
}
fn maybe_send_document_state_messages(&self) {
if !opts::get().wait_for_stable_image {
return;
}
for (_, document) in self.documents.borrow().iter() {
document
.window()
.maybe_send_idle_document_state_to_constellation();
}
} }
/// Handle incoming messages from other tasks and the task queue. /// Handle incoming messages from other tasks and the task queue.
@ -1679,10 +1667,13 @@ impl ScriptThread {
docs.clear(); docs.clear();
} }
// Update the rendering whenever we receive an IPC message. This may not actually do anything if if self.needs_rendering_update.load(Ordering::Relaxed) {
// we are running animations and the compositor hasn't requested a new frame yet via a TickAllAnimatons
// message.
self.update_the_rendering(can_gc); self.update_the_rendering(can_gc);
}
self.maybe_fulfill_font_ready_promises(can_gc);
self.maybe_schedule_rendering_opportunity_after_ipc_message();
self.maybe_send_document_state_messages();
true true
} }
@ -2194,7 +2185,7 @@ impl ScriptThread {
devtools::handle_get_selectors(&documents, id, node_id, reply, can_gc) devtools::handle_get_selectors(&documents, id, node_id, reply, can_gc)
}, },
DevtoolScriptControlMsg::GetComputedStyle(id, node_id, reply) => { DevtoolScriptControlMsg::GetComputedStyle(id, node_id, reply) => {
devtools::handle_get_computed_style(&documents, id, node_id, reply, can_gc) devtools::handle_get_computed_style(&documents, id, node_id, reply)
}, },
DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => { DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => {
devtools::handle_get_layout(&documents, id, node_id, reply, can_gc) devtools::handle_get_layout(&documents, id, node_id, reply, can_gc)
@ -2317,7 +2308,6 @@ impl ScriptThread {
selector, selector,
partial, partial,
reply, reply,
can_gc,
) )
}, },
WebDriverScriptCommand::FindElementsTagName(selector, reply) => { WebDriverScriptCommand::FindElementsTagName(selector, reply) => {
@ -2359,7 +2349,6 @@ impl ScriptThread {
selector, selector,
partial, partial,
reply, reply,
can_gc,
), ),
WebDriverScriptCommand::FindElementElementsTagName(selector, element_id, reply) => { WebDriverScriptCommand::FindElementElementsTagName(selector, element_id, reply) => {
webdriver_handlers::handle_find_element_elements_tag_name( webdriver_handlers::handle_find_element_elements_tag_name(
@ -2406,7 +2395,6 @@ impl ScriptThread {
selector, selector,
partial, partial,
reply, reply,
can_gc,
), ),
WebDriverScriptCommand::FindShadowElementsTagName(selector, shadow_root_id, reply) => { WebDriverScriptCommand::FindShadowElementsTagName(selector, shadow_root_id, reply) => {
webdriver_handlers::handle_find_shadow_elements_tag_name( webdriver_handlers::handle_find_shadow_elements_tag_name(
@ -2489,14 +2477,7 @@ impl ScriptThread {
) )
}, },
WebDriverScriptCommand::GetElementCSS(node_id, name, reply) => { WebDriverScriptCommand::GetElementCSS(node_id, name, reply) => {
webdriver_handlers::handle_get_css( webdriver_handlers::handle_get_css(&documents, pipeline_id, node_id, name, reply)
&documents,
pipeline_id,
node_id,
name,
reply,
can_gc,
)
}, },
WebDriverScriptCommand::GetElementRect(node_id, reply) => { WebDriverScriptCommand::GetElementRect(node_id, reply) => {
webdriver_handlers::handle_get_rect(&documents, pipeline_id, node_id, reply, can_gc) webdriver_handlers::handle_get_rect(&documents, pipeline_id, node_id, reply, can_gc)
@ -2511,7 +2492,7 @@ impl ScriptThread {
) )
}, },
WebDriverScriptCommand::GetElementText(node_id, reply) => { WebDriverScriptCommand::GetElementText(node_id, reply) => {
webdriver_handlers::handle_get_text(&documents, pipeline_id, node_id, reply, can_gc) webdriver_handlers::handle_get_text(&documents, pipeline_id, node_id, reply)
}, },
WebDriverScriptCommand::GetElementInViewCenterPoint(node_id, reply) => { WebDriverScriptCommand::GetElementInViewCenterPoint(node_id, reply) => {
webdriver_handlers::handle_get_element_in_view_center_point( webdriver_handlers::handle_get_element_in_view_center_point(

View file

@ -145,7 +145,6 @@ impl TaskManager {
task_source_functions!(self, performance_timeline_task_source, PerformanceTimeline); task_source_functions!(self, performance_timeline_task_source, PerformanceTimeline);
task_source_functions!(self, port_message_queue, PortMessage); task_source_functions!(self, port_message_queue, PortMessage);
task_source_functions!(self, remote_event_task_source, RemoteEvent); task_source_functions!(self, remote_event_task_source, RemoteEvent);
task_source_functions!(self, rendering_task_source, Rendering);
task_source_functions!(self, timer_task_source, Timer); task_source_functions!(self, timer_task_source, Timer);
task_source_functions!(self, user_interaction_task_source, UserInteraction); task_source_functions!(self, user_interaction_task_source, UserInteraction);
task_source_functions!(self, websocket_task_source, WebSocket); task_source_functions!(self, websocket_task_source, WebSocket);

View file

@ -194,14 +194,13 @@ fn matching_links(
links: &NodeList, links: &NodeList,
link_text: String, link_text: String,
partial: bool, partial: bool,
can_gc: CanGc,
) -> impl Iterator<Item = String> + '_ { ) -> impl Iterator<Item = String> + '_ {
links links
.iter() .iter()
.filter(move |node| { .filter(move |node| {
let content = node let content = node
.downcast::<HTMLElement>() .downcast::<HTMLElement>()
.map(|element| element.InnerText(can_gc)) .map(|element| element.InnerText())
.map_or("".to_owned(), String::from) .map_or("".to_owned(), String::from)
.trim() .trim()
.to_owned(); .to_owned();
@ -218,7 +217,6 @@ fn all_matching_links(
root_node: &Node, root_node: &Node,
link_text: String, link_text: String,
partial: bool, partial: bool,
can_gc: CanGc,
) -> Result<Vec<String>, ErrorStatus> { ) -> Result<Vec<String>, ErrorStatus> {
// <https://w3c.github.io/webdriver/#dfn-find> // <https://w3c.github.io/webdriver/#dfn-find>
// Step 7.2. If a DOMException, SyntaxError, XPathException, or other error occurs // Step 7.2. If a DOMException, SyntaxError, XPathException, or other error occurs
@ -226,7 +224,7 @@ fn all_matching_links(
root_node root_node
.query_selector_all(DOMString::from("a")) .query_selector_all(DOMString::from("a"))
.map_err(|_| ErrorStatus::InvalidSelector) .map_err(|_| ErrorStatus::InvalidSelector)
.map(|nodes| matching_links(&nodes, link_text, partial, can_gc).collect()) .map(|nodes| matching_links(&nodes, link_text, partial).collect())
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -749,7 +747,6 @@ pub(crate) fn handle_find_elements_link_text(
selector: String, selector: String,
partial: bool, partial: bool,
reply: IpcSender<Result<Vec<String>, ErrorStatus>>, reply: IpcSender<Result<Vec<String>, ErrorStatus>>,
can_gc: CanGc,
) { ) {
match retrieve_document_and_check_root_existence(documents, pipeline) { match retrieve_document_and_check_root_existence(documents, pipeline) {
Ok(document) => reply Ok(document) => reply
@ -757,7 +754,6 @@ pub(crate) fn handle_find_elements_link_text(
document.upcast::<Node>(), document.upcast::<Node>(),
selector.clone(), selector.clone(),
partial, partial,
can_gc,
)) ))
.unwrap(), .unwrap(),
Err(error) => reply.send(Err(error)).unwrap(), Err(error) => reply.send(Err(error)).unwrap(),
@ -896,12 +892,11 @@ pub(crate) fn handle_find_element_elements_link_text(
selector: String, selector: String,
partial: bool, partial: bool,
reply: IpcSender<Result<Vec<String>, ErrorStatus>>, reply: IpcSender<Result<Vec<String>, ErrorStatus>>,
can_gc: CanGc,
) { ) {
reply reply
.send( .send(
get_known_element(documents, pipeline, element_id).and_then(|element| { get_known_element(documents, pipeline, element_id).and_then(|element| {
all_matching_links(element.upcast::<Node>(), selector.clone(), partial, can_gc) all_matching_links(element.upcast::<Node>(), selector.clone(), partial)
}), }),
) )
.unwrap(); .unwrap();
@ -986,17 +981,11 @@ pub(crate) fn handle_find_shadow_elements_link_text(
selector: String, selector: String,
partial: bool, partial: bool,
reply: IpcSender<Result<Vec<String>, ErrorStatus>>, reply: IpcSender<Result<Vec<String>, ErrorStatus>>,
can_gc: CanGc,
) { ) {
reply reply
.send( .send(
get_known_shadow_root(documents, pipeline, shadow_root_id).and_then(|shadow_root| { get_known_shadow_root(documents, pipeline, shadow_root_id).and_then(|shadow_root| {
all_matching_links( all_matching_links(shadow_root.upcast::<Node>(), selector.clone(), partial)
shadow_root.upcast::<Node>(),
selector.clone(),
partial,
can_gc,
)
}), }),
) )
.unwrap(); .unwrap();
@ -1540,14 +1529,13 @@ pub(crate) fn handle_get_text(
pipeline: PipelineId, pipeline: PipelineId,
node_id: String, node_id: String,
reply: IpcSender<Result<String, ErrorStatus>>, reply: IpcSender<Result<String, ErrorStatus>>,
can_gc: CanGc,
) { ) {
reply reply
.send( .send(
get_known_element(documents, pipeline, node_id).map(|element| { get_known_element(documents, pipeline, node_id).map(|element| {
element element
.downcast::<HTMLElement>() .downcast::<HTMLElement>()
.map(|htmlelement| htmlelement.InnerText(can_gc).to_string()) .map(|htmlelement| htmlelement.InnerText().to_string())
.unwrap_or_else(|| { .unwrap_or_else(|| {
element element
.upcast::<Node>() .upcast::<Node>()
@ -1652,7 +1640,6 @@ pub(crate) fn handle_get_css(
node_id: String, node_id: String,
name: String, name: String,
reply: IpcSender<Result<String, ErrorStatus>>, reply: IpcSender<Result<String, ErrorStatus>>,
can_gc: CanGc,
) { ) {
reply reply
.send( .send(
@ -1661,7 +1648,7 @@ pub(crate) fn handle_get_css(
String::from( String::from(
window window
.GetComputedStyle(&element, None) .GetComputedStyle(&element, None)
.GetPropertyValue(DOMString::from(name), can_gc), .GetPropertyValue(DOMString::from(name)),
) )
}), }),
) )
@ -1912,7 +1899,7 @@ fn is_element_in_view(element: &Element, document: &Document, can_gc: CanGc) ->
// An element is said to have pointer events disabled // An element is said to have pointer events disabled
// if the resolved value of its "pointer-events" style property is "none". // if the resolved value of its "pointer-events" style property is "none".
let pointer_events_enabled = element let pointer_events_enabled = element
.style(can_gc) .style()
.is_none_or(|style| style.get_inherited_ui().pointer_events != PointerEvents::None); .is_none_or(|style| style.get_inherited_ui().pointer_events != PointerEvents::None);
// An element is in view if it is a member of its own pointer-interactable paint tree, // An element is in view if it is a member of its own pointer-interactable paint tree,
@ -1939,7 +1926,6 @@ fn get_element_pointer_interactable_paint_tree(
Some(center_point) => document.ElementsFromPoint( Some(center_point) => document.ElementsFromPoint(
Finite::wrap(center_point.x as f64), Finite::wrap(center_point.x as f64),
Finite::wrap(center_point.y as f64), Finite::wrap(center_point.y as f64),
can_gc,
), ),
None => Vec::new(), None => Vec::new(),
} }

View file

@ -80,12 +80,8 @@ DOMInterfaces = {
'canGc': ['GetSize'], 'canGc': ['GetSize'],
}, },
'CanvasGradient': {
'canGc': ['AddColorStop'],
},
'CanvasRenderingContext2D': { 'CanvasRenderingContext2D': {
'canGc': ['GetTransform','GetImageData', 'CreateImageData', 'CreateImageData_', 'SetFont', 'FillText', 'MeasureText', 'SetStrokeStyle', 'SetFillStyle', 'SetShadowColor', 'CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient'], 'canGc': ['GetTransform','GetImageData', 'CreateImageData', 'CreateImageData_', 'MeasureText', 'CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient'],
}, },
'CharacterData': { 'CharacterData': {
@ -146,7 +142,7 @@ DOMInterfaces = {
}, },
'CSSStyleDeclaration': { 'CSSStyleDeclaration': {
'canGc': ['RemoveProperty', 'SetCssText', 'GetPropertyValue', 'SetProperty', 'CssFloat', 'SetCssFloat'] 'canGc': ['RemoveProperty', 'SetCssText', 'SetProperty', 'SetCssFloat']
}, },
'CustomElementRegistry': { 'CustomElementRegistry': {
@ -172,7 +168,7 @@ DOMInterfaces = {
'Document': { 'Document': {
'additionalTraits': ["crate::interfaces::DocumentHelpers"], 'additionalTraits': ["crate::interfaces::DocumentHelpers"],
'canGc': ['Close', 'CreateElement', 'CreateElementNS', 'ImportNode', 'SetTitle', 'Write', 'Writeln', 'CreateEvent', 'CreateRange', 'Open', 'Open_', 'CreateComment', 'CreateAttribute', 'CreateAttributeNS', 'CreateDocumentFragment', 'CreateTextNode', 'CreateCDATASection', 'CreateProcessingInstruction', 'Prepend', 'Append', 'ReplaceChildren', 'SetBgColor', 'SetFgColor', 'Fonts', 'ElementFromPoint', 'ElementsFromPoint', 'GetScrollingElement', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate', 'StyleSheets', 'Implementation', 'GetElementsByTagName', 'GetElementsByTagNameNS', 'GetElementsByClassName', 'AdoptNode', 'CreateNodeIterator', 'SetBody', 'GetElementsByName', 'Images', 'Embeds', 'Plugins', 'Links', 'Forms', 'Scripts', 'Anchors', 'Applets', 'Children', 'GetSelection', 'NamedGetter', 'AdoptedStyleSheets'], 'canGc': ['Close', 'CreateElement', 'CreateElementNS', 'ImportNode', 'SetTitle', 'Write', 'Writeln', 'CreateEvent', 'CreateRange', 'Open', 'Open_', 'CreateComment', 'CreateAttribute', 'CreateAttributeNS', 'CreateDocumentFragment', 'CreateTextNode', 'CreateCDATASection', 'CreateProcessingInstruction', 'Prepend', 'Append', 'ReplaceChildren', 'SetBgColor', 'SetFgColor', 'Fonts', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate', 'StyleSheets', 'Implementation', 'GetElementsByTagName', 'GetElementsByTagNameNS', 'GetElementsByClassName', 'AdoptNode', 'CreateNodeIterator', 'SetBody', 'GetElementsByName', 'Images', 'Embeds', 'Plugins', 'Links', 'Forms', 'Scripts', 'Anchors', 'Applets', 'Children', 'GetSelection', 'NamedGetter', 'AdoptedStyleSheets'],
}, },
'DissimilarOriginWindow': { 'DissimilarOriginWindow': {
@ -236,7 +232,7 @@ DOMInterfaces = {
}, },
'Element': { 'Element': {
'canGc': ['SetHTMLUnsafe', 'SetInnerHTML', 'SetOuterHTML', 'InsertAdjacentHTML', 'GetClientRects', 'GetBoundingClientRect', 'InsertAdjacentText', 'ToggleAttribute', 'SetAttribute', 'SetAttributeNS', 'SetId','SetClassName','Prepend','Append','ReplaceChildren','Before','After','ReplaceWith', 'SetRole', 'SetAriaAtomic', 'SetAriaAutoComplete', 'SetAriaBrailleLabel', 'SetAriaBrailleRoleDescription', 'SetAriaBusy', 'SetAriaChecked', 'SetAriaColCount', 'SetAriaColIndex', 'SetAriaColIndexText', 'SetAriaColSpan', 'SetAriaCurrent', 'SetAriaDescription', 'SetAriaDisabled', 'SetAriaExpanded', 'SetAriaHasPopup', 'SetAriaHidden', 'SetAriaInvalid', 'SetAriaKeyShortcuts', 'SetAriaLabel', 'SetAriaLevel', 'SetAriaLive', 'SetAriaModal', 'SetAriaMultiLine', 'SetAriaMultiSelectable', 'SetAriaOrientation', 'SetAriaPlaceholder', 'SetAriaPosInSet', 'SetAriaPressed','SetAriaReadOnly', 'SetAriaRelevant', 'SetAriaRequired', 'SetAriaRoleDescription', 'SetAriaRowCount', 'SetAriaRowIndex', 'SetAriaRowIndexText', 'SetAriaRowSpan', 'SetAriaSelected', 'SetAriaSetSize','SetAriaSort', 'SetAriaValueMax', 'SetAriaValueMin', 'SetAriaValueNow', 'SetAriaValueText', 'SetScrollTop', 'SetScrollLeft', 'Scroll', 'Scroll_', 'ScrollBy', 'ScrollBy_', 'ScrollWidth', 'ScrollHeight', 'ScrollTop', 'ScrollLeft', 'ClientTop', 'ClientLeft', 'ClientWidth', 'ClientHeight', 'RequestFullscreen', 'GetHTML', 'GetInnerHTML', 'GetOuterHTML', 'ClassList', 'Attributes', 'SetAttributeNode', 'SetAttributeNodeNS', 'RemoveAttribute', 'RemoveAttributeNS', 'RemoveAttributeNode', 'GetElementsByTagName', 'GetElementsByTagNameNS', 'GetElementsByClassName', 'ScrollTo', 'ScrollTo_', 'Children', 'Remove', 'InsertAdjacentElement', 'AttachShadow'], 'canGc': ['SetHTMLUnsafe', 'SetInnerHTML', 'SetOuterHTML', 'InsertAdjacentHTML', 'GetClientRects', 'GetBoundingClientRect', 'InsertAdjacentText', 'ToggleAttribute', 'SetAttribute', 'SetAttributeNS', 'SetId','SetClassName','Prepend','Append','ReplaceChildren','Before','After','ReplaceWith', 'SetRole', 'SetAriaAtomic', 'SetAriaAutoComplete', 'SetAriaBrailleLabel', 'SetAriaBrailleRoleDescription', 'SetAriaBusy', 'SetAriaChecked', 'SetAriaColCount', 'SetAriaColIndex', 'SetAriaColIndexText', 'SetAriaColSpan', 'SetAriaCurrent', 'SetAriaDescription', 'SetAriaDisabled', 'SetAriaExpanded', 'SetAriaHasPopup', 'SetAriaHidden', 'SetAriaInvalid', 'SetAriaKeyShortcuts', 'SetAriaLabel', 'SetAriaLevel', 'SetAriaLive', 'SetAriaModal', 'SetAriaMultiLine', 'SetAriaMultiSelectable', 'SetAriaOrientation', 'SetAriaPlaceholder', 'SetAriaPosInSet', 'SetAriaPressed','SetAriaReadOnly', 'SetAriaRelevant', 'SetAriaRequired', 'SetAriaRoleDescription', 'SetAriaRowCount', 'SetAriaRowIndex', 'SetAriaRowIndexText', 'SetAriaRowSpan', 'SetAriaSelected', 'SetAriaSetSize','SetAriaSort', 'SetAriaValueMax', 'SetAriaValueMin', 'SetAriaValueNow', 'SetAriaValueText', 'RequestFullscreen', 'GetHTML', 'GetInnerHTML', 'GetOuterHTML', 'ClassList', 'Attributes', 'SetAttributeNode', 'SetAttributeNodeNS', 'RemoveAttribute', 'RemoveAttributeNS', 'RemoveAttributeNode', 'GetElementsByTagName', 'GetElementsByTagNameNS', 'GetElementsByClassName', 'Children', 'Remove', 'InsertAdjacentElement', 'AttachShadow'],
}, },
'ElementInternals': { 'ElementInternals': {
@ -362,7 +358,7 @@ DOMInterfaces = {
}, },
'HTMLElement': { 'HTMLElement': {
'canGc': ['AttachInternals', 'Focus', 'Blur', 'Click', 'SetInnerText', 'SetOuterText', "SetTranslate", 'SetAutofocus', 'GetOffsetParent', 'OffsetTop', 'OffsetLeft', 'OffsetWidth', 'OffsetHeight', 'InnerText', 'GetOuterText', 'GetOnerror', 'GetOnload', 'GetOnblur', 'GetOnfocus', 'GetOnresize', 'GetOnscroll', 'Style', 'Dataset'], 'canGc': ['AttachInternals', 'Focus', 'Blur', 'Click', 'SetInnerText', 'SetOuterText', "SetTranslate", 'SetAutofocus', 'GetOnerror', 'GetOnload', 'GetOnblur', 'GetOnfocus', 'GetOnresize', 'GetOnscroll', 'Style', 'Dataset'],
}, },
'HTMLFieldSetElement': { 'HTMLFieldSetElement': {
@ -386,7 +382,7 @@ DOMInterfaces = {
}, },
'HTMLImageElement': { 'HTMLImageElement': {
'canGc': ['RequestSubmit', 'ReportValidity', 'Reset','SetRel', 'Width', 'Height', 'Decode', 'SetCrossOrigin', 'SetWidth', 'SetHeight', 'SetReferrerPolicy'], 'canGc': ['RequestSubmit', 'ReportValidity', 'Reset','SetRel', 'Decode', 'SetCrossOrigin', 'SetWidth', 'SetHeight', 'SetReferrerPolicy'],
}, },
'HTMLInputElement': { 'HTMLInputElement': {
@ -427,7 +423,7 @@ DOMInterfaces = {
}, },
'HTMLScriptElement': { 'HTMLScriptElement': {
'canGc': ['InnerText', 'SetAsync', 'SetCrossOrigin', 'SetInnerText', 'SetSrc', 'SetText', 'SetTextContent'] 'canGc': ['SetAsync', 'SetCrossOrigin', 'SetInnerText', 'SetSrc', 'SetText', 'SetTextContent']
}, },
'HTMLSelectElement': { 'HTMLSelectElement': {
@ -492,10 +488,6 @@ DOMInterfaces = {
'canGc': ['Ports'], 'canGc': ['Ports'],
}, },
'MouseEvent': {
'canGc': ['InitMouseEvent', 'OffsetX', 'OffsetY'],
},
'NavigationPreloadManager': { 'NavigationPreloadManager': {
'inRealms': ['Disable', 'Enable', 'GetState', 'SetHeaderValue'], 'inRealms': ['Disable', 'Enable', 'GetState', 'SetHeaderValue'],
'canGc': ['Disable', 'Enable', 'GetState', 'SetHeaderValue'], 'canGc': ['Disable', 'Enable', 'GetState', 'SetHeaderValue'],
@ -528,11 +520,11 @@ DOMInterfaces = {
}, },
'OffscreenCanvasRenderingContext2D': { 'OffscreenCanvasRenderingContext2D': {
'canGc': ['CreateImageData', 'CreateImageData_', 'GetImageData', 'GetTransform', 'SetFont', 'FillText', 'MeasureText', 'SetStrokeStyle', 'SetFillStyle', 'SetShadowColor', 'CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient'], 'canGc': ['CreateImageData', 'CreateImageData_', 'GetImageData', 'GetTransform', 'MeasureText', 'CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient'],
}, },
'PaintRenderingContext2D': { 'PaintRenderingContext2D': {
'canGc': ['GetTransform', 'SetStrokeStyle', 'SetFillStyle', 'SetShadowColor'], 'canGc': ['GetTransform'],
}, },
'Performance': { 'Performance': {
@ -591,7 +583,7 @@ DOMInterfaces = {
}, },
'ShadowRoot': { 'ShadowRoot': {
'canGc': ['SetHTMLUnsafe', 'ElementFromPoint', 'ElementsFromPoint', 'SetInnerHTML', 'GetHTML', 'InnerHTML', 'AdoptedStyleSheets'], 'canGc': ['SetHTMLUnsafe', 'SetInnerHTML', 'GetHTML', 'InnerHTML', 'AdoptedStyleSheets'],
}, },
'StaticRange': { 'StaticRange': {
@ -654,12 +646,8 @@ DOMInterfaces = {
'additionalTraits': ['crate::interfaces::WebGL2RenderingContextHelpers'], 'additionalTraits': ['crate::interfaces::WebGL2RenderingContextHelpers'],
}, },
'WheelEvent': {
'canGc': ['InitWheelEvent'],
},
'Window': { 'Window': {
'canGc': ['Stop', 'Fetch', 'Scroll', 'Scroll_','ScrollBy', 'ScrollBy_', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap', 'CreateImageBitmap_', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'], 'canGc': ['Stop', 'Fetch', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap', 'CreateImageBitmap_', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'],
'inRealms': ['Fetch', 'GetOpener', 'WebdriverCallback', 'WebdriverException'], 'inRealms': ['Fetch', 'GetOpener', 'WebdriverCallback', 'WebdriverException'],
'additionalTraits': ['crate::interfaces::WindowHelpers'], 'additionalTraits': ['crate::interfaces::WindowHelpers'],
}, },

View file

@ -259,6 +259,9 @@ pub trait Layout {
/// not exist in the tree. /// not exist in the tree.
fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>; fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
/// Returns true if this layout needs to produce a new display list for rendering updates.
fn needs_new_display_list(&self) -> bool;
fn query_content_box(&self, node: TrustedNodeAddress) -> Option<Rect<Au>>; fn query_content_box(&self, node: TrustedNodeAddress) -> Option<Rect<Au>>;
fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec<Rect<Au>>; fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec<Rect<Au>>;
fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>; fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>;

View file

@ -1,3 +0,0 @@
[layer-statement-before-import.html]
[insert other rules before the first layer statement without imports]
expected: FAIL

View file

@ -12595,7 +12595,7 @@
] ]
], ],
"basic-transition.html": [ "basic-transition.html": [
"b80e8a666a6e6202b4ecafe628ef00ebcecfe168", "c3461d3a5f9f1259296168742028db3bd4fc3668",
[ [
null, null,
{} {}
@ -12623,7 +12623,7 @@
] ]
], ],
"transition-raf.html": [ "transition-raf.html": [
"c38404503408e04b3c75b42df18ec3a7ec0819f5", "aa3ed54e3e08a190f227c87165523306aed5a6bc",
[ [
null, null,
{} {}
@ -12743,7 +12743,7 @@
] ]
], ],
"stylesheet_media_queries.html": [ "stylesheet_media_queries.html": [
"49956367a16c3de98d173d4cf5692c05451340a0", "d04eb4b23f107b1e4d127cf633d4155ab3bdf629",
[ [
null, null,
{} {}

View file

@ -20,15 +20,26 @@ var div = document.getElementById('check-me');
var span = div.childNodes[0]; var span = div.childNodes[0];
async_test(function(t) { async_test(function(t) {
window.addEventListener('load', function() { window.addEventListener('load', function() {
assert_equals(getComputedStyle(div).getPropertyValue('background-color'), 'rgb(255, 0, 0)');
div.id = "check-me-2";
requestAnimationFrame(function() {
var test = new window.TestBinding(); var test = new window.TestBinding();
div.addEventListener("transitionstart", () => {
// The transition should have just started so the current value of the style should
// not be the value expected after the transition.
assert_not_equals(getComputedStyle(div).getPropertyValue('background-color'), 'rgb(0, 0, 0)');
test.advanceClock(2000); test.advanceClock(2000);
span.innerHTML = "a"; });
div.addEventListener("transitionend", () => {
// The transition should be finished so the value of the style should be the final one.
assert_equals(getComputedStyle(div).getPropertyValue('background-color'), 'rgb(0, 0, 0)'); assert_equals(getComputedStyle(div).getPropertyValue('background-color'), 'rgb(0, 0, 0)');
t.done(); t.done();
}); });
// The starting value should be the one set in the style.
assert_equals(getComputedStyle(div).getPropertyValue('background-color'), 'rgb(255, 0, 0)');
// Start the transition.
div.id = "check-me-2";
}) })
}) })
</script> </script>

View file

@ -32,9 +32,12 @@ async_test(function(t) {
window.addEventListener('load', function() { window.addEventListener('load', function() {
assert_equals(getComputedStyle(box).getPropertyValue('width'), '100px'); assert_equals(getComputedStyle(box).getPropertyValue('width'), '100px');
box.className = "expose";
box.addEventListener("transitionstart", () => {
// Let the first restyle run at zero, then advance the clock. // Let the first restyle run at zero, then advance the clock.
setTimeout(function() { test.advanceClock(500) }, 0); test.advanceClock(500);
});
box.className = "expose";
}); });
}, "Transitions should work during RAF loop") }, "Transitions should work during RAF loop")
</script> </script>

View file

@ -14,8 +14,10 @@ window.onload = test.step_func(function() {
assert_equals(frame.contentWindow.getComputedStyle(element).backgroundColor, "rgb(255, 0, 0)"); assert_equals(frame.contentWindow.getComputedStyle(element).backgroundColor, "rgb(255, 0, 0)");
frame.width = "300"; frame.width = "300";
frameDoc.documentElement.offsetWidth; // Force layout frameDoc.documentElement.offsetWidth; // Force layout
window.requestAnimationFrame(() => {
window.requestAnimationFrame(test.step_func_done(function () { window.requestAnimationFrame(test.step_func_done(function () {
assert_equals(frame.contentWindow.getComputedStyle(element).backgroundColor, "rgb(0, 255, 0)"); assert_equals(frame.contentWindow.getComputedStyle(element).backgroundColor, "rgb(0, 255, 0)");
})); }))
});
}); });
</script> </script>