aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ohos.yml15
-rw-r--r--Cargo.lock42
-rw-r--r--components/compositing/compositor.rs29
-rw-r--r--components/compositing/webview_renderer.rs14
-rw-r--r--components/constellation/constellation.rs30
-rw-r--r--components/constellation/tracing.rs1
-rw-r--r--components/fonts/platform/windows/font.rs4
-rw-r--r--components/fonts/platform/windows/font_list.rs22
-rw-r--r--components/layout/construct_modern.rs1
-rw-r--r--components/layout/display_list/mod.rs87
-rw-r--r--components/layout/display_list/stacking_context.rs76
-rw-r--r--components/layout/flexbox/layout.rs30
-rw-r--r--components/layout/flexbox/mod.rs3
-rw-r--r--components/layout/flow/construct.rs31
-rw-r--r--components/layout/flow/float.rs3
-rw-r--r--components/layout/flow/inline/construct.rs5
-rw-r--r--components/layout/flow/inline/inline_box.rs8
-rw-r--r--components/layout/flow/inline/line.rs3
-rw-r--r--components/layout/flow/inline/mod.rs19
-rw-r--r--components/layout/flow/root.rs2
-rw-r--r--components/layout/fragment_tree/fragment.rs4
-rw-r--r--components/layout/layout_impl.rs2
-rw-r--r--components/layout/lib.rs20
-rw-r--r--components/layout/table/construct.rs13
-rw-r--r--components/layout/taffy/mod.rs3
-rw-r--r--components/layout/traversal.rs12
-rw-r--r--components/net/image_cache.rs4
-rw-r--r--components/pixels/lib.rs90
-rw-r--r--components/script/canvas_state.rs4
-rw-r--r--components/script/dom/headers.rs258
-rw-r--r--components/script/dom/htmlcanvaselement.rs8
-rw-r--r--components/script/dom/htmlinputelement.rs4
-rw-r--r--components/script/dom/htmlmediaelement.rs4
-rw-r--r--components/script/dom/htmltablecellelement.rs16
-rw-r--r--components/script/dom/htmltablecolelement.rs14
-rw-r--r--components/script/dom/webglrenderingcontext.rs3
-rw-r--r--components/script/dom/webgpu/gpu.rs8
-rw-r--r--components/script/script_thread.rs38
-rw-r--r--components/script/test.rs1
-rw-r--r--components/shared/compositing/lib.rs12
-rw-r--r--components/shared/constellation/from_script_message.rs3
-rw-r--r--components/shared/embedder/input_events.rs56
-rw-r--r--components/shared/embedder/webdriver.rs26
-rw-r--r--components/webdriver_server/actions.rs250
-rw-r--r--components/webdriver_server/lib.rs114
-rw-r--r--ports/servoshell/Cargo.toml2
-rw-r--r--ports/servoshell/desktop/headed_window.rs8
-rw-r--r--ports/servoshell/egl/app_state.rs32
-rw-r--r--support/hitrace-bencher/runs.json45
-rw-r--r--tests/unit/script/headers.rs75
-rw-r--r--tests/unit/script/lib.rs2
-rw-r--r--tests/wpt/meta/css/CSS2/generated-content/content-070.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/text/text-decoration-va-length-001.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/text/text-decoration-va-length-002.xht.ini2
-rw-r--r--tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini9
-rw-r--r--tests/wpt/meta/css/css-flexbox/intrinsic-size/row-wrap-001.html.ini6
-rw-r--r--tests/wpt/meta/css/css-text-decor/text-decoration-decorating-box-001.html.ini2
-rw-r--r--tests/wpt/meta/css/css-text-decor/text-decoration-dotted-002.html.ini2
-rw-r--r--tests/wpt/meta/css/css-text-decor/text-decoration-propagation-display-contents.html.ini2
-rw-r--r--tests/wpt/meta/css/css-text-decor/text-decoration-style-multiple.html.ini2
-rw-r--r--tests/wpt/meta/css/css-text-decor/text-shadow/decorations-multiple-zorder.html.ini2
-rw-r--r--tests/wpt/meta/fetch/api/request/request-headers.any.js.ini16
-rw-r--r--tests/wpt/meta/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm.ini3
63 files changed, 981 insertions, 625 deletions
diff --git a/.github/workflows/ohos.yml b/.github/workflows/ohos.yml
index 404e305f61b..f2c8233a785 100644
--- a/.github/workflows/ohos.yml
+++ b/.github/workflows/ohos.yml
@@ -169,11 +169,16 @@ jobs:
- name: Build for aarch64 HarmonyOS
run: |
./mach build --locked --target aarch64-unknown-linux-ohos --profile=${{ inputs.profile }} --flavor=harmonyos --no-default-features --features tracing,tracing-hitrace
+ - name: Upload supprt/hitrace-bencher/runs.json
+ uses: actions/upload-artifact@v4
+ with:
+ name: runs.json
+ path: support/hitrace-bencher/runs.json
- uses: actions/upload-artifact@v4
with:
# Upload the **unsigned** artifact - We don't have the signing materials in pull request workflows
- path: target/openharmony/aarch64-unknown-linux-ohos/${{ inputs.profile }}/entry/build/harmonyos/outputs/default/servoshell-default-unsigned.hap
name: servoshell-hos-${{ inputs.profile }}.hap
+ path: target/openharmony/aarch64-unknown-linux-ohos/${{ inputs.profile }}/entry/build/harmonyos/outputs/default/servoshell-default-unsigned.hap
test-harmonyos-aarch64:
@@ -239,8 +244,14 @@ jobs:
[[ $servo_pid =~ ^[0-9]+$ ]] || { echo "It looks like servo crashed!" ; exit 1; }
# If the grep fails, then the trace output for the "page loaded" prompt is missing
grep 'org\.servo\.servo-.* tracing_mark_write.*PageLoadEndedPrompt' test_output/servo.ftrace
+ - name: Getting runs file
+ uses: actions/download-artifact@v4
+ with:
+ # Name of the artifact to download.
+ # If unspecified, all artifacts for the run are downloaded.
+ name: runs.json
- name: "Run benchmark"
- run: hitrace-bench --bencher -b "org.servo.servo" -p "https://www.servo.org" -n 5
+ run: hitrace-bench -r runs.json
- name: Getting bencher
uses: bencherdev/bencher@main
- name: Getting model name
diff --git a/Cargo.lock b/Cargo.lock
index 818239507c2..d7aa149ecdd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1065,7 +1065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -4043,9 +4043,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
-version = "0.2.13"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
+checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
dependencies = [
"jiff-static",
"log",
@@ -4056,9 +4056,9 @@ dependencies = [
[[package]]
name = "jiff-static"
-version = "0.2.13"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
+checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
dependencies = [
"proc-macro2",
"quote",
@@ -4261,7 +4261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -6627,7 +6627,7 @@ dependencies = [
[[package]]
name = "servo-media"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"once_cell",
"servo-media-audio",
@@ -6640,7 +6640,7 @@ dependencies = [
[[package]]
name = "servo-media-audio"
version = "0.2.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"byte-slice-cast",
"euclid",
@@ -6661,7 +6661,7 @@ dependencies = [
[[package]]
name = "servo-media-derive"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"proc-macro2",
"quote",
@@ -6671,7 +6671,7 @@ dependencies = [
[[package]]
name = "servo-media-dummy"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"ipc-channel",
"servo-media",
@@ -6685,7 +6685,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"byte-slice-cast",
"glib",
@@ -6718,7 +6718,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer-render"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"gstreamer",
"gstreamer-video",
@@ -6728,7 +6728,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer-render-android"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"glib",
"gstreamer",
@@ -6742,7 +6742,7 @@ dependencies = [
[[package]]
name = "servo-media-gstreamer-render-unix"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"glib",
"gstreamer",
@@ -6757,7 +6757,7 @@ dependencies = [
[[package]]
name = "servo-media-player"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"ipc-channel",
"serde",
@@ -6769,7 +6769,7 @@ dependencies = [
[[package]]
name = "servo-media-streams"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"uuid",
]
@@ -6777,12 +6777,12 @@ dependencies = [
[[package]]
name = "servo-media-traits"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
[[package]]
name = "servo-media-webrtc"
version = "0.1.0"
-source = "git+https://github.com/servo/media#eb96030cdd153ebcbbe3836dc6471303187740e9"
+source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0"
dependencies = [
"log",
"servo-media-streams",
@@ -8909,7 +8909,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -9257,9 +9257,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winit"
-version = "0.30.10"
+version = "0.30.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e"
+checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4"
dependencies = [
"ahash",
"android-activity",
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 74ada7b21c6..855f60e57b2 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -620,29 +620,37 @@ impl IOCompositor {
}
},
- CompositorMsg::WebDriverMouseButtonEvent(webview_id, action, button, x, y) => {
+ CompositorMsg::WebDriverMouseButtonEvent(
+ webview_id,
+ action,
+ button,
+ x,
+ y,
+ message_id,
+ ) => {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}");
return;
};
let dppx = webview_renderer.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
- webview_renderer.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
- point,
- action,
- button,
- }));
+ webview_renderer.dispatch_input_event(
+ InputEvent::MouseButton(MouseButtonEvent::new(action, button, point))
+ .with_webdriver_message_id(Some(message_id)),
+ );
},
- CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y) => {
+ CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y, message_id) => {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
warn!("Handling input event for unknown webview: {webview_id}");
return;
};
let dppx = webview_renderer.device_pixels_per_page_pixel();
let point = dppx.transform_point(Point2D::new(x, y));
- webview_renderer
- .dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
+ webview_renderer.dispatch_input_event(
+ InputEvent::MouseMove(MouseMoveEvent::new(point))
+ .with_webdriver_message_id(Some(message_id)),
+ );
},
CompositorMsg::WebDriverWheelScrollEvent(webview_id, x, y, delta_x, delta_y) => {
@@ -1456,10 +1464,11 @@ impl IOCompositor {
format: PixelFormat::RGBA8,
frames: vec![ImageFrame {
delay: None,
- bytes: ipc::IpcSharedMemory::from_bytes(&image),
+ byte_range: 0..image.len(),
width: image.width(),
height: image.height(),
}],
+ bytes: ipc::IpcSharedMemory::from_bytes(&image),
id: None,
cors_status: CorsStatus::Safe,
}))
diff --git a/components/compositing/webview_renderer.rs b/components/compositing/webview_renderer.rs
index 0a6bdf9ae0a..b0e91ccb02e 100644
--- a/components/compositing/webview_renderer.rs
+++ b/components/compositing/webview_renderer.rs
@@ -687,17 +687,17 @@ impl WebViewRenderer {
/// <http://w3c.github.io/touch-events/#mouse-events>
fn simulate_mouse_click(&mut self, point: DevicePoint) {
let button = MouseButton::Left;
- self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
- self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
+ self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent::new(point)));
+ self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ MouseButtonAction::Down,
button,
- action: MouseButtonAction::Down,
point,
- }));
- self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
+ )));
+ self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ MouseButtonAction::Up,
button,
- action: MouseButtonAction::Up,
point,
- }));
+ )));
}
pub(crate) fn notify_scroll_event(
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 5db37800c42..1816cf05373 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -132,7 +132,7 @@ use embedder_traits::{
FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError,
JavaScriptEvaluationId, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState,
MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg,
- WebDriverLoadStatus,
+ WebDriverCommandResponse, WebDriverLoadStatus,
};
use euclid::Size2D;
use euclid::default::Size2D as UntypedSize2D;
@@ -532,6 +532,8 @@ pub struct InitialConstellationState {
struct WebDriverData {
load_channel: Option<(PipelineId, IpcSender<WebDriverLoadStatus>)>,
resize_channel: Option<IpcSender<Size2D<f32, CSSPixel>>>,
+ // Forward responses from the script thread to the webdriver server.
+ input_command_response_sender: Option<IpcSender<WebDriverCommandResponse>>,
}
impl WebDriverData {
@@ -539,6 +541,7 @@ impl WebDriverData {
WebDriverData {
load_channel: None,
resize_channel: None,
+ input_command_response_sender: None,
}
}
}
@@ -1867,6 +1870,18 @@ where
ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result) => {
self.handle_finish_javascript_evaluation(evaluation_id, result)
},
+ ScriptToConstellationMessage::WebDriverInputComplete(msg_id) => {
+ if let Some(ref reply_sender) = self.webdriver.input_command_response_sender {
+ reply_sender
+ .send(WebDriverCommandResponse { id: msg_id })
+ .unwrap_or_else(|_| {
+ warn!("Failed to send WebDriverInputComplete {:?}", msg_id);
+ self.webdriver.input_command_response_sender = None;
+ });
+ } else {
+ warn!("No WebDriver input_command_response_sender");
+ }
+ },
}
}
@@ -4836,7 +4851,11 @@ where
mouse_button,
x,
y,
+ msg_id,
+ response_sender,
) => {
+ self.webdriver.input_command_response_sender = Some(response_sender);
+
self.compositor_proxy
.send(CompositorMsg::WebDriverMouseButtonEvent(
webview_id,
@@ -4844,11 +4863,16 @@ where
mouse_button,
x,
y,
+ msg_id,
));
},
- WebDriverCommandMsg::MouseMoveAction(webview_id, x, y) => {
+ WebDriverCommandMsg::MouseMoveAction(webview_id, x, y, msg_id, response_sender) => {
+ self.webdriver.input_command_response_sender = Some(response_sender);
+
self.compositor_proxy
- .send(CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y));
+ .send(CompositorMsg::WebDriverMouseMoveEvent(
+ webview_id, x, y, msg_id,
+ ));
},
WebDriverCommandMsg::WheelScrollAction(webview, x, y, delta_x, delta_y) => {
self.compositor_proxy
diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs
index 940cc9614cc..6237665b87f 100644
--- a/components/constellation/tracing.rs
+++ b/components/constellation/tracing.rs
@@ -177,6 +177,7 @@ mod from_script {
Self::TitleChanged(..) => target!("TitleChanged"),
Self::IFrameSizes(..) => target!("IFrameSizes"),
Self::ReportMemory(..) => target!("ReportMemory"),
+ Self::WebDriverInputComplete(..) => target!("WebDriverInputComplete"),
Self::FinishJavaScriptEvaluation(..) => target!("FinishJavaScriptEvaluation"),
}
}
diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs
index e33b2ad9d3e..74f592e63b9 100644
--- a/components/fonts/platform/windows/font.rs
+++ b/components/fonts/platform/windows/font.rs
@@ -132,7 +132,9 @@ impl PlatformFontMethods for PlatformFont {
pt_size: Option<Au>,
) -> Result<PlatformFont, &'static str> {
let font_face = FontCollection::system()
- .get_font_from_descriptor(&font_identifier.font_descriptor)
+ .font_from_descriptor(&font_identifier.font_descriptor)
+ .ok()
+ .flatten()
.ok_or("Could not create Font from descriptor")?
.create_font_face();
Self::new(font_face, pt_size)
diff --git a/components/fonts/platform/windows/font_list.rs b/components/fonts/platform/windows/font_list.rs
index d1aa19e178a..e9cdaac8562 100644
--- a/components/fonts/platform/windows/font_list.rs
+++ b/components/fonts/platform/windows/font_list.rs
@@ -25,7 +25,9 @@ where
{
let system_fc = FontCollection::system();
for family in system_fc.families_iter() {
- callback(family.name());
+ if let Ok(family_name) = family.family_name() {
+ callback(family_name);
+ }
}
}
@@ -40,13 +42,17 @@ pub struct LocalFontIdentifier {
impl LocalFontIdentifier {
pub fn index(&self) -> u32 {
FontCollection::system()
- .get_font_from_descriptor(&self.font_descriptor)
+ .font_from_descriptor(&self.font_descriptor)
+ .ok()
+ .flatten()
.map_or(0, |font| font.create_font_face().get_index())
}
pub(crate) fn native_font_handle(&self) -> NativeFontHandle {
let face = FontCollection::system()
- .get_font_from_descriptor(&self.font_descriptor)
+ .font_from_descriptor(&self.font_descriptor)
+ .ok()
+ .flatten()
.expect("Could not create Font from FontDescriptor")
.create_font_face();
let path = face
@@ -62,7 +68,9 @@ impl LocalFontIdentifier {
}
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
- let font = FontCollection::system().get_font_from_descriptor(&self.font_descriptor)?;
+ let font = FontCollection::system()
+ .font_from_descriptor(&self.font_descriptor)
+ .ok()??;
let face = font.create_font_face();
let files = face.get_files();
assert!(!files.is_empty());
@@ -86,10 +94,12 @@ where
F: FnMut(FontTemplate),
{
let system_fc = FontCollection::system();
- if let Some(family) = system_fc.get_font_family_by_name(family_name) {
+ if let Ok(Some(family)) = system_fc.font_family_by_name(family_name) {
let count = family.get_font_count();
for i in 0..count {
- let font = family.get_font(i);
+ let Ok(font) = family.font(i) else {
+ continue;
+ };
let template_descriptor = (&font).into();
let local_font_identifier = LocalFontIdentifier {
font_descriptor: Arc::new(font.to_descriptor()),
diff --git a/components/layout/construct_modern.rs b/components/layout/construct_modern.rs
index 8f1282ec9f6..d09744b2031 100644
--- a/components/layout/construct_modern.rs
+++ b/components/layout/construct_modern.rs
@@ -150,7 +150,6 @@ impl<'a, 'dom> ModernContainerBuilder<'a, 'dom> {
let inline_formatting_context = inline_formatting_context_builder.finish(
self.context,
- self.propagated_data,
true, /* has_first_formatted_line */
false, /* is_single_line_text_box */
self.info.style.writing_mode.to_bidi_level(),
diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs
index 95689cf1186..58ec58c9ce2 100644
--- a/components/layout/display_list/mod.rs
+++ b/components/layout/display_list/mod.rs
@@ -572,6 +572,7 @@ impl Fragment {
section: StackingContextSection,
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
let spatial_id = builder.spatial_id(builder.current_scroll_node_id);
let clip_chain_id = builder.clip_chain_id(builder.current_clip_id);
@@ -683,9 +684,12 @@ impl Fragment {
.get_inherited_box()
.visibility
{
- Visibility::Visible => {
- self.build_display_list_for_text_fragment(text, builder, containing_block)
- },
+ Visibility::Visible => self.build_display_list_for_text_fragment(
+ text,
+ builder,
+ containing_block,
+ text_decorations,
+ ),
Visibility::Hidden => (),
Visibility::Collapse => (),
}
@@ -723,6 +727,7 @@ impl Fragment {
fragment: &TextFragment,
builder: &mut DisplayListBuilder,
containing_block: &PhysicalRect<Au>,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
// NB: The order of painting text components (CSS Text Decoration Module Level 3) is:
// shadows, underline, overline, text, text-emphasis, and then line-through.
@@ -774,23 +779,33 @@ impl Fragment {
);
}
- if fragment
- .text_decoration_line
- .contains(TextDecorationLine::UNDERLINE)
- {
- let mut rect = rect;
- rect.origin.y += font_metrics.ascent - font_metrics.underline_offset;
- rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
+ for text_decoration in text_decorations.iter() {
+ if text_decoration.line.contains(TextDecorationLine::UNDERLINE) {
+ let mut rect = rect;
+ rect.origin.y += font_metrics.ascent - font_metrics.underline_offset;
+ rect.size.height =
+ Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
+ self.build_display_list_for_text_decoration(
+ &parent_style,
+ builder,
+ &rect,
+ text_decoration,
+ );
+ }
}
- if fragment
- .text_decoration_line
- .contains(TextDecorationLine::OVERLINE)
- {
- let mut rect = rect;
- rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
+ for text_decoration in text_decorations.iter() {
+ if text_decoration.line.contains(TextDecorationLine::OVERLINE) {
+ let mut rect = rect;
+ rect.size.height =
+ Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
+ self.build_display_list_for_text_decoration(
+ &parent_style,
+ builder,
+ &rect,
+ text_decoration,
+ );
+ }
}
// TODO: This caret/text selection implementation currently does not account for vertical text
@@ -867,14 +882,22 @@ impl Fragment {
None,
);
- if fragment
- .text_decoration_line
- .contains(TextDecorationLine::LINE_THROUGH)
- {
- let mut rect = rect;
- rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset;
- rect.size.height = Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
+ for text_decoration in text_decorations.iter() {
+ if text_decoration
+ .line
+ .contains(TextDecorationLine::LINE_THROUGH)
+ {
+ let mut rect = rect;
+ rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset;
+ rect.size.height =
+ Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx));
+ self.build_display_list_for_text_decoration(
+ &parent_style,
+ builder,
+ &rect,
+ text_decoration,
+ );
+ }
}
if !shadows.0.is_empty() {
@@ -887,15 +910,11 @@ impl Fragment {
parent_style: &ServoArc<ComputedValues>,
builder: &mut DisplayListBuilder,
rect: &PhysicalRect<Au>,
- color: &AbsoluteColor,
+ text_decoration: &FragmentTextDecoration,
) {
let rect = rect.to_webrender();
let wavy_line_thickness = (0.33 * rect.size().height).ceil();
- let text_decoration_color = parent_style
- .clone_text_decoration_color()
- .resolve_to_absolute(color);
- let text_decoration_style = parent_style.clone_text_decoration_style();
- if text_decoration_style == ComputedTextDecorationStyle::MozNone {
+ if text_decoration.style == ComputedTextDecorationStyle::MozNone {
return;
}
let common_properties = builder.common_properties(rect, parent_style);
@@ -904,8 +923,8 @@ impl Fragment {
&rect,
wavy_line_thickness,
wr::LineOrientation::Horizontal,
- &rgba(text_decoration_color),
- text_decoration_style.to_webrender(),
+ &rgba(text_decoration.color),
+ text_decoration.style.to_webrender(),
);
// XXX(ferjm) support text-decoration-style: double
}
diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs
index bcd882e3fcc..d22a2b6656a 100644
--- a/components/layout/display_list/stacking_context.rs
+++ b/components/layout/display_list/stacking_context.rs
@@ -5,6 +5,7 @@
use core::f32;
use std::cell::RefCell;
use std::mem;
+use std::sync::Arc;
use app_units::Au;
use base::id::ScrollTreeNodeId;
@@ -18,13 +19,15 @@ use euclid::default::{Point2D, Rect, Size2D};
use log::warn;
use servo_config::opts::DebugOptions;
use style::Zero;
+use style::color::AbsoluteColor;
use style::computed_values::float::T as ComputedFloat;
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
use style::computed_values::overflow_x::T as ComputedOverflow;
use style::computed_values::position::T as ComputedPosition;
+use style::computed_values::text_decoration_style::T as TextDecorationStyle;
use style::values::computed::angle::Angle;
use style::values::computed::basic_shape::ClipPath;
-use style::values::computed::{ClipRectOrAuto, Length};
+use style::values::computed::{ClipRectOrAuto, Length, TextDecorationLine};
use style::values::generics::box_::Perspective;
use style::values::generics::transform::{self, GenericRotate, GenericScale, GenericTranslate};
use style::values::specified::box_::DisplayOutside;
@@ -168,12 +171,14 @@ impl StackingContextTree {
};
let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug);
+ let text_decorations = Default::default();
for fragment in &fragment_tree.root_fragments {
fragment.build_stacking_context_tree(
&mut stacking_context_tree,
&containing_block_info,
&mut root_stacking_context,
StackingContextBuildMode::SkipHoisted,
+ &text_decorations,
);
}
root_stacking_context.sort();
@@ -246,6 +251,14 @@ impl StackingContextTree {
}
}
+/// The text decorations for a Fragment, collecting during [`StackingContextTree`] construction.
+#[derive(Clone, Debug)]
+pub(crate) struct FragmentTextDecoration {
+ pub line: TextDecorationLine,
+ pub color: AbsoluteColor,
+ pub style: TextDecorationStyle,
+}
+
/// A piece of content that directly belongs to a section of a stacking context.
///
/// This is generally part of a fragment, like its borders or foreground, but it
@@ -261,6 +274,7 @@ pub(crate) enum StackingContextContent {
fragment: Fragment,
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
+ text_decorations: Arc<Vec<FragmentTextDecoration>>,
},
/// An index into [StackingContext::atomic_inline_stacking_containers].
@@ -292,6 +306,7 @@ impl StackingContextContent {
fragment,
is_hit_test_for_scrollable_overflow,
is_collapsed_table_borders,
+ text_decorations,
} => {
builder.current_scroll_node_id = *scroll_node_id;
builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
@@ -302,6 +317,7 @@ impl StackingContextContent {
*section,
*is_hit_test_for_scrollable_overflow,
*is_collapsed_table_borders,
+ text_decorations,
);
},
Self::AtomicInlineStackingContainer { index } => {
@@ -800,6 +816,7 @@ impl Fragment {
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
mode: StackingContextBuildMode,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
let containing_block = containing_block_info.get_containing_block_for_fragment(self);
let fragment_clone = self.clone();
@@ -812,6 +829,11 @@ impl Fragment {
return;
}
+ let text_decorations = match self {
+ Fragment::Float(..) => &Default::default(),
+ _ => text_decorations,
+ };
+
// If this fragment has a transform applied that makes it take up no space
// then we don't need to create any stacking contexts for it.
let has_non_invertible_transform = fragment
@@ -828,6 +850,7 @@ impl Fragment {
containing_block,
containing_block_info,
stacking_context,
+ text_decorations,
);
},
Fragment::AbsoluteOrFixedPositioned(fragment) => {
@@ -842,6 +865,7 @@ impl Fragment {
containing_block_info,
stacking_context,
StackingContextBuildMode::IncludeHoisted,
+ &Default::default(),
);
},
Fragment::Positioning(fragment) => {
@@ -851,6 +875,7 @@ impl Fragment {
containing_block,
containing_block_info,
stacking_context,
+ text_decorations,
);
},
Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
@@ -867,6 +892,7 @@ impl Fragment {
fragment: fragment_clone,
is_hit_test_for_scrollable_overflow: false,
is_collapsed_table_borders: false,
+ text_decorations: text_decorations.clone(),
});
},
}
@@ -929,6 +955,7 @@ impl BoxFragment {
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
self.build_stacking_context_tree_maybe_creating_reference_frame(
fragment,
@@ -936,6 +963,7 @@ impl BoxFragment {
containing_block,
containing_block_info,
parent_stacking_context,
+ text_decorations,
);
}
@@ -946,6 +974,7 @@ impl BoxFragment {
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
let reference_frame_data =
match self.reference_frame_data_if_necessary(&containing_block.rect) {
@@ -957,6 +986,7 @@ impl BoxFragment {
containing_block,
containing_block_info,
parent_stacking_context,
+ text_decorations,
);
},
};
@@ -999,6 +1029,7 @@ impl BoxFragment {
&adjusted_containing_block,
&new_containing_block_info,
parent_stacking_context,
+ text_decorations,
);
}
@@ -1009,6 +1040,7 @@ impl BoxFragment {
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
let context_type = match self.get_stacking_context_type() {
Some(context_type) => context_type,
@@ -1019,6 +1051,7 @@ impl BoxFragment {
containing_block,
containing_block_info,
parent_stacking_context,
+ text_decorations,
);
return;
},
@@ -1072,6 +1105,7 @@ impl BoxFragment {
containing_block,
containing_block_info,
&mut child_stacking_context,
+ text_decorations,
);
let mut stolen_children = vec![];
@@ -1097,6 +1131,7 @@ impl BoxFragment {
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
let mut new_scroll_node_id = containing_block.scroll_node_id;
let mut new_clip_id = containing_block.clip_id;
@@ -1164,6 +1199,7 @@ impl BoxFragment {
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: false,
is_collapsed_table_borders: false,
+ text_decorations: text_decorations.clone(),
});
};
@@ -1198,6 +1234,7 @@ impl BoxFragment {
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: true,
is_collapsed_table_borders: false,
+ text_decorations: text_decorations.clone(),
});
}
}
@@ -1239,12 +1276,46 @@ impl BoxFragment {
containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants)
};
+ // Text decorations are not propagated to atomic inline-level descendants.
+ // From https://drafts.csswg.org/css2/#lining-striking-props:
+ // > Note that text decorations are not propagated to floating and absolutely
+ // > positioned descendants, nor to the contents of atomic inline-level descendants
+ // > such as inline blocks and inline tables.
+ let text_decorations = match self.is_atomic_inline_level() ||
+ self.base
+ .flags
+ .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER)
+ {
+ true => &Default::default(),
+ false => text_decorations,
+ };
+
+ let new_text_decoration;
+ let text_decorations = match self.style.clone_text_decoration_line() {
+ TextDecorationLine::NONE => text_decorations,
+ line => {
+ let mut new_vector = (**text_decorations).clone();
+ let color = &self.style.get_inherited_text().color;
+ new_vector.push(FragmentTextDecoration {
+ line,
+ color: self
+ .style
+ .clone_text_decoration_color()
+ .resolve_to_absolute(color),
+ style: self.style.clone_text_decoration_style(),
+ });
+ new_text_decoration = Arc::new(new_vector);
+ &new_text_decoration
+ },
+ };
+
for child in &self.children {
child.build_stacking_context_tree(
stacking_context_tree,
&new_containing_block_info,
stacking_context,
StackingContextBuildMode::SkipHoisted,
+ text_decorations,
);
}
@@ -1263,6 +1334,7 @@ impl BoxFragment {
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: false,
is_collapsed_table_borders: true,
+ text_decorations: text_decorations.clone(),
});
}
}
@@ -1646,6 +1718,7 @@ impl PositioningFragment {
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
+ text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
let rect = self
.rect
@@ -1660,6 +1733,7 @@ impl PositioningFragment {
&new_containing_block_info,
stacking_context,
StackingContextBuildMode::SkipHoisted,
+ text_decorations,
);
}
}
diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs
index 8fc5aa0778b..985f5f3cf65 100644
--- a/components/layout/flexbox/layout.rs
+++ b/components/layout/flexbox/layout.rs
@@ -417,9 +417,8 @@ struct DesiredFlexFractionAndGrowOrShrinkFactor {
#[derive(Default)]
struct FlexItemBoxInlineContentSizesInfo {
outer_flex_base_size: Au,
- content_min_main_size: Au,
- content_max_main_size: Option<Au>,
- pbm_auto_is_zero: FlexRelativeVec2<Au>,
+ outer_min_main_size: Au,
+ outer_max_main_size: Option<Au>,
min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
min_content_main_size_for_multiline_container: Au,
@@ -583,9 +582,8 @@ impl FlexContainer {
for FlexItemBoxInlineContentSizesInfo {
outer_flex_base_size,
- content_min_main_size,
- content_max_main_size,
- pbm_auto_is_zero,
+ outer_min_main_size,
+ outer_max_main_size,
min_flex_factors,
max_flex_factors,
min_content_main_size_for_multiline_container,
@@ -595,16 +593,13 @@ impl FlexContainer {
// > 4. Add each item’s flex base size to the product of its flex grow factor (scaled flex shrink
// > factor, if shrinking) and the chosen flex fraction, then clamp that result by the max main size
// > floored by the min main size.
- let outer_min_main_size = *content_min_main_size + pbm_auto_is_zero.main;
- let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
-
// > 5. The flex container’s max-content size is the largest sum (among all the lines) of the
// > afore-calculated sizes of all items within a single line.
container_max_content_size += (*outer_flex_base_size +
Au::from_f32_px(
max_flex_factors.flex_grow_or_shrink_factor * chosen_max_flex_fraction,
))
- .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
+ .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
// > The min-content main size of a single-line flex container is calculated
// > identically to the max-content main size, except that the flex items’
@@ -621,7 +616,7 @@ impl FlexContainer {
Au::from_f32_px(
min_flex_factors.flex_grow_or_shrink_factor * chosen_min_flex_fraction,
))
- .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
+ .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
} else {
container_min_content_size
.max_assign(*min_content_main_size_for_multiline_container);
@@ -2458,6 +2453,8 @@ impl FlexItemBox {
};
let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
+ let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
+ let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
let max_flex_factors = self.desired_flex_factors_for_preferred_width(
content_contribution_sizes.max_content,
flex_base_size,
@@ -2483,20 +2480,19 @@ impl FlexItemBox {
content_contribution_sizes.min_content;
let style_position = &self.style().get_position();
if style_position.flex_grow.is_zero() {
- min_content_main_size_for_multiline_container.min_assign(flex_base_size);
+ min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
}
if style_position.flex_shrink.is_zero() {
- min_content_main_size_for_multiline_container.max_assign(flex_base_size);
+ min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
}
min_content_main_size_for_multiline_container =
min_content_main_size_for_multiline_container
- .clamp_between_extremums(content_min_main_size, content_max_main_size);
+ .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
FlexItemBoxInlineContentSizesInfo {
outer_flex_base_size,
- content_min_main_size,
- content_max_main_size,
- pbm_auto_is_zero,
+ outer_min_main_size,
+ outer_max_main_size,
min_flex_factors,
max_flex_factors,
min_content_main_size_for_multiline_container,
diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs
index 96a311ee2b5..7f4a869a944 100644
--- a/components/layout/flexbox/mod.rs
+++ b/components/layout/flexbox/mod.rs
@@ -105,8 +105,7 @@ impl FlexContainer {
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
) -> Self {
- let mut builder =
- ModernContainerBuilder::new(context, info, propagated_data.union(&info.style));
+ let mut builder = ModernContainerBuilder::new(context, info, propagated_data);
contents.traverse(context, info, &mut builder);
let items = builder.finish();
diff --git a/components/layout/flow/construct.rs b/components/layout/flow/construct.rs
index 334da8ae2b0..cc3fe0e6f44 100644
--- a/components/layout/flow/construct.rs
+++ b/components/layout/flow/construct.rs
@@ -199,7 +199,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
context,
info,
block_level_boxes: Vec::new(),
- propagated_data: propagated_data.union(&info.style),
+ propagated_data,
have_already_seen_first_line_for_text_indent: false,
anonymous_box_info: None,
anonymous_table_content: Vec::new(),
@@ -228,7 +228,6 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> {
self.inline_formatting_context_builder.take()?.finish(
self.context,
- self.propagated_data,
!self.have_already_seen_first_line_for_text_indent,
self.info.is_single_line_text_input(),
self.info.style.writing_mode.to_bidi_level(),
@@ -280,16 +279,6 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
// creation of an inline table. It requires the parent to be an inline box.
let inline_table = self.currently_processing_inline_box();
- // Text decorations are not propagated to atomic inline-level descendants.
- // From https://drafts.csswg.org/css2/#lining-striking-props:
- // > Note that text decorations are not propagated to floating and absolutely
- // > positioned descendants, nor to the contents of atomic inline-level descendants
- // > such as inline blocks and inline tables.
- let propagated_data = match inline_table {
- true => self.propagated_data.without_text_decorations(),
- false => self.propagated_data,
- };
-
let contents: Vec<AnonymousTableContent<'dom>> =
self.anonymous_table_content.drain(..).collect();
let last_text = match contents.last() {
@@ -298,7 +287,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
};
let (table_info, ifc) =
- Table::construct_anonymous(self.context, self.info, contents, propagated_data);
+ Table::construct_anonymous(self.context, self.info, contents, self.propagated_data);
if inline_table {
self.ensure_inline_formatting_context_builder()
@@ -315,7 +304,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
info: table_info,
box_slot: BoxSlot::dummy(),
kind: BlockLevelCreator::AnonymousTable { table_block },
- propagated_data,
+ propagated_data: self.propagated_data,
});
}
@@ -464,7 +453,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
contents,
list_item_style,
},
- propagated_data: self.propagated_data.without_text_decorations(),
+ propagated_data: self.propagated_data,
});
}
@@ -480,15 +469,14 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
else {
// If this inline element is an atomic, handle it and return.
let context = self.context;
- let propagaged_data = self.propagated_data.without_text_decorations();
+ let propagated_data = self.propagated_data;
let atomic = self.ensure_inline_formatting_context_builder().push_atomic(
IndependentFormattingContext::construct(
context,
info,
display_inside,
contents,
- // Text decorations are not propagated to atomic inline-level descendants.
- propagaged_data,
+ propagated_data,
),
);
box_slot.set(LayoutBox::InlineLevel(vec![atomic]));
@@ -550,7 +538,6 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
.and_then(|builder| {
builder.split_around_block_and_finish(
self.context,
- self.propagated_data,
!self.have_already_seen_first_line_for_text_indent,
self.info.style.writing_mode.to_bidi_level(),
)
@@ -631,7 +618,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
info: info.clone(),
box_slot,
kind,
- propagated_data: self.propagated_data.without_text_decorations(),
+ propagated_data: self.propagated_data,
});
}
@@ -664,7 +651,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
info: info.clone(),
box_slot,
kind,
- propagated_data: self.propagated_data.without_text_decorations(),
+ propagated_data: self.propagated_data,
});
}
@@ -754,7 +741,7 @@ impl BlockLevelJob<'_> {
context,
info,
contents,
- self.propagated_data.without_text_decorations(),
+ self.propagated_data,
false, /* is_list_item */
);
ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
diff --git a/components/layout/flow/float.rs b/components/layout/flow/float.rs
index f1ae2b4459a..bb6c7ca8983 100644
--- a/components/layout/flow/float.rs
+++ b/components/layout/flow/float.rs
@@ -897,8 +897,7 @@ impl FloatBox {
info,
display_inside,
contents,
- // Text decorations are not propagated to any out-of-flow descendants
- propagated_data.without_text_decorations(),
+ propagated_data,
),
}
}
diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs
index 600da9b721a..07a2e914835 100644
--- a/components/layout/flow/inline/construct.rs
+++ b/components/layout/flow/inline/construct.rs
@@ -16,7 +16,6 @@ use super::{
InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem,
SharedInlineStyles,
};
-use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom_traversal::NodeAndStyleInfo;
@@ -344,7 +343,6 @@ impl InlineFormattingContextBuilder {
pub(crate) fn split_around_block_and_finish(
&mut self,
layout_context: &LayoutContext,
- propagated_data: PropagatedBoxTreeData,
has_first_formatted_line: bool,
default_bidi_level: Level,
) -> Option<InlineFormattingContext> {
@@ -386,7 +384,6 @@ impl InlineFormattingContextBuilder {
inline_builder_from_before_split.finish(
layout_context,
- propagated_data,
has_first_formatted_line,
/* is_single_line_text_input = */ false,
default_bidi_level,
@@ -397,7 +394,6 @@ impl InlineFormattingContextBuilder {
pub(crate) fn finish(
self,
layout_context: &LayoutContext,
- propagated_data: PropagatedBoxTreeData,
has_first_formatted_line: bool,
is_single_line_text_input: bool,
default_bidi_level: Level,
@@ -410,7 +406,6 @@ impl InlineFormattingContextBuilder {
Some(InlineFormattingContext::new_with_builder(
self,
layout_context,
- propagated_data,
has_first_formatted_line,
is_single_line_text_input,
default_bidi_level,
diff --git a/components/layout/flow/inline/inline_box.rs b/components/layout/flow/inline/inline_box.rs
index b547f3b5935..a9642d3b222 100644
--- a/components/layout/flow/inline/inline_box.rs
+++ b/components/layout/flow/inline/inline_box.rs
@@ -256,13 +256,7 @@ impl InlineBoxContainerState {
}
Self {
- base: InlineContainerState::new(
- style,
- flags,
- Some(parent_container),
- parent_container.text_decoration_line,
- font_metrics,
- ),
+ base: InlineContainerState::new(style, flags, Some(parent_container), font_metrics),
identifier: inline_box.identifier,
base_fragment_info: inline_box.base.base_fragment_info,
pbm,
diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs
index 3b92078d67d..14a1531883f 100644
--- a/components/layout/flow/inline/line.rs
+++ b/components/layout/flow/inline/line.rs
@@ -15,7 +15,6 @@ use style::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
use style::values::generics::font::LineHeight;
use style::values::specified::align::AlignFlags;
use style::values::specified::box_::DisplayOutside;
-use style::values::specified::text::TextDecorationLine;
use unicode_bidi::{BidiInfo, Level};
use webrender_api::FontInstanceKey;
@@ -572,7 +571,6 @@ impl LineItemLayout<'_, '_> {
font_metrics: text_item.font_metrics,
font_key: text_item.font_key,
glyphs: text_item.text,
- text_decoration_line: text_item.text_decoration_line,
justification_adjustment: self.justification_adjustment,
selection_range: text_item.selection_range,
})),
@@ -765,7 +763,6 @@ pub(super) struct TextRunLineItem {
pub text: Vec<std::sync::Arc<GlyphStore>>,
pub font_metrics: FontMetrics,
pub font_key: FontInstanceKey,
- pub text_decoration_line: TextDecorationLine,
/// The BiDi level of this [`TextRunLineItem`] to enable reordering.
pub bidi_level: Level,
pub selection_range: Option<Range<ByteIndex>>,
diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs
index 74d42ca6fb4..6fd4a51a526 100644
--- a/components/layout/flow/inline/mod.rs
+++ b/components/layout/flow/inline/mod.rs
@@ -103,7 +103,7 @@ use style::properties::style_structs::InheritedText;
use style::values::generics::box_::VerticalAlignKeyword;
use style::values::generics::font::LineHeight;
use style::values::specified::box_::BaselineSource;
-use style::values::specified::text::{TextAlignKeyword, TextDecorationLine};
+use style::values::specified::text::TextAlignKeyword;
use style::values::specified::{TextAlignLast, TextJustify};
use text_run::{
TextRun, XI_LINE_BREAKING_CLASS_GL, XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
@@ -134,7 +134,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
-use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData, SharedStyle};
+use crate::{ConstraintSpace, ContainingBlock, SharedStyle};
// From gfxFontConstants.h in Firefox.
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
@@ -163,8 +163,6 @@ pub(crate) struct InlineFormattingContext {
/// share styles with all [`TextRun`] children.
pub(super) shared_inline_styles: SharedInlineStyles,
- pub(super) text_decoration_line: TextDecorationLine,
-
/// Whether this IFC contains the 1st formatted line of an element:
/// <https://www.w3.org/TR/css-pseudo-4/#first-formatted-line>.
pub(super) has_first_formatted_line: bool,
@@ -628,12 +626,6 @@ pub(super) struct InlineContainerState {
/// this inline box on the current line OR any previous line.
has_content: RefCell<bool>,
- /// Indicates whether this nesting level have text decorations in effect.
- /// From <https://drafts.csswg.org/css-text-decor/#line-decoration>
- // "When specified on or propagated to a block container that establishes
- // an IFC..."
- text_decoration_line: TextDecorationLine,
-
/// The block size contribution of this container's default font ie the size of the
/// "strut." Whether this is integrated into the [`Self::nested_strut_block_sizes`]
/// depends on the line-height quirk described in
@@ -1461,7 +1453,6 @@ impl InlineFormattingContextLayout<'_> {
inline_styles: text_run.inline_styles.clone(),
font_metrics,
font_key: ifc_font_info.key,
- text_decoration_line: self.current_inline_container_state().text_decoration_line,
bidi_level,
selection_range,
},
@@ -1655,7 +1646,6 @@ impl InlineFormattingContext {
pub(super) fn new_with_builder(
builder: InlineFormattingContextBuilder,
layout_context: &LayoutContext,
- propagated_data: PropagatedBoxTreeData,
has_first_formatted_line: bool,
is_single_line_text_input: bool,
starting_bidi_level: Level,
@@ -1711,7 +1701,6 @@ impl InlineFormattingContext {
.last()
.expect("Should have at least one SharedInlineStyle for the root of an IFC")
.clone(),
- text_decoration_line: propagated_data.text_decoration,
has_first_formatted_line,
contains_floats: builder.contains_floats,
is_single_line_text_input,
@@ -1781,7 +1770,6 @@ impl InlineFormattingContext {
style.to_arc(),
inline_container_state_flags,
None, /* parent_container */
- self.text_decoration_line,
default_font_metrics.as_ref(),
),
inline_box_state_stack: Vec::new(),
@@ -1879,10 +1867,8 @@ impl InlineContainerState {
style: Arc<ComputedValues>,
flags: InlineContainerStateFlags,
parent_container: Option<&InlineContainerState>,
- parent_text_decoration_line: TextDecorationLine,
font_metrics: Option<&FontMetrics>,
) -> Self {
- let text_decoration_line = parent_text_decoration_line | style.clone_text_decoration_line();
let font_metrics = font_metrics.cloned().unwrap_or_else(FontMetrics::empty);
let line_height = line_height(
&style,
@@ -1919,7 +1905,6 @@ impl InlineContainerState {
style,
flags,
has_content: RefCell::new(false),
- text_decoration_line,
nested_strut_block_sizes: nested_block_sizes,
strut_block_sizes,
baseline_offset,
diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs
index a37db54065d..8ad3671032e 100644
--- a/components/layout/flow/root.rs
+++ b/components/layout/flow/root.rs
@@ -314,7 +314,7 @@ fn construct_for_root_element(
let contents = ReplacedContents::for_element(root_element, context)
.map_or_else(|| NonReplacedContents::OfElement.into(), Contents::Replaced);
- let propagated_data = PropagatedBoxTreeData::default().union(&info.style);
+ let propagated_data = PropagatedBoxTreeData::default();
let root_box = if box_style.position.is_absolutely_positioned() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(ArcRefCell::new(
AbsolutelyPositionedBox::construct(context, &info, display_inside, contents),
diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs
index 1ebc7b3c989..c81fd59e36b 100644
--- a/components/layout/fragment_tree/fragment.rs
+++ b/components/layout/fragment_tree/fragment.rs
@@ -14,7 +14,6 @@ use range::Range as ServoRange;
use servo_arc::Arc as ServoArc;
use style::Zero;
use style::properties::ComputedValues;
-use style::values::specified::text::TextDecorationLine;
use webrender_api::{FontInstanceKey, ImageKey};
use super::{
@@ -72,9 +71,6 @@ pub(crate) struct TextFragment {
#[conditional_malloc_size_of]
pub glyphs: Vec<Arc<GlyphStore>>,
- /// A flag that represents the _used_ value of the text-decoration property.
- pub text_decoration_line: TextDecorationLine,
-
/// Extra space to add for each justification opportunity.
pub justification_adjustment: Au,
pub selection_range: Option<ServoRange<ByteIndex>>,
diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs
index b490b4a0506..8162ed1dd0b 100644
--- a/components/layout/layout_impl.rs
+++ b/components/layout/layout_impl.rs
@@ -773,7 +773,7 @@ impl LayoutThread {
let root_node = root_element.as_node();
let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node);
- if damage == RestyleDamage::REPAINT {
+ if damage.is_empty() || damage == RestyleDamage::REPAINT {
layout_context.style_context.stylist.rule_tree().maybe_gc();
return false;
}
diff --git a/components/layout/lib.rs b/components/layout/lib.rs
index cd992387277..ea254723dcb 100644
--- a/components/layout/lib.rs
+++ b/components/layout/lib.rs
@@ -41,7 +41,6 @@ use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc as ServoArc;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
-use style::values::computed::TextDecorationLine;
use crate::geom::{LogicalVec2, SizeConstraint};
use crate::style_ext::AspectRatio;
@@ -163,39 +162,20 @@ impl<'a> From<&'_ DefiniteContainingBlock<'a>> for ContainingBlock<'a> {
/// propoagation, but only during `BoxTree` construction.
#[derive(Clone, Copy, Debug)]
struct PropagatedBoxTreeData {
- text_decoration: TextDecorationLine,
allow_percentage_column_in_tables: bool,
}
impl Default for PropagatedBoxTreeData {
fn default() -> Self {
Self {
- text_decoration: Default::default(),
allow_percentage_column_in_tables: true,
}
}
}
impl PropagatedBoxTreeData {
- pub(crate) fn union(&self, style: &ComputedValues) -> Self {
- Self {
- // FIXME(#31736): This is only taking into account the line style and not the decoration
- // color. This should collect information about both so that they can be rendered properly.
- text_decoration: self.text_decoration | style.clone_text_decoration_line(),
- allow_percentage_column_in_tables: self.allow_percentage_column_in_tables,
- }
- }
-
- pub(crate) fn without_text_decorations(&self) -> Self {
- Self {
- text_decoration: TextDecorationLine::NONE,
- allow_percentage_column_in_tables: self.allow_percentage_column_in_tables,
- }
- }
-
fn disallowing_percentage_table_columns(&self) -> PropagatedBoxTreeData {
Self {
- text_decoration: self.text_decoration,
allow_percentage_column_in_tables: false,
}
}
diff --git a/components/layout/table/construct.rs b/components/layout/table/construct.rs
index 0c238073df2..0b22ea1c13a 100644
--- a/components/layout/table/construct.rs
+++ b/components/layout/table/construct.rs
@@ -81,12 +81,7 @@ impl Table {
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
) -> Self {
- let mut traversal = TableBuilderTraversal::new(
- context,
- info,
- grid_style,
- propagated_data.union(&info.style),
- );
+ let mut traversal = TableBuilderTraversal::new(context, info, grid_style, propagated_data);
contents.traverse(context, info, &mut traversal);
traversal.finish()
}
@@ -771,9 +766,6 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
});
self.builder.table.row_groups.push(row_group.clone());
- let previous_propagated_data = self.current_propagated_data;
- self.current_propagated_data = self.current_propagated_data.union(&info.style);
-
let new_row_group_index = self.builder.table.row_groups.len() - 1;
self.current_row_group_index = Some(new_row_group_index);
@@ -785,7 +777,6 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
self.finish_anonymous_row_if_needed();
self.current_row_group_index = None;
- self.current_propagated_data = previous_propagated_data;
self.builder.incoming_rowspans.clear();
box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
@@ -936,7 +927,7 @@ impl<'style, 'builder, 'dom, 'a> TableRowBuilder<'style, 'builder, 'dom, 'a> {
table_traversal,
info,
current_anonymous_cell_content: Vec::new(),
- propagated_data: propagated_data.union(&info.style),
+ propagated_data,
}
}
diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs
index 2bc7a598d08..05fc09c5511 100644
--- a/components/layout/taffy/mod.rs
+++ b/components/layout/taffy/mod.rs
@@ -36,8 +36,7 @@ impl TaffyContainer {
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
) -> Self {
- let mut builder =
- ModernContainerBuilder::new(context, info, propagated_data.union(&info.style));
+ let mut builder = ModernContainerBuilder::new(context, info, propagated_data);
contents.traverse(context, info, &mut builder);
let items = builder.finish();
diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs
index d05deb24bfa..faf25dc170d 100644
--- a/components/layout/traversal.rs
+++ b/components/layout/traversal.rs
@@ -105,7 +105,9 @@ pub(crate) fn compute_damage_and_repair_style_inner(
parent_restyle_damage: RestyleDamage,
) -> RestyleDamage {
let original_damage;
- let damage = {
+ let damage;
+
+ {
let mut element_data = node
.style_data()
.expect("Should not run `compute_damage` before styling.")
@@ -113,14 +115,14 @@ pub(crate) fn compute_damage_and_repair_style_inner(
.borrow_mut();
original_damage = std::mem::take(&mut element_data.damage);
+ damage = original_damage | parent_restyle_damage;
+
if let Some(ref style) = element_data.styles.primary {
if style.get_box().display == Display::None {
- return parent_restyle_damage;
+ return damage;
}
}
-
- original_damage | parent_restyle_damage
- };
+ }
let mut propagated_damage = damage;
for child in iter_child_nodes(node) {
diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs
index 46a2a4ea111..8276baa07e7 100644
--- a/components/net/image_cache.rs
+++ b/components/net/image_cache.rs
@@ -66,10 +66,10 @@ fn set_webrender_image_key(compositor_api: &CrossProcessCompositorApi, image: &m
return;
}
let mut bytes = Vec::new();
- let frame_bytes = image.bytes();
+ let frame_bytes = image.first_frame().bytes;
let is_opaque = match image.format {
PixelFormat::BGRA8 => {
- bytes.extend_from_slice(&frame_bytes);
+ bytes.extend_from_slice(frame_bytes);
pixels::rgba8_premultiply_inplace(bytes.as_mut_slice())
},
PixelFormat::RGB8 => {
diff --git a/components/pixels/lib.rs b/components/pixels/lib.rs
index c1f57875c6d..35b32c92414 100644
--- a/components/pixels/lib.rs
+++ b/components/pixels/lib.rs
@@ -4,6 +4,7 @@
use std::borrow::Cow;
use std::io::Cursor;
+use std::ops::Range;
use std::time::Duration;
use std::{cmp, fmt, vec};
@@ -126,13 +127,24 @@ pub struct Image {
pub format: PixelFormat,
pub id: Option<ImageKey>,
pub cors_status: CorsStatus,
+ pub bytes: IpcSharedMemory,
pub frames: Vec<ImageFrame>,
}
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct ImageFrame {
pub delay: Option<Duration>,
- pub bytes: IpcSharedMemory,
+ /// References a range of the `bytes` field from the image that this
+ /// frame belongs to.
+ pub byte_range: Range<usize>,
+ pub width: u32,
+ pub height: u32,
+}
+
+/// A non-owning reference to the data of an [ImageFrame]
+pub struct ImageFrameView<'a> {
+ pub delay: Option<Duration>,
+ pub bytes: &'a [u8],
pub width: u32,
pub height: u32,
}
@@ -142,12 +154,19 @@ impl Image {
self.frames.len() > 1
}
- pub fn bytes(&self) -> IpcSharedMemory {
- self.frames
- .first()
- .expect("Should have at least one frame")
- .bytes
- .clone()
+ pub fn frames(&self) -> impl Iterator<Item = ImageFrameView> {
+ self.frames.iter().map(|frame| ImageFrameView {
+ delay: frame.delay,
+ bytes: self.bytes.get(frame.byte_range.clone()).unwrap(),
+ width: frame.width,
+ height: frame.height,
+ })
+ }
+
+ pub fn first_frame(&self) -> ImageFrameView {
+ self.frames()
+ .next()
+ .expect("All images should have at least one frame")
}
}
@@ -189,7 +208,7 @@ pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option<Image>
rgba8_byte_swap_colors_inplace(&mut rgba);
let frame = ImageFrame {
delay: None,
- bytes: IpcSharedMemory::from_bytes(&rgba),
+ byte_range: 0..rgba.len(),
width: rgba.width(),
height: rgba.height(),
};
@@ -198,6 +217,7 @@ pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option<Image>
height: rgba.height(),
format: PixelFormat::BGRA8,
frames: vec![frame],
+ bytes: IpcSharedMemory::from_bytes(&rgba),
id: None,
cors_status,
})
@@ -364,45 +384,61 @@ fn decode_gif(buffer: &[u8], cors_status: CorsStatus) -> Option<Image> {
// This uses `map_while`, because the first non-decodable frame seems to
// send the frame iterator into an infinite loop. See
// <https://github.com/image-rs/image/issues/2442>.
+ let mut frame_data = vec![];
+ let mut total_number_of_bytes = 0;
let frames: Vec<ImageFrame> = decoded_gif
.into_frames()
.map_while(|decoded_frame| {
- let mut frame = match decoded_frame {
+ let mut gif_frame = match decoded_frame {
Ok(decoded_frame) => decoded_frame,
Err(error) => {
debug!("decode GIF frame error: {error}");
return None;
},
};
- rgba8_byte_swap_colors_inplace(frame.buffer_mut());
+ rgba8_byte_swap_colors_inplace(gif_frame.buffer_mut());
+ let frame_start = total_number_of_bytes;
+ total_number_of_bytes += gif_frame.buffer().len();
+
+ // The image size should be at least as large as the largest frame.
+ let frame_width = gif_frame.buffer().width();
+ let frame_height = gif_frame.buffer().height();
+ width = cmp::max(width, frame_width);
+ height = cmp::max(height, frame_height);
let frame = ImageFrame {
- bytes: IpcSharedMemory::from_bytes(frame.buffer()),
- delay: Some(Duration::from(frame.delay())),
- width: frame.buffer().width(),
- height: frame.buffer().height(),
+ byte_range: frame_start..total_number_of_bytes,
+ delay: Some(Duration::from(gif_frame.delay())),
+ width: frame_width,
+ height: frame_height,
};
- // The image size should be at least as large as the largest frame.
- width = cmp::max(width, frame.width);
- height = cmp::max(height, frame.height);
+ frame_data.push(gif_frame);
+
Some(frame)
})
.collect();
if frames.is_empty() {
debug!("Animated Image decoding error");
- None
- } else {
- Some(Image {
- width,
- height,
- cors_status,
- frames,
- id: None,
- format: PixelFormat::BGRA8,
- })
+ return None;
}
+
+ // Coalesce the frame data into one single shared memory region.
+ let mut bytes = Vec::with_capacity(total_number_of_bytes);
+ for frame in frame_data {
+ bytes.extend_from_slice(frame.buffer());
+ }
+
+ Some(Image {
+ width,
+ height,
+ cors_status,
+ frames,
+ id: None,
+ format: PixelFormat::BGRA8,
+ bytes: IpcSharedMemory::from_bytes(&bytes),
+ })
}
#[cfg(test)]
diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs
index dabe6a5728b..d4840a5b470 100644
--- a/components/script/canvas_state.rs
+++ b/components/script/canvas_state.rs
@@ -17,7 +17,7 @@ use cssparser::color::clamp_unit_f32;
use cssparser::{Parser, ParserInput};
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
use euclid::vec2;
-use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
use net_traits::image_cache::{ImageCache, ImageResponse};
use net_traits::request::CorsSettings;
use pixels::PixelFormat;
@@ -350,7 +350,7 @@ impl CanvasState {
size.cast(),
format,
alpha_mode,
- img.bytes(),
+ IpcSharedMemory::from_bytes(img.first_frame().bytes),
))
}
diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs
index 0e8dcf92ccd..f195992faa5 100644
--- a/components/script/dom/headers.rs
+++ b/components/script/dom/headers.rs
@@ -13,6 +13,7 @@ use net_traits::fetch::headers::{
is_forbidden_method,
};
use net_traits::request::is_cors_safelisted_request_header;
+use net_traits::trim_http_whitespace;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
@@ -33,7 +34,7 @@ pub(crate) struct Headers {
header_list: DomRefCell<HyperHeaders>,
}
-// https://fetch.spec.whatwg.org/#concept-headers-guard
+/// <https://fetch.spec.whatwg.org/#concept-headers-guard>
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) enum Guard {
Immutable,
@@ -66,7 +67,7 @@ impl Headers {
}
impl HeadersMethods<crate::DomTypeHolder> for Headers {
- // https://fetch.spec.whatwg.org/#dom-headers
+ /// <https://fetch.spec.whatwg.org/#dom-headers>
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
@@ -78,47 +79,41 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
Ok(dom_headers_new)
}
- // https://fetch.spec.whatwg.org/#concept-headers-append
+ /// <https://fetch.spec.whatwg.org/#concept-headers-append>
fn Append(&self, name: ByteString, value: ByteString) -> ErrorResult {
- // Step 1
- let value = normalize_value(value);
+ // 1. Normalize value.
+ let value = trim_http_whitespace(&value);
- // Step 2
- // https://fetch.spec.whatwg.org/#headers-validate
- let (mut valid_name, valid_value) = validate_name_and_value(name, value)?;
+ // 2. If validating (name, value) for headers returns false, then return.
+ let Some((mut valid_name, valid_value)) =
+ self.validate_name_and_value(name, ByteString::new(value.into()))?
+ else {
+ return Ok(());
+ };
valid_name = valid_name.to_lowercase();
- if self.guard.get() == Guard::Immutable {
- return Err(Error::Type("Guard is immutable".to_string()));
- }
- if self.guard.get() == Guard::Request &&
- is_forbidden_request_header(&valid_name, &valid_value)
- {
- return Ok(());
- }
- if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
- return Ok(());
- }
-
- // Step 3
+ // 3. If headers’s guard is "request-no-cors":
if self.guard.get() == Guard::RequestNoCors {
+ // 3.1. Let temporaryValue be the result of getting name from headers’s header list.
let tmp_value = if let Some(mut value) =
get_value_from_header_list(&valid_name, &self.header_list.borrow())
{
+ // 3.3. Otherwise, set temporaryValue to temporaryValue, followed by 0x2C 0x20, followed by value.
value.extend(b", ");
- value.extend(valid_value.clone());
+ value.extend(valid_value.to_vec());
value
} else {
- valid_value.clone()
+ // 3.2. If temporaryValue is null, then set temporaryValue to value.
+ valid_value.to_vec()
};
-
+ // 3.4. If (name, temporaryValue) is not a no-CORS-safelisted request-header, then return.
if !is_cors_safelisted_request_header(&valid_name, &tmp_value) {
return Ok(());
}
}
- // Step 4
+ // 4. Append (name, value) to headers’s header list.
match HeaderValue::from_bytes(&valid_value) {
Ok(value) => {
self.header_list
@@ -134,7 +129,7 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
},
};
- // Step 5
+ // 5. If headers’s guard is "request-no-cors", then remove privileged no-CORS request-headers from headers.
if self.guard.get() == Guard::RequestNoCors {
self.remove_privileged_no_cors_request_headers();
}
@@ -142,50 +137,53 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
Ok(())
}
- // https://fetch.spec.whatwg.org/#dom-headers-delete
+ /// <https://fetch.spec.whatwg.org/#dom-headers-delete>
fn Delete(&self, name: ByteString) -> ErrorResult {
- // Step 1
- let (mut valid_name, valid_value) = validate_name_and_value(name, ByteString::new(vec![]))?;
+ // Step 1 If validating (name, ``) for this returns false, then return.
+ let name_and_value = self.validate_name_and_value(name, ByteString::new(vec![]))?;
+ let Some((mut valid_name, _valid_value)) = name_and_value else {
+ return Ok(());
+ };
valid_name = valid_name.to_lowercase();
- // Step 2
- if self.guard.get() == Guard::Immutable {
- return Err(Error::Type("Guard is immutable".to_string()));
- }
- // Step 3
- if self.guard.get() == Guard::Request &&
- is_forbidden_request_header(&valid_name, &valid_value)
- {
- return Ok(());
- }
- // Step 4
+ // Step 2 If this’s guard is "request-no-cors", name is not a no-CORS-safelisted request-header name,
+ // and name is not a privileged no-CORS request-header name, then return.
if self.guard.get() == Guard::RequestNoCors &&
!is_cors_safelisted_request_header(&valid_name, &b"invalid".to_vec())
{
return Ok(());
}
- // Step 5
- if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
- return Ok(());
+
+ // 3. If this’s header list does not contain name, then return.
+ // 4. Delete name from this’s header list.
+ self.header_list.borrow_mut().remove(valid_name);
+
+ // 5. If this’s guard is "request-no-cors", then remove privileged no-CORS request-headers from this.
+ if self.guard.get() == Guard::RequestNoCors {
+ self.remove_privileged_no_cors_request_headers();
}
- // Step 6
- self.header_list.borrow_mut().remove(&valid_name);
+
Ok(())
}
- // https://fetch.spec.whatwg.org/#dom-headers-get
+ /// <https://fetch.spec.whatwg.org/#dom-headers-get>
fn Get(&self, name: ByteString) -> Fallible<Option<ByteString>> {
- // Step 1
+ // 1. If name is not a header name, then throw a TypeError.
let valid_name = validate_name(name)?;
+
+ // 2. Return the result of getting name from this’s header list.
Ok(
get_value_from_header_list(&valid_name, &self.header_list.borrow())
.map(ByteString::new),
)
}
- // https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
+ /// <https://fetch.spec.whatwg.org/#dom-headers-getsetcookie>
fn GetSetCookie(&self) -> Vec<ByteString> {
+ // 1. If this’s header list does not contain `Set-Cookie`, then return « ».
+ // 2. Return the values of all headers in this’s header list whose name is a
+ // byte-case-insensitive match for `Set-Cookie`, in order.
self.header_list
.borrow()
.get_all("set-cookie")
@@ -194,42 +192,36 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
.collect()
}
- // https://fetch.spec.whatwg.org/#dom-headers-has
+ /// <https://fetch.spec.whatwg.org/#dom-headers-has>
fn Has(&self, name: ByteString) -> Fallible<bool> {
- // Step 1
+ // 1. If name is not a header name, then throw a TypeError.
let valid_name = validate_name(name)?;
- // Step 2
+ // 2. Return true if this’s header list contains name; otherwise false.
Ok(self.header_list.borrow_mut().get(&valid_name).is_some())
}
- // https://fetch.spec.whatwg.org/#dom-headers-set
+ /// <https://fetch.spec.whatwg.org/#dom-headers-set>
fn Set(&self, name: ByteString, value: ByteString) -> Fallible<()> {
- // Step 1
- let value = normalize_value(value);
- // Step 2
- let (mut valid_name, valid_value) = validate_name_and_value(name, value)?;
- valid_name = valid_name.to_lowercase();
- // Step 3
- if self.guard.get() == Guard::Immutable {
- return Err(Error::Type("Guard is immutable".to_string()));
- }
- // Step 4
- if self.guard.get() == Guard::Request &&
- is_forbidden_request_header(&valid_name, &valid_value)
- {
+ // 1. Normalize value
+ let value = trim_http_whitespace(&value);
+
+ // 2. If validating (name, value) for this returns false, then return.
+ let Some((mut valid_name, valid_value)) =
+ self.validate_name_and_value(name, ByteString::new(value.into()))?
+ else {
return Ok(());
- }
- // Step 5
+ };
+ valid_name = valid_name.to_lowercase();
+
+ // 3. If this’s guard is "request-no-cors" and (name, value) is not a
+ // no-CORS-safelisted request-header, then return.
if self.guard.get() == Guard::RequestNoCors &&
- !is_cors_safelisted_request_header(&valid_name, &valid_value)
+ !is_cors_safelisted_request_header(&valid_name, &valid_value.to_vec())
{
return Ok(());
}
- // Step 6
- if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
- return Ok(());
- }
- // Step 7
+
+ // 4. Set (name, value) in this’s header list.
// https://fetch.spec.whatwg.org/#concept-header-list-set
match HeaderValue::from_bytes(&valid_value) {
Ok(value) => {
@@ -245,6 +237,12 @@ impl HeadersMethods<crate::DomTypeHolder> for Headers {
);
},
};
+
+ // 5. If this’s guard is "request-no-cors", then remove privileged no-CORS request-headers from this.
+ if self.guard.get() == Guard::RequestNoCors {
+ self.remove_privileged_no_cors_request_headers();
+ }
+
Ok(())
}
}
@@ -260,7 +258,7 @@ impl Headers {
Ok(())
}
- // https://fetch.spec.whatwg.org/#concept-headers-fill
+ /// <https://fetch.spec.whatwg.org/#concept-headers-fill>
pub(crate) fn fill(&self, filler: Option<HeadersInit>) -> ErrorResult {
match filler {
Some(HeadersInit::ByteStringSequenceSequence(v)) => {
@@ -316,12 +314,12 @@ impl Headers {
self.header_list.borrow_mut().clone()
}
- // https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
+ /// <https://fetch.spec.whatwg.org/#concept-header-extract-mime-type>
pub(crate) fn extract_mime_type(&self) -> Vec<u8> {
extract_mime_type(&self.header_list.borrow()).unwrap_or_default()
}
- // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
+ /// <https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine>
pub(crate) fn sort_and_combine(&self) -> Vec<(String, Vec<u8>)> {
let borrowed_header_list = self.header_list.borrow();
let mut header_vec = vec![];
@@ -341,11 +339,38 @@ impl Headers {
header_vec
}
- // https://fetch.spec.whatwg.org/#ref-for-privileged-no-cors-request-header-name
+ /// <https://fetch.spec.whatwg.org/#ref-for-privileged-no-cors-request-header-name>
pub(crate) fn remove_privileged_no_cors_request_headers(&self) {
- // https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name
+ // <https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name>
self.header_list.borrow_mut().remove("range");
}
+
+ /// <https://fetch.spec.whatwg.org/#headers-validate>
+ pub(crate) fn validate_name_and_value(
+ &self,
+ name: ByteString,
+ value: ByteString,
+ ) -> Fallible<Option<(String, ByteString)>> {
+ // 1. If name is not a header name or value is not a header value, then throw a TypeError.
+ let valid_name = validate_name(name)?;
+ if !is_legal_header_value(&value) {
+ return Err(Error::Type("Header value is not valid".to_string()));
+ }
+ // 2. If headers’s guard is "immutable", then throw a TypeError.
+ if self.guard.get() == Guard::Immutable {
+ return Err(Error::Type("Guard is immutable".to_string()));
+ }
+ // 3. If headers’s guard is "request" and (name, value) is a forbidden request-header, then return false.
+ if self.guard.get() == Guard::Request && is_forbidden_request_header(&valid_name, &value) {
+ return Ok(None);
+ }
+ // 4. If headers’s guard is "response" and name is a forbidden response-header name, then return false.
+ if self.guard.get() == Guard::Response && is_forbidden_response_header(&valid_name) {
+ return Ok(None);
+ }
+
+ Ok(Some((valid_name, value)))
+ }
}
impl Iterable for Headers {
@@ -391,6 +416,7 @@ pub(crate) fn is_forbidden_request_header(name: &str, value: &[u8]) -> bool {
"keep-alive",
"origin",
"referer",
+ "set-cookie",
"te",
"trailer",
"transfer-encoding",
@@ -448,26 +474,11 @@ pub(crate) fn is_forbidden_request_header(name: &str, value: &[u8]) -> bool {
false
}
-// https://fetch.spec.whatwg.org/#forbidden-response-header-name
+/// <https://fetch.spec.whatwg.org/#forbidden-response-header-name>
fn is_forbidden_response_header(name: &str) -> bool {
- matches!(name, "set-cookie" | "set-cookie2")
-}
-
-// There is some unresolved confusion over the definition of a name and a value.
-//
-// As of December 2019, WHATWG has no formal grammar production for value;
-// https://fetch.spec.whatg.org/#concept-header-value just says not to have
-// newlines, nulls, or leading/trailing whitespace. It even allows
-// octets that aren't a valid UTF-8 encoding, and WPT tests reflect this.
-// The HeaderValue class does not fully reflect this, so headers
-// containing bytes with values 1..31 or 127 can't be created, failing
-// WPT tests but probably not affecting anything important on the real Internet.
-fn validate_name_and_value(name: ByteString, value: ByteString) -> Fallible<(String, Vec<u8>)> {
- let valid_name = validate_name(name)?;
- if !is_legal_header_value(&value) {
- return Err(Error::Type("Header value is not valid".to_string()));
- }
- Ok((valid_name, value.into()))
+ // A forbidden response-header name is a header name that is a byte-case-insensitive match for one of
+ let name = name.to_ascii_lowercase();
+ matches!(name.as_str(), "set-cookie" | "set-cookie2")
}
fn validate_name(name: ByteString) -> Fallible<String> {
@@ -480,47 +491,20 @@ fn validate_name(name: ByteString) -> Fallible<String> {
}
}
-// Removes trailing and leading HTTP whitespace bytes.
-// https://fetch.spec.whatwg.org/#concept-header-value-normalize
-pub fn normalize_value(value: ByteString) -> ByteString {
- match (
- index_of_first_non_whitespace(&value),
- index_of_last_non_whitespace(&value),
- ) {
- (Some(begin), Some(end)) => ByteString::new(value[begin..end + 1].to_owned()),
- _ => ByteString::new(vec![]),
- }
-}
-
-fn is_http_whitespace(byte: u8) -> bool {
- byte == b'\t' || byte == b'\n' || byte == b'\r' || byte == b' '
-}
-
-fn index_of_first_non_whitespace(value: &ByteString) -> Option<usize> {
- for (index, &byte) in value.iter().enumerate() {
- if !is_http_whitespace(byte) {
- return Some(index);
- }
- }
- None
-}
-
-fn index_of_last_non_whitespace(value: &ByteString) -> Option<usize> {
- for (index, &byte) in value.iter().enumerate().rev() {
- if !is_http_whitespace(byte) {
- return Some(index);
- }
- }
- None
-}
-
-// http://tools.ietf.org/html/rfc7230#section-3.2
+/// <http://tools.ietf.org/html/rfc7230#section-3.2>
fn is_field_name(name: &ByteString) -> bool {
is_token(name)
}
-// https://fetch.spec.whatg.org/#concept-header-value
-fn is_legal_header_value(value: &ByteString) -> bool {
+// As of December 2019, WHATWG has no formal grammar production for value;
+// https://fetch.spec.whatg.org/#concept-header-value just says not to have
+// newlines, nulls, or leading/trailing whitespace. It even allows
+// octets that aren't a valid UTF-8 encoding, and WPT tests reflect this.
+// The HeaderValue class does not fully reflect this, so headers
+// containing bytes with values 1..31 or 127 can't be created, failing
+// WPT tests but probably not affecting anything important on the real Internet.
+/// <https://fetch.spec.whatg.org/#concept-header-value>
+fn is_legal_header_value(value: &[u8]) -> bool {
let value_len = value.len();
if value_len == 0 {
return true;
@@ -533,7 +517,7 @@ fn is_legal_header_value(value: &ByteString) -> bool {
b' ' | b'\t' => return false,
_ => {},
};
- for &ch in &value[..] {
+ for &ch in value {
match ch {
b'\0' | b'\n' | b'\r' => return false,
_ => {},
@@ -555,12 +539,12 @@ fn is_legal_header_value(value: &ByteString) -> bool {
// }
}
-// https://tools.ietf.org/html/rfc5234#appendix-B.1
+/// <https://tools.ietf.org/html/rfc5234#appendix-B.1>
pub(crate) fn is_vchar(x: u8) -> bool {
matches!(x, 0x21..=0x7E)
}
-// http://tools.ietf.org/html/rfc7230#section-3.2.6
+/// <http://tools.ietf.org/html/rfc7230#section-3.2.6>
pub(crate) fn is_obs_text(x: u8) -> bool {
matches!(x, 0x80..=0xFF)
}
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index 27da9f2b537..499e91c127b 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -27,6 +27,7 @@ use servo_media::streams::registry::MediaStreamId;
use snapshot::Snapshot;
use style::attr::AttrValue;
+use super::node::NodeDamage;
pub(crate) use crate::canvas_context::*;
use crate::conversions::Convert;
use crate::dom::attr::Attr;
@@ -687,8 +688,11 @@ impl VirtualMethods for HTMLCanvasElement {
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
match attr.local_name() {
- &local_name!("width") | &local_name!("height") => self.recreate_contexts_after_resize(),
- _ => (),
+ &local_name!("width") | &local_name!("height") => {
+ self.recreate_contexts_after_resize();
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ },
+ _ => {},
};
}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 3c78ba67772..c6a3e2227f5 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -520,6 +520,7 @@ impl HTMLInputElement {
let mut value = textinput.single_line_content().clone();
self.sanitize_value(&mut value);
textinput.set_content(value);
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
fn does_minmaxlength_apply(&self) -> bool {
@@ -2668,6 +2669,7 @@ impl VirtualMethods for HTMLInputElement {
let mut value = textinput.single_line_content().clone();
self.sanitize_value(&mut value);
textinput.set_content(value);
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
// Steps 7-9
if !previously_selectable && self.selection_api_applies() {
@@ -2695,6 +2697,8 @@ impl VirtualMethods for HTMLInputElement {
self.sanitize_value(&mut value);
self.textinput.borrow_mut().set_content(value);
self.update_placeholder_shown_state();
+
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
},
local_name!("name") if self.input_type() == InputType::Radio => {
self.radio_group_updated(
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 391da272ef3..d1791620592 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -1369,7 +1369,6 @@ impl HTMLMediaElement {
.lock()
.unwrap()
.render_poster_frame(image);
- self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
if pref!(media_testing_enabled) {
self.owner_global()
@@ -1618,7 +1617,6 @@ impl HTMLMediaElement {
// TODO: 6. Abort the overall resource selection algorithm.
},
PlayerEvent::VideoFrameUpdated => {
- self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
// Check if the frame was resized
if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
self.handle_resize(Some(frame.width as u32), Some(frame.height as u32));
@@ -2017,12 +2015,12 @@ impl HTMLMediaElement {
pub(crate) fn clear_current_frame_data(&self) {
self.handle_resize(None, None);
self.video_renderer.lock().unwrap().current_frame = None;
- self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
fn handle_resize(&self, width: Option<u32>, height: Option<u32>) {
if let Some(video_elem) = self.downcast::<HTMLVideoElement>() {
video_elem.resize(width, height);
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
}
diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs
index 8b553923230..78f00132580 100644
--- a/components/script/dom/htmltablecellelement.rs
+++ b/components/script/dom/htmltablecellelement.rs
@@ -9,6 +9,9 @@ use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::color::AbsoluteColor;
use style::context::QuirksMode;
+use super::attr::Attr;
+use super::element::AttributeMutation;
+use super::node::NodeDamage;
use crate::dom::bindings::codegen::Bindings::HTMLTableCellElementBinding::HTMLTableCellElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::inheritance::Castable;
@@ -174,6 +177,19 @@ impl VirtualMethods for HTMLTableCellElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
+ if let Some(super_type) = self.super_type() {
+ super_type.attribute_mutated(attr, mutation, can_gc);
+ }
+
+ if matches!(*attr.local_name(), local_name!("colspan")) {
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ }
+ if matches!(*attr.local_name(), local_name!("rowspan")) {
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ }
+ }
+
fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue {
match *local_name {
local_name!("colspan") => {
diff --git a/components/script/dom/htmltablecolelement.rs b/components/script/dom/htmltablecolelement.rs
index 9e8eecf1147..70355f274fc 100644
--- a/components/script/dom/htmltablecolelement.rs
+++ b/components/script/dom/htmltablecolelement.rs
@@ -7,8 +7,10 @@ use html5ever::{LocalName, Prefix, local_name, ns};
use js::rust::HandleObject;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
+use super::attr::Attr;
use super::bindings::root::LayoutDom;
-use super::element::Element;
+use super::element::{AttributeMutation, Element};
+use super::node::NodeDamage;
use crate::dom::bindings::codegen::Bindings::HTMLTableColElementBinding::HTMLTableColElementMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
@@ -93,6 +95,16 @@ impl VirtualMethods for HTMLTableColElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
+ if let Some(super_type) = self.super_type() {
+ super_type.attribute_mutated(attr, mutation, can_gc);
+ }
+
+ if matches!(*attr.local_name(), local_name!("span")) {
+ self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ }
+ }
+
fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue {
match *local_name {
local_name!("span") => {
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index 98170f9655b..b82051b3b12 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -619,7 +619,8 @@ impl WebGLRenderingContext {
let size = Size2D::new(img.width, img.height);
- TexPixels::new(img.bytes(), size, img.format, false)
+ let data = IpcSharedMemory::from_bytes(img.first_frame().bytes);
+ TexPixels::new(data, size, img.format, false)
},
// TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
// but we need to refactor it moving it to `HTMLCanvasElement` and support
diff --git a/components/script/dom/webgpu/gpu.rs b/components/script/dom/webgpu/gpu.rs
index 20380e07bfb..b2534cda9a8 100644
--- a/components/script/dom/webgpu/gpu.rs
+++ b/components/script/dom/webgpu/gpu.rs
@@ -86,8 +86,12 @@ impl GPUMethods<crate::DomTypeHolder> for GPU {
/// <https://gpuweb.github.io/gpuweb/#dom-gpu-getpreferredcanvasformat>
fn GetPreferredCanvasFormat(&self) -> GPUTextureFormat {
- // TODO: real implementation
- GPUTextureFormat::Rgba8unorm
+ // From https://github.com/mozilla-firefox/firefox/blob/24d49101ce17b78c3ba1217d00297fe2891be6b3/dom/webgpu/Instance.h#L68
+ if cfg!(target_os = "android") {
+ GPUTextureFormat::Rgba8unorm
+ } else {
+ GPUTextureFormat::Bgra8unorm
+ }
}
/// <https://www.w3.org/TR/webgpu/#dom-gpu-wgsllanguagefeatures>
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 4815e6feae1..3ee5bfbd662 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -1081,6 +1081,10 @@ impl ScriptThread {
for event in document.take_pending_input_events().into_iter() {
document.update_active_keyboard_modifiers(event.active_keyboard_modifiers);
+ // We do this now, because the event is consumed below, but the order doesn't really
+ // matter as the event will be handled before any new ScriptThread messages are processed.
+ self.notify_webdriver_input_event_completed(pipeline_id, &event.event);
+
match event.event {
InputEvent::MouseButton(mouse_button_event) => {
document.handle_mouse_button_event(
@@ -1144,6 +1148,19 @@ impl ScriptThread {
ScriptThread::set_user_interacting(false);
}
+ fn notify_webdriver_input_event_completed(&self, pipeline_id: PipelineId, event: &InputEvent) {
+ let Some(id) = event.webdriver_message_id() else {
+ return;
+ };
+
+ if let Err(error) = self.senders.pipeline_to_constellation_sender.send((
+ pipeline_id,
+ ScriptToConstellationMessage::WebDriverInputComplete(id),
+ )) {
+ warn!("ScriptThread failed to send WebDriverInputComplete {id:?}: {error:?}",);
+ }
+ }
+
/// <https://html.spec.whatwg.org/multipage/#update-the-rendering>
///
/// Attempt to update the rendering and then do a microtask checkpoint if rendering was actually
@@ -3420,7 +3437,7 @@ impl ScriptThread {
// the pointer, when the user presses down and releases the primary pointer button"
// Servo-specific: Trigger if within 10px of the down point
- if let InputEvent::MouseButton(mouse_button_event) = event.event {
+ if let InputEvent::MouseButton(mouse_button_event) = &event.event {
if let MouseButton::Left = mouse_button_event.button {
match mouse_button_event.action {
MouseButtonAction::Up => {
@@ -3429,16 +3446,23 @@ impl ScriptThread {
let pixel_dist =
(pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt();
if pixel_dist < 10.0 * document.window().device_pixel_ratio().get() {
- document.note_pending_input_event(event.clone());
+ // Pass webdriver_id to the newly generated click event
+ document.note_pending_input_event(ConstellationInputEvent {
+ hit_test_result: event.hit_test_result.clone(),
+ pressed_mouse_buttons: event.pressed_mouse_buttons,
+ active_keyboard_modifiers: event.active_keyboard_modifiers,
+ event: event.event.clone().with_webdriver_message_id(None),
+ });
document.note_pending_input_event(ConstellationInputEvent {
hit_test_result: event.hit_test_result,
pressed_mouse_buttons: event.pressed_mouse_buttons,
active_keyboard_modifiers: event.active_keyboard_modifiers,
- event: InputEvent::MouseButton(MouseButtonEvent {
- action: MouseButtonAction::Click,
- button: mouse_button_event.button,
- point: mouse_button_event.point,
- }),
+ event: InputEvent::MouseButton(MouseButtonEvent::new(
+ MouseButtonAction::Click,
+ mouse_button_event.button,
+ mouse_button_event.point,
+ ))
+ .with_webdriver_message_id(event.event.webdriver_message_id()),
});
return;
}
diff --git a/components/script/test.rs b/components/script/test.rs
index c5933696efc..35e05621125 100644
--- a/components/script/test.rs
+++ b/components/script/test.rs
@@ -7,7 +7,6 @@
pub use crate::dom::bindings::refcounted::TrustedPromise;
//pub use crate::dom::bindings::root::Dom;
pub use crate::dom::bindings::str::{ByteString, DOMString};
-pub use crate::dom::headers::normalize_value;
//pub use crate::dom::node::Node;
pub mod area {
diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs
index 061dfe023df..d88217142cc 100644
--- a/components/shared/compositing/lib.rs
+++ b/components/shared/compositing/lib.rs
@@ -10,6 +10,7 @@ use base::id::{PipelineId, WebViewId};
use crossbeam_channel::Sender;
use embedder_traits::{
AnimationState, EventLoopWaker, MouseButton, MouseButtonAction, TouchEventResult,
+ WebDriverMessageId,
};
use euclid::Rect;
use ipc_channel::ipc::IpcSender;
@@ -101,9 +102,16 @@ pub enum CompositorMsg {
/// The load of a page has completed
LoadComplete(WebViewId),
/// WebDriver mouse button event
- WebDriverMouseButtonEvent(WebViewId, MouseButtonAction, MouseButton, f32, f32),
+ WebDriverMouseButtonEvent(
+ WebViewId,
+ MouseButtonAction,
+ MouseButton,
+ f32,
+ f32,
+ WebDriverMessageId,
+ ),
/// WebDriver mouse move event
- WebDriverMouseMoveEvent(WebViewId, f32, f32),
+ WebDriverMouseMoveEvent(WebViewId, f32, f32, WebDriverMessageId),
// Webdriver wheel scroll event
WebDriverWheelScrollEvent(WebViewId, f32, f32, f64, f64),
diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs
index a5424abe6d1..fa391f93859 100644
--- a/components/shared/constellation/from_script_message.rs
+++ b/components/shared/constellation/from_script_message.rs
@@ -17,6 +17,7 @@ use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, Worke
use embedder_traits::{
AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError,
JavaScriptEvaluationId, MediaSessionEvent, TouchEventResult, ViewportDetails,
+ WebDriverMessageId,
};
use euclid::default::Size2D as UntypedSize2D;
use http::{HeaderMap, Method};
@@ -652,6 +653,8 @@ pub enum ScriptToConstellationMessage {
JavaScriptEvaluationId,
Result<JSValue, JavaScriptEvaluationError>,
),
+ /// Notify the completion of a webdriver command.
+ WebDriverInputComplete(WebDriverMessageId),
}
impl fmt::Debug for ScriptToConstellationMessage {
diff --git a/components/shared/embedder/input_events.rs b/components/shared/embedder/input_events.rs
index acaa9afb3ff..869c4eee004 100644
--- a/components/shared/embedder/input_events.rs
+++ b/components/shared/embedder/input_events.rs
@@ -8,6 +8,8 @@ use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use webrender_api::units::DevicePoint;
+use crate::WebDriverMessageId;
+
/// An input event that is sent from the embedder to Servo.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum InputEvent {
@@ -42,6 +44,38 @@ impl InputEvent {
InputEvent::Wheel(event) => Some(event.point),
}
}
+
+ pub fn webdriver_message_id(&self) -> Option<WebDriverMessageId> {
+ match self {
+ InputEvent::EditingAction(..) => None,
+ InputEvent::Gamepad(..) => None,
+ InputEvent::Ime(..) => None,
+ InputEvent::Keyboard(..) => None,
+ InputEvent::MouseButton(event) => event.webdriver_id,
+ InputEvent::MouseMove(event) => event.webdriver_id,
+ InputEvent::Touch(..) => None,
+ InputEvent::Wheel(..) => None,
+ }
+ }
+
+ pub fn with_webdriver_message_id(self, webdriver_id: Option<WebDriverMessageId>) -> Self {
+ match self {
+ InputEvent::EditingAction(..) => {},
+ InputEvent::Gamepad(..) => {},
+ InputEvent::Ime(..) => {},
+ InputEvent::Keyboard(..) => {},
+ InputEvent::MouseButton(mut event) => {
+ event.webdriver_id = webdriver_id;
+ },
+ InputEvent::MouseMove(mut event) => {
+ event.webdriver_id = webdriver_id;
+ },
+ InputEvent::Touch(..) => {},
+ InputEvent::Wheel(..) => {},
+ };
+
+ self
+ }
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@@ -49,6 +83,18 @@ pub struct MouseButtonEvent {
pub action: MouseButtonAction,
pub button: MouseButton,
pub point: DevicePoint,
+ webdriver_id: Option<WebDriverMessageId>,
+}
+
+impl MouseButtonEvent {
+ pub fn new(action: MouseButtonAction, button: MouseButton, point: DevicePoint) -> Self {
+ Self {
+ action,
+ button,
+ point,
+ webdriver_id: None,
+ }
+ }
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@@ -102,6 +148,16 @@ pub enum MouseButtonAction {
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct MouseMoveEvent {
pub point: DevicePoint,
+ webdriver_id: Option<WebDriverMessageId>,
+}
+
+impl MouseMoveEvent {
+ pub fn new(point: DevicePoint) -> Self {
+ Self {
+ point,
+ webdriver_id: None,
+ }
+ }
}
/// The type of input represented by a multi-touch event.
diff --git a/components/shared/embedder/webdriver.rs b/components/shared/embedder/webdriver.rs
index 3716a29951a..e7118d32737 100644
--- a/components/shared/embedder/webdriver.rs
+++ b/components/shared/embedder/webdriver.rs
@@ -24,6 +24,9 @@ use webrender_api::units::DeviceIntSize;
use crate::{MouseButton, MouseButtonAction};
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct WebDriverMessageId(pub usize);
+
/// Messages to the constellation originating from the WebDriver server.
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverCommandMsg {
@@ -41,9 +44,23 @@ pub enum WebDriverCommandMsg {
/// Act as if keys were pressed or release in the browsing context with the given ID.
KeyboardAction(BrowsingContextId, KeyboardEvent),
/// Act as if the mouse was clicked in the browsing context with the given ID.
- MouseButtonAction(WebViewId, MouseButtonAction, MouseButton, f32, f32),
+ MouseButtonAction(
+ WebViewId,
+ MouseButtonAction,
+ MouseButton,
+ f32,
+ f32,
+ WebDriverMessageId,
+ IpcSender<WebDriverCommandResponse>,
+ ),
/// Act as if the mouse was moved in the browsing context with the given ID.
- MouseMoveAction(WebViewId, f32, f32),
+ MouseMoveAction(
+ WebViewId,
+ f32,
+ f32,
+ WebDriverMessageId,
+ IpcSender<WebDriverCommandResponse>,
+ ),
/// Act as if the mouse wheel is scrolled in the browsing context given the given ID.
WheelScrollAction(WebViewId, f32, f32, f64, f64),
/// Set the window size.
@@ -189,6 +206,11 @@ pub enum WebDriverFrameId {
}
#[derive(Debug, Deserialize, Serialize)]
+pub struct WebDriverCommandResponse {
+ pub id: WebDriverMessageId,
+}
+
+#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverLoadStatus {
Complete,
Timeout,
diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs
index 7965120b0fd..cde418f920c 100644
--- a/components/webdriver_server/actions.rs
+++ b/components/webdriver_server/actions.rs
@@ -98,20 +98,83 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-actions
pub(crate) fn dispatch_actions(
- &mut self,
+ &self,
actions_by_tick: &[ActionSequence],
) -> Result<(), ErrorStatus> {
+ // Step 1. Wait for an action queue token with input state.
+ let new_token = self.id_generator.next();
+ assert!(self.current_action_id.get().is_none());
+ self.current_action_id.set(Some(new_token));
+
+ // Step 2. Let actions result be the result of dispatch actions inner.
+ let res = self.dispatch_actions_inner(actions_by_tick);
+
+ // Step 3. Dequeue input state's actions queue.
+ self.current_action_id.set(None);
+
+ // Step 4. Return actions result.
+ res
+ }
+
+ // https://w3c.github.io/webdriver/#dfn-dispatch-actions-inner
+ fn dispatch_actions_inner(
+ &self,
+ actions_by_tick: &[ActionSequence],
+ ) -> Result<(), ErrorStatus> {
+ // Step 1. For each item tick actions in actions by tick
for tick_actions in actions_by_tick.iter() {
+ // Step 1.2. Let tick duration be the result of
+ // computing the tick duration with argument tick actions.
let tick_duration = compute_tick_duration(tick_actions);
+
+ // Step 1.3. Try to dispatch tick actions
self.dispatch_tick_actions(tick_actions, tick_duration)?;
+
+ // Step 1.4. Wait for
+ // The user agent event loop has spun enough times to process the DOM events
+ // generated by the last invocation of the dispatch tick actions steps.
+ //
+ // To ensure we wait for all events to be processed, only the last event in
+ // this tick action step holds the message id.
+ // Whenever a new event is generated, the message id is passed to it.
+ //
+ // TO-DO: remove the first match after webdriver_id is implemented in all commands
+ match tick_actions.actions {
+ ActionsType::Key { .. } | ActionsType::Wheel { .. } | ActionsType::Null { .. } => {
+ return Ok(());
+ },
+ _ => {},
+ }
+
+ match self.constellation_receiver.recv() {
+ Ok(response) => {
+ let current_waiting_id = self
+ .current_action_id
+ .get()
+ .expect("Current id should be set before dispat_actions_inner is called");
+
+ if current_waiting_id != response.id {
+ dbg!("Dispatch actions completed with wrong id in response");
+ return Err(ErrorStatus::UnknownError);
+ }
+ },
+ Err(error) => {
+ dbg!("Dispatch actions completed with IPC error: {:?}", error);
+ return Err(ErrorStatus::UnknownError);
+ },
+ };
}
+
+ // Step 2. Return success with data null.
+ dbg!("Dispatch actions completed successfully");
Ok(())
}
- fn dispatch_general_action(&mut self, source_id: &str) {
- self.session_mut()
+ fn dispatch_general_action(&self, source_id: &str) {
+ self.session()
.unwrap()
.input_state_table
+ .borrow_mut()
.entry(source_id.to_string())
.or_insert(InputSourceState::Null);
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pause-action
@@ -120,7 +183,7 @@ impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-tick-actions
fn dispatch_tick_actions(
- &mut self,
+ &self,
tick_actions: &ActionSequence,
tick_duration: u64,
) -> Result<(), ErrorStatus> {
@@ -138,9 +201,10 @@ impl Handler {
self.dispatch_general_action(source_id);
},
KeyActionItem::Key(action) => {
- self.session_mut()
+ self.session()
.unwrap()
.input_state_table
+ .borrow_mut()
.entry(source_id.to_string())
.or_insert(InputSourceState::Key(KeyInputState::new()));
match action {
@@ -149,7 +213,7 @@ impl Handler {
// Step 9. If subtype is "keyDown", append a copy of action
// object with the subtype property changed to "keyUp" to
// input state's input cancel list.
- self.session_mut().unwrap().input_cancel_list.push(
+ self.session().unwrap().input_cancel_list.borrow_mut().push(
ActionSequence {
id: source_id.into(),
actions: ActionsType::Key {
@@ -180,9 +244,10 @@ impl Handler {
self.dispatch_general_action(source_id);
},
PointerActionItem::Pointer(action) => {
- self.session_mut()
+ self.session()
.unwrap()
.input_state_table
+ .borrow_mut()
.entry(source_id.to_string())
.or_insert(InputSourceState::Pointer(PointerInputState::new(
&parameters.pointer_type,
@@ -195,7 +260,7 @@ impl Handler {
// Step 10. If subtype is "pointerDown", append a copy of action
// object with the subtype property changed to "pointerUp" to
// input state's input cancel list.
- self.session_mut().unwrap().input_cancel_list.push(
+ self.session().unwrap().input_cancel_list.borrow_mut().push(
ActionSequence {
id: source_id.into(),
actions: ActionsType::Pointer {
@@ -232,9 +297,10 @@ impl Handler {
self.dispatch_general_action(source_id)
},
WheelActionItem::Wheel(action) => {
- self.session_mut()
+ self.session()
.unwrap()
.input_state_table
+ .borrow_mut()
.entry(source_id.to_string())
.or_insert(InputSourceState::Wheel);
match action {
@@ -252,12 +318,25 @@ impl Handler {
}
// https://w3c.github.io/webdriver/#dfn-dispatch-a-keydown-action
- fn dispatch_keydown_action(&mut self, source_id: &str, action: &KeyDownAction) {
- // Step 1
+ fn dispatch_keydown_action(&self, source_id: &str, action: &KeyDownAction) {
+ let session = self.session().unwrap();
+
let raw_key = action.value.chars().next().unwrap();
- let key_input_state = self.get_key_input_state_mut(source_id);
+ let mut input_state_table = session.input_state_table.borrow_mut();
+ let key_input_state = match input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Key(key_input_state) => key_input_state,
+ _ => unreachable!(),
+ };
+
+ session.input_cancel_list.borrow_mut().push(ActionSequence {
+ id: source_id.into(),
+ actions: ActionsType::Key {
+ actions: vec![KeyActionItem::Key(KeyAction::Up(KeyUpAction {
+ value: action.value.clone(),
+ }))],
+ },
+ });
- // Step 2 - 11. Done by `keyboard-types` crate.
let keyboard_event = key_input_state.dispatch_keydown(raw_key);
// Step 12
@@ -271,12 +350,25 @@ impl Handler {
}
// https://w3c.github.io/webdriver/#dfn-dispatch-a-keyup-action
- fn dispatch_keyup_action(&mut self, source_id: &str, action: &KeyUpAction) {
- // Step 1
+ fn dispatch_keyup_action(&self, source_id: &str, action: &KeyUpAction) {
+ let session = self.session().unwrap();
+
let raw_key = action.value.chars().next().unwrap();
- let key_input_state = self.get_key_input_state_mut(source_id);
+ let mut input_state_table = session.input_state_table.borrow_mut();
+ let key_input_state = match input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Key(key_input_state) => key_input_state,
+ _ => unreachable!(),
+ };
+
+ session.input_cancel_list.borrow_mut().push(ActionSequence {
+ id: source_id.into(),
+ actions: ActionsType::Key {
+ actions: vec![KeyActionItem::Key(KeyAction::Up(KeyUpAction {
+ value: action.value.clone(),
+ }))],
+ },
+ });
- // Step 2 - 11. Done by `keyboard-types` crate.
if let Some(keyboard_event) = key_input_state.dispatch_keyup(raw_key) {
// Step 12
let cmd_msg = WebDriverCommandMsg::KeyboardAction(
@@ -289,44 +381,49 @@ impl Handler {
}
}
- fn get_pointer_input_state_mut(&mut self, source_id: &str) -> &mut PointerInputState {
- let session = self.session_mut().unwrap();
- let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
- _ => unreachable!(),
- };
- pointer_input_state
- }
+ /// <https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerdown-action>
+ pub(crate) fn dispatch_pointerdown_action(&self, source_id: &str, action: &PointerDownAction) {
+ let session = self.session().unwrap();
- fn get_key_input_state_mut(&mut self, source_id: &str) -> &mut KeyInputState {
- let session = self.session_mut().unwrap();
- let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Key(key_input_state) => key_input_state,
+ let mut input_state_table = session.input_state_table.borrow_mut();
+ let pointer_input_state = match input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
_ => unreachable!(),
};
- key_input_state
- }
-
- // https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerdown-action
- pub(crate) fn dispatch_pointerdown_action(
- &mut self,
- source_id: &str,
- action: &PointerDownAction,
- ) {
- let webview_id = self.session().unwrap().webview_id;
- let pointer_input_state = self.get_pointer_input_state_mut(source_id);
if pointer_input_state.pressed.contains(&action.button) {
return;
}
pointer_input_state.pressed.insert(action.button);
+ session.input_cancel_list.borrow_mut().push(ActionSequence {
+ id: source_id.into(),
+ actions: ActionsType::Pointer {
+ parameters: PointerActionParameters {
+ pointer_type: match pointer_input_state.subtype {
+ PointerType::Mouse => PointerType::Mouse,
+ PointerType::Pen => PointerType::Pen,
+ PointerType::Touch => PointerType::Touch,
+ },
+ },
+ actions: vec![PointerActionItem::Pointer(PointerAction::Up(
+ PointerUpAction {
+ button: action.button,
+ ..Default::default()
+ },
+ ))],
+ },
+ });
+
+ let msg_id = self.current_action_id.get().unwrap();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
- webview_id,
+ session.webview_id,
MouseButtonAction::Down,
action.button.into(),
pointer_input_state.x as f32,
pointer_input_state.y as f32,
+ msg_id,
+ self.constellation_sender.clone(),
);
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
@@ -334,21 +431,48 @@ impl Handler {
}
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action
- pub(crate) fn dispatch_pointerup_action(&mut self, source_id: &str, action: &PointerUpAction) {
- let webview_id = self.session().unwrap().webview_id;
- let pointer_input_state = self.get_pointer_input_state_mut(source_id);
+ pub(crate) fn dispatch_pointerup_action(&self, source_id: &str, action: &PointerUpAction) {
+ let session = self.session().unwrap();
+
+ let mut input_state_table = session.input_state_table.borrow_mut();
+ let pointer_input_state = match input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ _ => unreachable!(),
+ };
if !pointer_input_state.pressed.contains(&action.button) {
return;
}
pointer_input_state.pressed.remove(&action.button);
+ session.input_cancel_list.borrow_mut().push(ActionSequence {
+ id: source_id.into(),
+ actions: ActionsType::Pointer {
+ parameters: PointerActionParameters {
+ pointer_type: match pointer_input_state.subtype {
+ PointerType::Mouse => PointerType::Mouse,
+ PointerType::Pen => PointerType::Pen,
+ PointerType::Touch => PointerType::Touch,
+ },
+ },
+ actions: vec![PointerActionItem::Pointer(PointerAction::Down(
+ PointerDownAction {
+ button: action.button,
+ ..Default::default()
+ },
+ ))],
+ },
+ });
+
+ let msg_id = self.current_action_id.get().unwrap();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
- webview_id,
+ session.webview_id,
MouseButtonAction::Up,
action.button.into(),
pointer_input_state.x as f32,
pointer_input_state.y as f32,
+ msg_id,
+ self.constellation_sender.clone(),
);
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
@@ -357,7 +481,7 @@ impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action
pub(crate) fn dispatch_pointermove_action(
- &mut self,
+ &self,
source_id: &str,
action: &PointerMoveAction,
tick_duration: u64,
@@ -370,10 +494,10 @@ impl Handler {
// Steps 3 - 4
let (start_x, start_y) = match self
- .session
- .as_ref()
+ .session()
.unwrap()
.input_state_table
+ .borrow_mut()
.get(source_id)
.unwrap()
{
@@ -416,7 +540,7 @@ impl Handler {
/// <https://w3c.github.io/webdriver/#dfn-perform-a-pointer-move>
#[allow(clippy::too_many_arguments)]
fn perform_pointer_move(
- &mut self,
+ &self,
source_id: &str,
duration: u64,
start_x: f64,
@@ -425,9 +549,13 @@ impl Handler {
target_y: f64,
tick_start: Instant,
) {
- let webview_id = self.session().unwrap().webview_id;
- let constellation_chan = self.constellation_chan.clone();
- let pointer_input_state = self.get_pointer_input_state_mut(source_id);
+ let session = self.session().unwrap();
+ let mut input_state_table = session.input_state_table.borrow_mut();
+ let pointer_input_state = match input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ _ => unreachable!(),
+ };
+
loop {
// Step 1
let time_delta = tick_start.elapsed().as_millis();
@@ -459,9 +587,15 @@ impl Handler {
// Step 7
if x != current_x || y != current_y {
// Step 7.2
- let cmd_msg = WebDriverCommandMsg::MouseMoveAction(webview_id, x as f32, y as f32);
- //TODO: Need Synchronization here before updating `pointer_input_state`
- constellation_chan
+ let msg_id = self.current_action_id.get().unwrap();
+ let cmd_msg = WebDriverCommandMsg::MouseMoveAction(
+ session.webview_id,
+ x as f32,
+ y as f32,
+ msg_id,
+ self.constellation_sender.clone(),
+ );
+ self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
// Step 7.3
@@ -481,7 +615,7 @@ impl Handler {
/// <https://w3c.github.io/webdriver/#dfn-dispatch-a-scroll-action>
fn dispatch_scroll_action(
- &mut self,
+ &self,
action: &WheelScrollAction,
tick_duration: u64,
) -> Result<(), ErrorStatus> {
@@ -546,7 +680,7 @@ impl Handler {
/// <https://w3c.github.io/webdriver/#dfn-perform-a-scroll>
#[allow(clippy::too_many_arguments)]
fn perform_scroll(
- &mut self,
+ &self,
duration: u64,
x: i64,
y: i64,
@@ -556,7 +690,7 @@ impl Handler {
mut curr_delta_y: i64,
tick_start: Instant,
) {
- let session = self.session_mut().unwrap();
+ let session = self.session().unwrap();
// Step 1
let time_delta = tick_start.elapsed().as_millis();
diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs
index 5735594b058..0e3fa9058d6 100644
--- a/components/webdriver_server/lib.rs
+++ b/components/webdriver_server/lib.rs
@@ -10,11 +10,12 @@ mod actions;
mod capabilities;
use std::borrow::ToOwned;
+use std::cell::{Cell, RefCell};
use std::collections::{BTreeMap, HashMap};
use std::io::Cursor;
use std::net::{SocketAddr, SocketAddrV4};
use std::time::Duration;
-use std::{env, fmt, mem, process, thread};
+use std::{env, fmt, process, thread};
use base::id::{BrowsingContextId, WebViewId};
use base64::Engine;
@@ -23,8 +24,9 @@ use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
use cookie::{CookieBuilder, Expiration};
use crossbeam_channel::{Receiver, Sender, after, select, unbounded};
use embedder_traits::{
- MouseButton, WebDriverCommandMsg, WebDriverCookieError, WebDriverFrameId, WebDriverJSError,
- WebDriverJSResult, WebDriverJSValue, WebDriverLoadStatus, WebDriverScriptCommand,
+ MouseButton, WebDriverCommandMsg, WebDriverCommandResponse, WebDriverCookieError,
+ WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue, WebDriverLoadStatus,
+ WebDriverMessageId, WebDriverScriptCommand,
};
use euclid::{Rect, Size2D};
use http::method::Method;
@@ -43,8 +45,8 @@ use servo_url::ServoUrl;
use style_traits::CSSPixel;
use uuid::Uuid;
use webdriver::actions::{
- ActionSequence, PointerDownAction, PointerMoveAction, PointerOrigin, PointerType,
- PointerUpAction,
+ ActionSequence, ActionsType, PointerAction, PointerActionItem, PointerActionParameters,
+ PointerDownAction, PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
};
use webdriver::capabilities::CapabilitiesMatching;
use webdriver::command::{
@@ -64,6 +66,26 @@ use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler};
use crate::actions::{InputSourceState, PointerInputState};
+#[derive(Default)]
+pub struct WebDriverMessageIdGenerator {
+ counter: Cell<usize>,
+}
+
+impl WebDriverMessageIdGenerator {
+ pub fn new() -> Self {
+ Self {
+ counter: Cell::new(0),
+ }
+ }
+
+ /// Returns a unique ID.
+ pub fn next(&self) -> WebDriverMessageId {
+ let id = self.counter.get();
+ self.counter.set(id + 1);
+ WebDriverMessageId(id)
+ }
+}
+
fn extension_routes() -> Vec<(Method, &'static str, ServoExtensionRoute)> {
vec![
(
@@ -145,10 +167,11 @@ pub struct WebDriverSession {
unhandled_prompt_behavior: String,
- // https://w3c.github.io/webdriver/#dfn-input-state-table
- input_state_table: HashMap<String, InputSourceState>,
- // https://w3c.github.io/webdriver/#dfn-input-cancel-list
- input_cancel_list: Vec<ActionSequence>,
+ /// <https://w3c.github.io/webdriver/#dfn-input-state-map>
+ input_state_table: RefCell<HashMap<String, InputSourceState>>,
+
+ /// <https://w3c.github.io/webdriver/#dfn-input-cancel-list>
+ input_cancel_list: RefCell<Vec<ActionSequence>>,
}
impl WebDriverSession {
@@ -172,8 +195,8 @@ impl WebDriverSession {
strict_file_interactability: false,
unhandled_prompt_behavior: "dismiss and notify".to_string(),
- input_state_table: HashMap::new(),
- input_cancel_list: Vec::new(),
+ input_state_table: RefCell::new(HashMap::new()),
+ input_cancel_list: RefCell::new(Vec::new()),
}
}
}
@@ -187,8 +210,22 @@ struct Handler {
/// for it to send us a load-status. Messages sent on it
/// will be forwarded to the load_status_receiver.
load_status_sender: IpcSender<WebDriverLoadStatus>,
+
session: Option<WebDriverSession>,
+
+ /// The channel for sending Webdriver messages to the constellation.
constellation_chan: Sender<EmbedderToConstellationMessage>,
+
+ /// The IPC sender which we can clone and pass along to the constellation
+ constellation_sender: IpcSender<WebDriverCommandResponse>,
+
+ /// Receiver notification from the constellation when a command is completed
+ constellation_receiver: IpcReceiver<WebDriverCommandResponse>,
+
+ id_generator: WebDriverMessageIdGenerator,
+
+ current_action_id: Cell<Option<WebDriverMessageId>>,
+
resize_timeout: u32,
}
@@ -409,11 +446,18 @@ impl Handler {
let (load_status_sender, receiver) = ipc::channel().unwrap();
let (sender, load_status_receiver) = unbounded();
ROUTER.route_ipc_receiver_to_crossbeam_sender(receiver, sender);
+
+ let (constellation_sender, constellation_receiver) = ipc::channel().unwrap();
+
Handler {
load_status_sender,
load_status_receiver,
session: None,
constellation_chan,
+ constellation_sender,
+ constellation_receiver,
+ id_generator: WebDriverMessageIdGenerator::new(),
+ current_action_id: Cell::new(None),
resize_timeout: 500,
}
}
@@ -1445,18 +1489,13 @@ impl Handler {
}
fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
- let input_cancel_list = {
- let session = self.session_mut()?;
- session.input_cancel_list.reverse();
- mem::take(&mut session.input_cancel_list)
- };
-
+ let input_cancel_list = self.session().unwrap().input_cancel_list.borrow();
if let Err(error) = self.dispatch_actions(&input_cancel_list) {
return Err(WebDriverError::new(error, ""));
}
- let session = self.session_mut()?;
- session.input_state_table = HashMap::new();
+ let session = self.session()?;
+ session.input_state_table.borrow_mut().clear();
Ok(WebDriverResponse::Void)
}
@@ -1614,7 +1653,7 @@ impl Handler {
let id = Uuid::new_v4().to_string();
// Step 8.1
- self.session_mut()?.input_state_table.insert(
+ self.session_mut()?.input_state_table.borrow_mut().insert(
id.clone(),
InputSourceState::Pointer(PointerInputState::new(&PointerType::Mouse)),
);
@@ -1645,19 +1684,31 @@ impl Handler {
..Default::default()
};
- // Step 8.16 Dispatch a list of actions with input state,
- // actions, session's current browsing context, and actions options.
- if let Err(error) =
- self.dispatch_pointermove_action(&id, &pointer_move_action, 0)
- {
- return Err(WebDriverError::new(error, ""));
- }
+ let action_sequence = ActionSequence {
+ id: id.clone(),
+ actions: ActionsType::Pointer {
+ parameters: PointerActionParameters {
+ pointer_type: PointerType::Mouse,
+ },
+ actions: vec![
+ PointerActionItem::Pointer(PointerAction::Move(
+ pointer_move_action,
+ )),
+ PointerActionItem::Pointer(PointerAction::Down(
+ pointer_down_action,
+ )),
+ PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
+ ],
+ },
+ };
- self.dispatch_pointerdown_action(&id, &pointer_down_action);
- self.dispatch_pointerup_action(&id, &pointer_up_action);
+ let _ = self.dispatch_actions(&[action_sequence]);
// Step 8.17 Remove an input source with input state and input id.
- self.session_mut()?.input_state_table.remove(&id);
+ self.session_mut()?
+ .input_state_table
+ .borrow_mut()
+ .remove(&id);
// Step 13
Ok(WebDriverResponse::Void)
@@ -1709,7 +1760,8 @@ impl Handler {
"Unexpected screenshot pixel format"
);
- let rgb = RgbaImage::from_raw(img.width, img.height, img.bytes().to_vec()).unwrap();
+ let rgb =
+ RgbaImage::from_raw(img.width, img.height, img.first_frame().bytes.to_vec()).unwrap();
let mut png_data = Cursor::new(Vec::new());
DynamicImage::ImageRgba8(rgb)
.write_to(&mut png_data, ImageFormat::Png)
diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml
index 967d72f171b..eee077f33ec 100644
--- a/ports/servoshell/Cargo.toml
+++ b/ports/servoshell/Cargo.toml
@@ -121,7 +121,7 @@ serde_json = { workspace = true }
servo_allocator = { path = "../../components/allocator" }
shellwords = "1.0.0"
surfman = { workspace = true, features = ["sm-raw-window-handle-06", "sm-x11"] }
-winit = "0.30.10"
+winit = "0.30.11"
[target.'cfg(any(all(target_os = "linux", not(target_env = "ohos")), target_os = "macos"))'.dependencies]
sig = "1.0"
diff --git a/ports/servoshell/desktop/headed_window.rs b/ports/servoshell/desktop/headed_window.rs
index 8efb16954d8..eac8d72331d 100644
--- a/ports/servoshell/desktop/headed_window.rs
+++ b/ports/servoshell/desktop/headed_window.rs
@@ -262,11 +262,11 @@ impl Window {
ElementState::Released => MouseButtonAction::Up,
};
- webview.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
+ webview.notify_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
action,
- button: mouse_button,
+ mouse_button,
point,
- }));
+ )));
}
/// Handle key events before sending them to Servo.
@@ -563,7 +563,7 @@ impl WindowPortsMethods for Window {
point.y -= (self.toolbar_height() * self.hidpi_scale_factor()).0;
self.webview_relative_mouse_point.set(point);
- webview.notify_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
+ webview.notify_input_event(InputEvent::MouseMove(MouseMoveEvent::new(point)));
},
WindowEvent::MouseWheel { delta, phase, .. } => {
let (mut dx, mut dy, mode) = match delta {
diff --git a/ports/servoshell/egl/app_state.rs b/ports/servoshell/egl/app_state.rs
index 737a2f23b7d..486b01060e8 100644
--- a/ports/servoshell/egl/app_state.rs
+++ b/ports/servoshell/egl/app_state.rs
@@ -537,31 +537,31 @@ impl RunningAppState {
/// Register a mouse movement.
pub fn mouse_move(&self, x: f32, y: f32) {
self.active_webview()
- .notify_input_event(InputEvent::MouseMove(MouseMoveEvent {
- point: Point2D::new(x, y),
- }));
+ .notify_input_event(InputEvent::MouseMove(MouseMoveEvent::new(Point2D::new(
+ x, y,
+ ))));
self.perform_updates();
}
/// Register a mouse button press.
pub fn mouse_down(&self, x: f32, y: f32, button: MouseButton) {
self.active_webview()
- .notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
- action: MouseButtonAction::Down,
+ .notify_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ MouseButtonAction::Down,
button,
- point: Point2D::new(x, y),
- }));
+ Point2D::new(x, y),
+ )));
self.perform_updates();
}
/// Register a mouse button release.
pub fn mouse_up(&self, x: f32, y: f32, button: MouseButton) {
self.active_webview()
- .notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
- action: MouseButtonAction::Up,
+ .notify_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ MouseButtonAction::Up,
button,
- point: Point2D::new(x, y),
- }));
+ Point2D::new(x, y),
+ )));
self.perform_updates();
}
@@ -589,11 +589,11 @@ impl RunningAppState {
/// Perform a click.
pub fn click(&self, x: f32, y: f32) {
self.active_webview()
- .notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
- action: MouseButtonAction::Click,
- button: MouseButton::Left,
- point: Point2D::new(x, y),
- }));
+ .notify_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ MouseButtonAction::Click,
+ MouseButton::Left,
+ Point2D::new(x, y),
+ )));
self.perform_updates();
}
diff --git a/support/hitrace-bencher/runs.json b/support/hitrace-bencher/runs.json
new file mode 100644
index 00000000000..2513e3f57d1
--- /dev/null
+++ b/support/hitrace-bencher/runs.json
@@ -0,0 +1,45 @@
+// This json file allows comments and is technically a hjson file.
+// Different {} correspond to completely different runs which can have
+// different filters and different arguments
+[
+ {
+ "args": {
+ "bencher": true, // output in bencher format
+ "url": "https://www.google.com", // the url to test
+ "tries": 5 // How many repeated tries we should have, we show the min,max,avg in the output
+ //"trace_buffer": 524288, // trace_buffer size of hitrace
+ //"sleep": 10, // how long should we wait for servo to load the page
+ //"bundle_name": "org.servo.servo", // the bundle name to start
+ //"commands": ["--ps=--tracing-filter", "info"] // arbitrary additional arguments
+ },
+ "filters": [
+ // Filters are currently given via the simple serialize of `filters::JsonValueFilter`.
+ // Filters have an arbitrary name that identifies them. This name will be used in bencher output
+ // where we format it with the E2E and the url.
+ // start_fn_partial matches a hitrace _start_ event where the given string is a substring of the function name.
+ // end_fn_partial matches a hitrace _start_ event where the given string is a substring of the function name.
+ // The filter will calculate the difference between these two start events.
+ // We currently do not support trace spans and only support exactly one match for start_fn_partial and end_fn_partial.
+ {
+ "name": "Load",
+ "start_fn_partial": "on_surface_created_cb",
+ "end_fn_partial": "PageLoadEndedPrompt"
+ }
+ ]
+ },
+ {
+ "args": {
+ "bencher": true,
+ "url": "https://www.servo.org",
+ "tries": 5
+ },
+ "filters": [
+ {
+ "name": "Load",
+ "start_fn_partial": "on_surface_created_cb",
+ "end_fn_partial": "PageLoadEndedPrompt"
+ }
+ ]
+ }
+]
+
diff --git a/tests/unit/script/headers.rs b/tests/unit/script/headers.rs
deleted file mode 100644
index 530559af7ff..00000000000
--- a/tests/unit/script/headers.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use script::test::{ByteString, normalize_value};
-
-#[test]
-fn test_normalize_empty_bytestring() {
- // empty ByteString test
- let empty_bytestring = ByteString::new(vec![]);
- let actual = normalize_value(empty_bytestring);
- let expected = ByteString::new(vec![]);
- assert_eq!(actual, expected);
-}
-
-#[test]
-fn test_normalize_all_whitespace_bytestring() {
- // All whitespace test. A horizontal tab, a line feed, a carriage return , and a space
- let all_whitespace_bytestring = ByteString::new(vec![b'\t', b'\n', b'\r', b' ']);
- let actual = normalize_value(all_whitespace_bytestring);
- let expected = ByteString::new(vec![]);
- assert_eq!(actual, expected);
-}
-
-#[test]
-fn test_normalize_non_empty_no_whitespace_bytestring() {
- // Non-empty, no whitespace ByteString test
- let no_whitespace_bytestring = ByteString::new(vec![b'S', b'!']);
- let actual = normalize_value(no_whitespace_bytestring);
- let expected = ByteString::new(vec![b'S', b'!']);
- assert_eq!(actual, expected);
-}
-
-#[test]
-fn test_normalize_non_empty_leading_whitespace_bytestring() {
- // Non-empty, leading whitespace, no trailing whitespace ByteString test
- let leading_whitespace_bytestring =
- ByteString::new(vec![b'\t', b'\n', b' ', b'\r', b'S', b'!']);
- let actual = normalize_value(leading_whitespace_bytestring);
- let expected = ByteString::new(vec![b'S', b'!']);
- assert_eq!(actual, expected);
-}
-
-#[test]
-fn test_normalize_non_empty_no_leading_whitespace_trailing_whitespace_bytestring() {
- // Non-empty, no leading whitespace, but with trailing whitespace ByteString test
- let trailing_whitespace_bytestring =
- ByteString::new(vec![b'S', b'!', b'\t', b'\n', b' ', b'\r']);
- let actual = normalize_value(trailing_whitespace_bytestring);
- let expected = ByteString::new(vec![b'S', b'!']);
- assert_eq!(actual, expected);
-}
-
-#[test]
-fn test_normalize_non_empty_leading_and_trailing_whitespace_bytestring() {
- // Non-empty, leading whitespace, and trailing whitespace ByteString test
- let whitespace_sandwich_bytestring = ByteString::new(vec![
- b'\t', b'\n', b' ', b'\r', b'S', b'!', b'\t', b'\n', b' ', b'\r',
- ]);
- let actual = normalize_value(whitespace_sandwich_bytestring);
- let expected = ByteString::new(vec![b'S', b'!']);
- assert_eq!(actual, expected);
-}
-
-#[test]
-fn test_normalize_non_empty_leading_trailing_and_internal_whitespace_bytestring() {
- // Non-empty, leading whitespace, trailing whitespace,
- // and internal whitespace ByteString test
- let whitespace_bigmac_bytestring = ByteString::new(vec![
- b'\t', b'\n', b' ', b'\r', b'S', b'\t', b'\n', b' ', b'\r', b'!', b'\t', b'\n', b' ', b'\r',
- ]);
- let actual = normalize_value(whitespace_bigmac_bytestring);
- let expected = ByteString::new(vec![b'S', b'\t', b'\n', b' ', b'\r', b'!']);
- assert_eq!(actual, expected);
-}
diff --git a/tests/unit/script/lib.rs b/tests/unit/script/lib.rs
index 4b258ede7d0..26c47e60f14 100644
--- a/tests/unit/script/lib.rs
+++ b/tests/unit/script/lib.rs
@@ -3,8 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#[cfg(test)]
-mod headers;
-#[cfg(test)]
mod htmlareaelement;
#[cfg(test)]
mod htmlimageelement;
diff --git a/tests/wpt/meta/css/CSS2/generated-content/content-070.xht.ini b/tests/wpt/meta/css/CSS2/generated-content/content-070.xht.ini
deleted file mode 100644
index 397ef880c7e..00000000000
--- a/tests/wpt/meta/css/CSS2/generated-content/content-070.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[content-070.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/text/text-decoration-va-length-001.xht.ini b/tests/wpt/meta/css/CSS2/text/text-decoration-va-length-001.xht.ini
new file mode 100644
index 00000000000..04285060eb5
--- /dev/null
+++ b/tests/wpt/meta/css/CSS2/text/text-decoration-va-length-001.xht.ini
@@ -0,0 +1,2 @@
+[text-decoration-va-length-001.xht]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/text/text-decoration-va-length-002.xht.ini b/tests/wpt/meta/css/CSS2/text/text-decoration-va-length-002.xht.ini
new file mode 100644
index 00000000000..8bd8764b744
--- /dev/null
+++ b/tests/wpt/meta/css/CSS2/text/text-decoration-va-length-002.xht.ini
@@ -0,0 +1,2 @@
+[text-decoration-va-length-002.xht]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini b/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini
index 408a853b460..e6b8dd0cf2e 100644
--- a/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini
+++ b/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini
@@ -10,12 +10,3 @@
[.flex 6]
expected: FAIL
-
- [.flex 13]
- expected: FAIL
-
- [.flex 14]
- expected: FAIL
-
- [.flex 15]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-flexbox/intrinsic-size/row-wrap-001.html.ini b/tests/wpt/meta/css/css-flexbox/intrinsic-size/row-wrap-001.html.ini
index 2b9a86b2d3b..3058f3f880d 100644
--- a/tests/wpt/meta/css/css-flexbox/intrinsic-size/row-wrap-001.html.ini
+++ b/tests/wpt/meta/css/css-flexbox/intrinsic-size/row-wrap-001.html.ini
@@ -1,9 +1,3 @@
[row-wrap-001.html]
- [.floating-flexbox 4]
- expected: FAIL
-
- [.floating-flexbox 5]
- expected: FAIL
-
[.floating-flexbox 1]
expected: FAIL
diff --git a/tests/wpt/meta/css/css-text-decor/text-decoration-decorating-box-001.html.ini b/tests/wpt/meta/css/css-text-decor/text-decoration-decorating-box-001.html.ini
new file mode 100644
index 00000000000..c7c62d17c1a
--- /dev/null
+++ b/tests/wpt/meta/css/css-text-decor/text-decoration-decorating-box-001.html.ini
@@ -0,0 +1,2 @@
+[text-decoration-decorating-box-001.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-text-decor/text-decoration-dotted-002.html.ini b/tests/wpt/meta/css/css-text-decor/text-decoration-dotted-002.html.ini
deleted file mode 100644
index 971f8e0a0fd..00000000000
--- a/tests/wpt/meta/css/css-text-decor/text-decoration-dotted-002.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[text-decoration-dotted-002.html]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-text-decor/text-decoration-propagation-display-contents.html.ini b/tests/wpt/meta/css/css-text-decor/text-decoration-propagation-display-contents.html.ini
deleted file mode 100644
index abeb0080d24..00000000000
--- a/tests/wpt/meta/css/css-text-decor/text-decoration-propagation-display-contents.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[text-decoration-propagation-display-contents.html]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-text-decor/text-decoration-style-multiple.html.ini b/tests/wpt/meta/css/css-text-decor/text-decoration-style-multiple.html.ini
deleted file mode 100644
index efc4aa161d5..00000000000
--- a/tests/wpt/meta/css/css-text-decor/text-decoration-style-multiple.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[text-decoration-style-multiple.html]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-text-decor/text-shadow/decorations-multiple-zorder.html.ini b/tests/wpt/meta/css/css-text-decor/text-shadow/decorations-multiple-zorder.html.ini
deleted file mode 100644
index 0a554b90ff8..00000000000
--- a/tests/wpt/meta/css/css-text-decor/text-shadow/decorations-multiple-zorder.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[decorations-multiple-zorder.html]
- expected: FAIL
diff --git a/tests/wpt/meta/fetch/api/request/request-headers.any.js.ini b/tests/wpt/meta/fetch/api/request/request-headers.any.js.ini
index 2e9dbd2cf7e..74ec35e6dc9 100644
--- a/tests/wpt/meta/fetch/api/request/request-headers.any.js.ini
+++ b/tests/wpt/meta/fetch/api/request/request-headers.any.js.ini
@@ -1,19 +1,3 @@
-[request-headers.any.html]
- [Adding invalid request header "Set-Cookie: KO"]
- expected: FAIL
-
- [Adding invalid request header "Access-Control-Request-Private-Network: KO"]
- expected: FAIL
-
-
-[request-headers.any.worker.html]
- [Adding invalid request header "Set-Cookie: KO"]
- expected: FAIL
-
- [Adding invalid request header "Access-Control-Request-Private-Network: KO"]
- expected: FAIL
-
-
[request-headers.any.serviceworker.html]
expected: ERROR
diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm.ini b/tests/wpt/meta/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm.ini
deleted file mode 100644
index 4cb15eeee5e..00000000000
--- a/tests/wpt/meta/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[intrinsic_sizes.htm]
- [default object size after src is removed]
- expected: FAIL