mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
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:
parent
fed1953198
commit
48e14be7ff
40 changed files with 566 additions and 654 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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,23 +1447,17 @@ 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
|
};
|
||||||
};
|
|
||||||
self.state.borrow_mut().font_style = Some((*resolved_font_style).clone());
|
self.state.borrow_mut().font_style = Some((*resolved_font_style).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 element’s parent element
|
// For this purpose, a value of overflow:clip on the the body element’s 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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| {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// " - body’s parent element’s computed value of the overflow-x or
|
// " - body’s parent element’s 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 {
|
||||||
|
|
||||||
// " - body’s computed value of the overflow-x or overflow-y properties
|
// " - body’s 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>()) ||
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 event’s dispatch flag is set, return the x-coordinate of the position
|
// > 1. If the event’s 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 event’s pageX attribute.
|
// > 2. Return the value of the event’s 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 event’s dispatch flag is set, return the y-coordinate of the
|
// > 1. If the event’s 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 event’s pageY attribute.
|
// 2. Return the value of the event’s 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,
|
||||||
|
|
|
@ -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 element’s node document.""
|
// "1. Let document be the element’s 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 element’s scrolling area."
|
// "7. Return the width of the element’s 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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 observer’s [[activeTargets]], and [[skippedTargets]].
|
// Step 2.1 Clear observer’s [[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)
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
// Checks if the html element has reftest-wait attribute present.
|
||||||
// > - the document is still loading
|
// See http://testthewebforward.org/docs/reftests.html
|
||||||
// > - the document has pending stylesheet requests
|
// and https://web-platform-tests.org/writing-tests/crashtest.html
|
||||||
// > - the document has pending layout operations which might cause the user agent to request
|
if document.GetDocumentElement().is_some_and(|elem| {
|
||||||
// > a font, or which depend on recently-loaded fonts
|
elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
|
||||||
//
|
elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
|
||||||
// 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
|
return;
|
||||||
// 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 self.font_context.web_fonts_still_loading() != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.pending_layout_images.borrow().is_empty() ||
|
||||||
|
!self.pending_images_for_rasterization.borrow().is_empty()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.Document().needs_rendering_update() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// When all these conditions are met, notify the constellation
|
||||||
// that this pipeline is ready to write the image (from the script thread
|
// that this pipeline is ready to write the image (from the script thread
|
||||||
// perspective at least).
|
// perspective at least).
|
||||||
if opts::get().wait_for_stable_image && updating_the_rendering {
|
debug!(
|
||||||
// Checks if the html element has reftest-wait attribute present.
|
"{:?}: Sending DocumentState::Idle to Constellation",
|
||||||
// See http://testthewebforward.org/docs/reftests.html
|
self.pipeline_id()
|
||||||
// and https://web-platform-tests.org/writing-tests/crashtest.html
|
);
|
||||||
let html_element = document.GetDocumentElement();
|
self.send_to_constellation(ScriptToConstellationMessage::SetDocumentState(
|
||||||
let reftest_wait = html_element.is_some_and(|elem| {
|
DocumentState::Idle,
|
||||||
elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
|
));
|
||||||
elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
|
self.has_sent_idle_message.set(true);
|
||||||
});
|
|
||||||
|
|
||||||
let has_sent_idle_message = self.has_sent_idle_message.get();
|
|
||||||
let no_pending_images = self.pending_layout_images.borrow().is_empty() &&
|
|
||||||
self.pending_images_for_rasterization.borrow().is_empty();
|
|
||||||
|
|
||||||
if !has_sent_idle_message &&
|
|
||||||
is_ready_state_complete &&
|
|
||||||
!reftest_wait &&
|
|
||||||
no_pending_images &&
|
|
||||||
!waiting_for_web_fonts_to_load
|
|
||||||
{
|
|
||||||
debug!(
|
|
||||||
"{:?}: Sending DocumentState::Idle to Constellation",
|
|
||||||
self.pipeline_id()
|
|
||||||
);
|
|
||||||
let event = ScriptToConstellationMessage::SetDocumentState(DocumentState::Idle);
|
|
||||||
self.send_to_constellation(event);
|
|
||||||
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),
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,37 +1208,50 @@ impl ScriptThread {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cancel_scheduled_update_the_rendering(&self) {
|
||||||
|
if let Some(timer_id) = self
|
||||||
|
.scheduled_script_thread_animation_timer
|
||||||
|
.borrow_mut()
|
||||||
|
.take()
|
||||||
|
{
|
||||||
|
self.timer_scheduler.borrow_mut().cancel_timer(timer_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule_update_the_rendering_timer_if_necessary(&self, delay: Duration) {
|
||||||
|
if self
|
||||||
|
.scheduled_script_thread_animation_timer
|
||||||
|
.borrow()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Scheduling ScriptThread animation frame.");
|
||||||
|
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>
|
/// <https://html.spec.whatwg.org/multipage/#update-the-rendering>
|
||||||
///
|
///
|
||||||
/// Attempt to update the rendering and then do a microtask checkpoint if rendering was actually
|
/// Attempt to update the rendering and then do a microtask checkpoint if rendering was actually
|
||||||
/// updated.
|
/// updated.
|
||||||
pub(crate) fn update_the_rendering(&self, can_gc: CanGc) {
|
pub(crate) fn update_the_rendering(&self, can_gc: CanGc) {
|
||||||
*self.last_render_opportunity_time.borrow_mut() = Some(Instant::now());
|
self.last_render_opportunity_time.set(Some(Instant::now()));
|
||||||
|
self.cancel_scheduled_update_the_rendering();
|
||||||
let is_animation_tick = self.has_pending_animation_tick.load(Ordering::Relaxed);
|
self.needs_rendering_update.store(false, 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
|
|
||||||
.scheduled_script_thread_animation_timer
|
|
||||||
.borrow_mut()
|
|
||||||
.take()
|
|
||||||
{
|
|
||||||
self.timer_scheduler.borrow_mut().cancel_timer(timer_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.can_continue_running_inner() {
|
if !self.can_continue_running_inner() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let any_animations_running = self.documents.borrow().iter().any(|(_, document)| {
|
|
||||||
document.is_fully_active() && document.animations().running_animation_count() != 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are any pending reflows and we are not having rendering opportunities
|
fn maybe_schedule_rendering_opportunity_after_rendering_update(&self, saw_any_reflows: bool) {
|
||||||
// driven by the compositor, then schedule the next rendering opportunity.
|
// If there are any pending reflows and we are not having rendering opportunities
|
||||||
//
|
// 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.is_fully_active() &&
|
||||||
!document.restyle_reason().is_empty()
|
!document.window().throttled() &&
|
||||||
}) else {
|
(document.animations().running_animation_count() != 0 ||
|
||||||
return;
|
document.has_active_request_animation_frame_callbacks())
|
||||||
};
|
})
|
||||||
|
{
|
||||||
// Queues a task to update the rendering.
|
const SCRIPT_THREAD_ANIMATION_TICK_DELAY: Duration = Duration::from_millis(30);
|
||||||
// <https://html.spec.whatwg.org/multipage/#event-loop-processing-model:queue-a-global-task>
|
self.schedule_update_the_rendering_timer_if_necessary(
|
||||||
//
|
SCRIPT_THREAD_ANIMATION_TICK_DELAY,
|
||||||
// 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
|
fn maybe_schedule_rendering_opportunity_after_ipc_message(&self) {
|
||||||
/// display lists. In the case that a `WebView` is animating or has a
|
// If no document needs a rendering update, exit early to avoid doing more work.
|
||||||
/// requestAnimationFrame callback, it may be that an animation tick reflow does
|
if !self
|
||||||
/// not change anything and thus does not send a new display list to the renderer.
|
.documents
|
||||||
/// If that's the case, we need to schedule a ScriptThread-based animation update
|
.borrow()
|
||||||
/// (to avoid waking the renderer up).
|
.iter()
|
||||||
fn schedule_script_thread_animation_tick_if_necessary(&self, saw_any_reflows: bool) {
|
.any(|(_, document)| document.needs_rendering_update())
|
||||||
if saw_any_reflows {
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always schedule a ScriptThread-based animation tick, unless none of the
|
// Wait 20 milliseconds between frames triggered by the script thread itself. This
|
||||||
// documents are active and have animations running and/or rAF callbacks.
|
// should, in theory, allow compositor-based ticks to arrive sooner.
|
||||||
if !self.documents.borrow().iter().any(|(_, document)| {
|
const SCRIPT_THREAD_ANIMATION_TICK_DELAY: Duration = Duration::from_millis(20);
|
||||||
document.is_fully_active() &&
|
let time_since_last_rendering_opportunity = self
|
||||||
!document.window().throttled() &&
|
.last_render_opportunity_time
|
||||||
(document.animations().running_animation_count() != 0 ||
|
.get()
|
||||||
document.has_active_request_animation_frame_callbacks())
|
.map(|last_render_opportunity_time| Instant::now() - last_render_opportunity_time)
|
||||||
}) {
|
.unwrap_or(Duration::MAX)
|
||||||
return;
|
.min(SCRIPT_THREAD_ANIMATION_TICK_DELAY);
|
||||||
}
|
|
||||||
|
|
||||||
/// The amount of time between ScriptThread animation ticks when nothing is
|
//eprintln!(" - scheduling update\n");
|
||||||
/// changing. In order to be more efficient, only tick at around 30 frames a
|
//// If it's been more than the time of a single frame the last rendering opportunity,
|
||||||
/// second, which also gives time for any renderer ticks to come in and cancel
|
//// just run it now.
|
||||||
/// this tick. A renderer tick might happen for a variety of reasons, such as a
|
//if time_since_last_rendering_opportunity > SCRIPT_THREAD_ANIMATION_TICK_DELAY {
|
||||||
/// Pipeline in another ScriptThread producing a display list.
|
// println!(" - running immediately");
|
||||||
const SCRIPT_THREAD_ANIMATION_TICK_DELAY: u64 = 30;
|
// self.update_the_rendering(can_gc);
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
debug!("Scheduling ScriptThread animation frame.");
|
self.schedule_update_the_rendering_timer_if_necessary(
|
||||||
let trigger_script_thread_animation = self.has_pending_animation_tick.clone();
|
SCRIPT_THREAD_ANIMATION_TICK_DELAY - time_since_last_rendering_opportunity,
|
||||||
let timer_id = self.schedule_timer(TimerEventRequest {
|
|
||||||
callback: Box::new(move || {
|
|
||||||
trigger_script_thread_animation.store(true, Ordering::Relaxed);
|
|
||||||
}),
|
|
||||||
duration: Duration::from_millis(SCRIPT_THREAD_ANIMATION_TICK_DELAY),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut scheduled_script_thread_animation_timer =
|
|
||||||
self.scheduled_script_thread_animation_timer.borrow_mut();
|
|
||||||
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
|
self.update_the_rendering(can_gc);
|
||||||
// message.
|
}
|
||||||
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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'],
|
||||||
},
|
},
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[layer-statement-before-import.html]
|
|
||||||
[insert other rules before the first layer statement without imports]
|
|
||||||
expected: FAIL
|
|
6
tests/wpt/mozilla/meta/MANIFEST.json
vendored
6
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -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,
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -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)');
|
var test = new window.TestBinding();
|
||||||
div.id = "check-me-2";
|
|
||||||
requestAnimationFrame(function() {
|
div.addEventListener("transitionstart", () => {
|
||||||
var test = new window.TestBinding();
|
// 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>
|
||||||
|
|
|
@ -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.addEventListener("transitionstart", () => {
|
||||||
|
// Let the first restyle run at zero, then advance the clock.
|
||||||
|
test.advanceClock(500);
|
||||||
|
});
|
||||||
box.className = "expose";
|
box.className = "expose";
|
||||||
// Let the first restyle run at zero, then advance the clock.
|
|
||||||
setTimeout(function() { test.advanceClock(500) }, 0);
|
|
||||||
});
|
});
|
||||||
}, "Transitions should work during RAF loop")
|
}, "Transitions should work during RAF loop")
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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(test.step_func_done(function () {
|
window.requestAnimationFrame(() => {
|
||||||
|
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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue