Compare commits

...

477 Commits

Author SHA1 Message Date
Nico Burns
bfd90c9f7d Implement HarfRust shaping backend
Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-22 20:00:18 +01:00
Martin Robinson
bf5da330e1 fonts: Stop using deprecated functions in dwrote (#38856)
The new version of the functions return `Result` types.

Testing: No tests because this should not change behavior.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-22 16:28:54 +00:00
Martin Robinson
777373054f fonts: Use fontations to read the OS/2 table of DirectWrite fonts (#38851)
Instead of copying the font table data in memory and parsing it with the
`truetype` crate, use a non-copying API from DirectWrite to implement a
`fontations` `TableProvider`. This has two benefits:

- We remove the dependency on the `truetype` crate finally.
- We do not have to make an in-memory copy of the table data when
  parsing the table.

The hope is that the `TableProvider` will be more generally useful in
the future.

Testing: There are no automated tests for Windows, but I manually
verified
that the data retrived via `fontations` matched that retrived by
`truetype`.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-22 16:23:35 +00:00
Abdelrahman Hossam
176e42d36d script: Add FocusOptions argument to Element.focus and implement FocusOptions.preventScroll (#38495)
This is an implementation of the `prevent_scroll` feature in the focus
transaction system. It allows to control whether focusing an element
should prevent scrolling or not.

Spec:
https://html.spec.whatwg.org/multipage/interaction.html#dom-focusoptions-preventscroll
Testing: Existing WPT tests

Signed-off-by: abdelrahman1234567 <abdelrahman.hossameldin.awadalla@huawei.com>
2025-08-22 14:05:32 +00:00
Martin Robinson
2ac8665e03 fonts: Add font variations support for Windows (#38831)
Unlike other platforms where we read the default axis values and combine
it with variations from style to make the font face, we set the
variations from the style when creating the font face and then read the
final variations from the face. It seems that DirectWrite does the
normalization of variation values internally.

This depends on servo/dwrote-rs#68.

Testing: We currently don't have tests for Windows, but variation
support is
covered by the WPT tests.
Fixes: This is part of #38800.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-22 11:10:17 +00:00
Martin Robinson
16ba172ba8 fonts: Add support for format(*-variations) to @font-face (#38832)
This is necessary to fully support font variations.

Testing: This will be tested once variations are enabled.
Fixes: This is part of #38800.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-22 09:25:19 +00:00
Shubham Gupta
09db6b8669 layout: Remove workaround for body while building overflow frame for StackingContextTree construction. (#38825)
While building the stacking context tree we were assuming that `<body>`
would have propagated its `overflow` value to the viewport, and thus its
used `overflow` would be `visible`.

However, the element that propagates `overflow` can be the root element
instead. Since #38598 we are correctly taking this into account in
`effective_overflow()`, so we no longer need to do anything special in
the stacking context logic.

Testing: `css/css-overflow/overflow-body-propagation-012.html`

Fixes: #38799

Signed-off-by: Shubham Gupta <shubham13297@gmail.com>
2025-08-22 07:59:02 +00:00
Martin Robinson
4784ff0375 script: Ensure that leaving the WebView sets the cursor back to the default cursor (#38759)
This changes makes a variety of changes to ensure that the cursor is set
back to the default cursor when it leaves the `WebView`:

1. Display list updates can come after a mouse leaves the `WebView`, so
   when refreshing the cursor after the update, base the updated cursor
   on the last hovered location in the `DocumentEventHandler`, rather
   than the compositor. This allows us to catch when the last hovered
   position is `None` (ie the cursor has left the `WebView`).
2. When handling `MouseLeftViewport` events for the cursor leaving the
   entire WebView, properly set the
   MouseLeftViewport::focus_moving_to_another_iframe` on the input event
   passed to the script thread.
3. When moving out of the `WebView` entirely, explicitly ask the
   embedder to set the cursor back to the default.

Testing: This change adds a unit test verifying this behavior.
Fixes: #38710.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-22 07:49:56 +00:00
Euclid Ye
66adf2bf9f webdriver: Consider shadow including descendant when computing "obscured" step of “element click” (#38841)
Testing: WebDriver Conformance test. In addition to fixing all "Element
Intercepted" errors in
https://github.com/yezhizhen/servo/actions/runs/17142506541, we are able
to fix many other tests.
Fixes: #38837

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-22 07:22:00 +00:00
Kenzie Raditya Tirtarahardja
cae8d22823 webdriver: Element Send keys use dispatch actions for KeyboardEvent (#38444)
Previously we immediately passed the KeyboardEvent to embedder. Now we
make element send keys go through the dispatch action which required by
spec. CompositionEvent still immediately passed through embedder

Testing: Should make
`./tests/wpt/tests/webdriver/tests/classic/element_send_keys/` more
stable.
Fixes: https://github.com/servo/servo/issues/38354
Fixes: https://github.com/servo/servo/issues/38442

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
Co-authored-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-22 05:20:54 +00:00
criskell
56ce19511c Make transaction field non-null in IDBObjectStore (#38834)
In the `IDBObjectStore::new` constructor, the `transaction` field is
initialized to null, but when using this constructor, we always execute
`set_transaction` immediately afterward. Therefore, we refactored to
require the `transaction` field to be specified during construction and
thereby also removed some no longer necessary assertions.

We also updated the `transaction` field in WebIDL to remove the nullable
capability.

Testing: WPT
Fixes: #38814

---------

Signed-off-by: criskell <96352451+criskell@users.noreply.github.com>
2025-08-22 05:10:13 +00:00
Gregory Terzian
ede9db2e18 script: abort planned form navigations (#38676)
Servo shows a behavior unlike FF and Safari(I don't have Chrome), where
stopping a window does not cancel planned form navigation, resulting in
an infinite navigation loop. The current behavior of Servo does seem to
follow the wording of the spec, so I will open a [companion issue at the
spec](https://github.com/whatwg/html/issues/11562), and I have also
written a WPT tests for the non-standard but widely followed behavior.
This PR also adds a beginning of an implementation of the "ongoing
navigation" concept, which is used by the spec to cancel navigations,
and which is used in this PR only to cancel planned form navigations.
The generation id concept, which corresponds to the planned navigation
concept in the spec, is turned into a simple struct private cell, and is
documented per the spec.

Testing: A new WPT test is added
Fixes: Only one part of https://github.com/servo/servo/issues/36747

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Signed-off-by: Gregory Terzian <2792687+gterzian@users.noreply.github.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-08-22 05:02:53 +00:00
Jo Steven Novaryo
0026213799 dom: Implement textual <input> lazy initialized placeholder (#38821)
Following #37527, every textual input is constructing the containers for
value and placeholder. However not all of textual `<input>` element
require the initialization of such placeholder container. This is
apparent with JS UI framework that defines its own placeholder
management.

This PR add lazy initialization for placeholder which construct the
relevant HTML elements for placeholder container whenever it is
necessary.

Testing: Existing WPT coverage

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-08-22 04:43:38 +00:00
Euclid Ye
66b0eb2687 webdriver: Simplify deserialize window_handles: Vec<String> (#38838)
We don't need to serialize internal String and then serialize the Result
type.

Testing: Tested. No behaviour change.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-22 04:34:43 +00:00
Ashwin Naren
cd5226adc2 net: Fix initial indexeddb version storage (#38836)
#38819 made a step in the right direction. Unfortunately sqlite doesn't
support unsigned integers, so I've been storing them as i64s internally,
but deserializing the bytes to u64s. This allows for an extra bit of
information, but by inserting 0 into the table, it was interpreted
`u64::from_ne_bytes([1,0,0,0....,0])` (or whatever the internal bit
representation of `0_i64` is on the platform), which is not intended.

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-22 04:12:41 +00:00
lumiscosity
39f3ce7a2e Make DOM geometry structs serializable (#38828)
Makes the following DOM geometry structs serializable:
- `DOMRect`
- `DOMRectReadOnly`
- `DOMQuad`
- `DOMMatrix`
- `DOMMatrixReadOnly`

Testing: Covered by WPT (`css/geometry/structured-serialization.html`).

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
2025-08-21 23:19:42 +00:00
Rahul Menon
e00f39d827 opts: Add a --force-ipc option (#38833)
Testing: servo.org loads properly with `./mach run -- -I`
Fixes: https://github.com/servo/servo/issues/38823

Signed-off-by: Rahul Menon <menonrahul02@gmail.com>
2025-08-21 22:55:14 +00:00
Ashwin Naren
f30be4e1ab Stub out IDBIndex (#38813)
Stubs the IDBIndex interface.

Testing: Mostly stubbing. Eventually covered by WPT.
Fixes: Partially #38100

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-21 22:54:19 +00:00
Josh Matthews
18230e9630 indexeddb: Initialize DB version to zero. (#38819)
None of our automated tests were executing the initial DB setup code
because the requested version always matched.

Testing: Existing WPT coverage.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-21 22:49:57 +00:00
batu_hoang
bce9f06cf8 webdriver: Refactor webdriver session and improve window handles (#38745)
This PR contains 2 parts:

1. Refactor webdriver session.
2. Improve webdriver window handles:
- Webdriver always get window handles from script thread by default.
- If script thread is blocked by user prompt, embedder stores the window
handle before user prompt appears, then webdriver can get window handle
from embedder.

Testing: Clear timeout cause by user prompt blocking script thread:
https://github.com/longvatrong111/servo/actions/runs/17033900026

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Co-authored-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-21 22:46:13 +00:00
Nico Burns
b18a65ed70 Shaper abstraction setup (#38755)
# Objective

- Reorganise Servo's shaper code in preparation for multiple shaping
backends
- Make it possible to keep https://github.com/servo/servo/pull/38707
up-to-date with `main` with minimal conflicts

## Changes made

- Split `components/fonts/shaper.rs` into
`components/fonts/shapers/mod.rs` and
`components/fonts/shapers/harfbuzz.rs`
- Add traits for generic shapers
- `ShapedGlyphData` now takes ownership of the HarfBuzz buffer
(`hb_buffer_t`). This allows it to be returned from
the`THarfShaper::shape_text` function. The buffer is now deallocated in
the `ShapedGlyphData`s `Drop` impl.
- Add traits for HarfBuzz-like shapers and move code from
`save_glyph_results` function to be generic over those traits so that it
can be shared by a future `HarfRust` backend.

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-21 20:44:39 +00:00
Martin Robinson
d4757c9e9f compositor: Remove IOCompositor::pipeline_to_webview_map (#38827)
This data structure is now unused after changes to the way that hit
testing works.

Testing: This is just removing dead code so no tests are necessary.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-21 15:16:41 +00:00
Jerens Lensun
48c2152f61 script_bindings(python): Add type to a function for codegen.py (#38771)
This PR focuses on adding type annotations to utility functions,
PropertyDefiner, and its child classes.

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
2025-08-21 14:45:45 +00:00
shuppy
4ba34b038c devtools: Fix available breakpoint positions with nested scripts (#38826)
in the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), there is a
separate
[Debugger.Script](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html)
object for each function in a script, forming a tree of Script objects.
since we were only issuing
[getPossibleBreakpoints()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getpossiblebreakpoints-query)
queries to the [root Script
object](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewscript-script-global)
for each script, we were failing to report locations inside functions as
available for setting breakpoints.

this patch fixes that by recursively issuing the
getPossibleBreakpoints() requests over the
[getChildScripts()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getchildscripts)
tree.

Testing: this patch adds a new devtools test

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-21 11:00:32 +00:00
Jonathan Schwender
7b4032e972 mozjs: Remove vendored icu crates (#38803)
Instead of vendoring a copy of icu_capi, mozjs now instead determines
the location of the provided c header files by parsing the cargo
metadata output.
This will allow vendoring mozjs and is a step towards publishing mozjs
and thus servo again.
Corresponding mozjs PR: https://github.com/servo/mozjs/pull/596

Testing: Covered by existing tests

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-21 10:13:03 +00:00
Jo Steven Novaryo
5cb1f37843 layout: Stretch <input> inner container to its containing block (#38775)
The `<input>` element inner container should be stretch to its
containing block. This would allow the alignment of the text. This is
done by adding `min-width: 100%`. It is required because we are not
using a custom layout contrary to Firefox and Chrome.

Testing: New Servo specific WPT.

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-08-21 07:26:32 +00:00
Rahul Menon
9cd019403f layout: Measure stacking context tree in layout thread memory report (#38762)
Measures the memory usage of the stacking context tree in the memory
report of the layout thread by adding `MallocSizeOf` to
`StackingContextTree` and all the types required for that. Also requires
adding `MallocSizeOf` to some webrender types.

Testing: Manually looked at about:memory
<img width="636" height="241" alt="image"
src="https://github.com/user-attachments/assets/6bf9d65a-0bf0-4a99-99b5-ddedba3269c1"
/>

Fixes: https://github.com/servo/servo/issues/38725

---------

Signed-off-by: Rahul Menon <menonrahul02@gmail.com>
2025-08-21 07:21:59 +00:00
atbrakhi
634c1897cf devtools: Handle removeBreakpoint on breakpoint-list actor (#38797)
The breakpoint-list actor did not implement a handler for the
`removeBreakpoint` request, causing the client to receive
`unrecognizedPacketType` error. This patch adds `removeBreakpoint`
handler


Fixes: Part of #36027

Signed-off-by: atbrakhi <atbrakhi@igalia.com>
2025-08-21 07:21:22 +00:00
lumiscosity
913066d2e5 Add legacy window aliases SVGMatrix/SVGPoint for DOMMatrix/DOMPoint (#38810)
Adds the legacy window aliases `SVGMatrix`/`SVGPoint` for
`DOMMatrix`/`DOMPoint`.

Testing: Covered by WPT (`css/geometry`).

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
2025-08-21 07:19:25 +00:00
Tim van der Lippe
3c89763b77 Implement trusted types for remaining attribute sinks (#38784)
Additionally, several methods were updated with
spec comments. That's because the "adopt the document
from the element document" step was missing.

By adding these spec comments, I also restructured
some code to avoid duplication of mutation records
and custom element reaction queueing.

Node.textContent doesn't propagate the error yet,
as that method has a lot of separate callers of
elements that wouldn't fail. I will refactor those
in a follow-up PR to keep things manageable.

This implements part of the DOM integration from
https://github.com/whatwg/dom/pull/1268

Part of #36258

---------

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
Signed-off-by: Tim van der Lippe <TimvdLippe@users.noreply.github.com>
2025-08-21 05:37:34 +00:00
Ashwin Naren
dd7b2a5ee2 Enable access to transaction from idbobjectstore (#38737)
Adds the transaction property to IDBObjectStore, as per spec.

Testing: WPT
Fixes: None

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-21 02:26:40 +00:00
Sebastian C
b869b7eb96 script: initial CookieStore implementation (#37968)
This is a first draft at implementing the required infrastructure for
CookieStore, which requires setting up IPC between script and the
resource thread to allow for async/"in parallel" handling of cookie
changes that have a promise API.

Cookie Store also will need to receive change events when cookies for a
url are changed so the architecture needs to support that.

Expect this PR to be reworked once the architecture becomes more
settled, cookie change events will be implemented in follow up PRs

Testing: WPT tests exist for this API
Part of #37674

---------

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
2025-08-21 01:00:24 +00:00
Kenzie Raditya Tirtarahardja
a75f3fd09b script(webdriver): Element send keys append to the end of filelist if Multiple (#38407)
Step 8.6 of [Element Send
Keys](https://w3c.github.io/webdriver/#dfn-element-send-keys) remote
end's implementation.
> 6. Complete implementation specific steps equivalent to setting the
[selected files](https://w3c.github.io/webdriver/#dfn-selected-files) on
the [input](https://w3c.github.io/webdriver/#dfn-input) element. **If
multiple is true files are be appended to element's [selected
files](https://w3c.github.io/webdriver/#dfn-selected-files).**

Testing: `webdriver/tests/classic/element_send_keys/file_upload.py`
Fixes: https://github.com/servo/servo/issues/38190

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
Co-authored-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-20 19:10:07 +00:00
Narfinger
011b314478 compostor: Move duplicated code for generating a batch of FontKeys into a function (#38804)
The code was duplicated for handling in normal messages and handling
while shutting down messages.
This cleans it up and puts them into a function.

Testing: Does not change functionality.

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2025-08-20 18:33:57 +00:00
Alex Touchet
8778f2d1f2 Update user agent strings to match Firefox 140 (#38802)
Update user agent strings to match Firefox 140 now that #38563 is
merged.

Testing: No tests for user agent string update.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-08-20 18:16:36 +00:00
dependabot[bot]
75d5e0fcc7 build(deps): bump taffy from 0.9.0 to 0.9.1 (#38805)
Bumps [taffy](https://github.com/DioxusLabs/taffy) from 0.9.0 to 0.9.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/DioxusLabs/taffy/blob/main/CHANGELOG.md">taffy's
changelog</a>.</em></p>
<blockquote>
<h2>0.9.1</h2>
<h3>Fixed</h3>
<ul>
<li>Flexbox: don't apply cross-axis stretch alignment to children with
auto margins (<a
href="https://redirect.github.com/DioxusLabs/taffy/issues/861">#861</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DioxusLabs/taffy/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=taffy&package-manager=cargo&previous-version=0.9.0&new-version=0.9.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 17:47:03 +00:00
lumiscosity
8e2f65bd16 Add matrixTransform for DOMPointReadOnly (#38801)
Adds the `matrixTransform` function for `DOMPointReadOnly`.

Testing: Covered by WPT tests (`css/geometry`)

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
2025-08-20 16:36:59 +00:00
Shubham Gupta
d8ff9c7a08 layout: Use overflow: visible if overflow was propagated to viewport (#38598)
The `overflow-*` values of either the root element or the `<body>` get
propagated to the viewport. However, we were missing this part:
> The element from which the value is propagated must then have a used
`overflow` value of `visible`.

See https://drafts.csswg.org/css-overflow/#overflow-propagation

Testing:
 - `css/cssom-view/scrolling-quirks-vs-nonquirks.html`
 - `css/css-overflow/overflow-body-propagation-007.html`
 - `css/css-overflow/overflow-body-propagation-008.html`
 - `css/css-overflow/overflow-body-propagation-009.html`
 - `css/css-overflow/scrollable-overflow-with-nested-elements-001.html` 
 - `css/css-overflow/scrollable-overflow-with-nested-elements-002.html` 
 - `css/css-overflow/scrollable-overflow-with-nested-elements-003.html`
 - `css/css-overflow/scrollable-overflow-with-nested-elements-004.html`
 - `css/css-overflow/scrollbar-gutter-scroll-into-view.html`

Failures:
 - `css/css-overflow/overflow-body-propagation-010.html`
   Failing because of missing support for `contain: paint`.
- `css/css-overflow/scrollable-overflow-with-nested-elements-005.html`
Failing because of wrong `data-expected-height`, but correct
`data-expected-scroll-height` which is core of this PR.
`data-expected-height` can be dealt separately.


Fixes: #38248

---------

Signed-off-by: Shubham Gupta <shubham13297@gmail.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-20 14:21:51 +00:00
Kenzie Raditya Tirtarahardja
37088aa4c3 wpt: Set servo window size and open blank page for wdspec (#38793)
On `ServoWdSpecBrowser`, we set the window size to `800x600` and open
`about:blank`. This will unify the behavior of servo and servodriver.


604b6ea26d/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/servodriver.py (L101-L102)

Testing: Unify the behavior of servo and servodriver for wdspec

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-20 13:26:59 +00:00
JoeDow
6e6ef513a9 Reuse StylesheetContent for inline style sheets with identical content (#38540)
For duplicate style sheets with identical content, `StylesheetContents`
can be reused to avoid redundant parsing of the inline style sheets.
Since duplicate stylesheets is a common case with web components, this
change will significantly improve performance. Additionally, the cache
hit rate of stylo's `CascadeDataCache` can now be significantly
improved.

When shared `StylesheetContents` is modified, copy-on-write will occur
to avoid affecting other sharers. And then updates the references to
`CssRule` or `PropertyDeclarationBlock` stored in the CSSOMs to ensure
that modifications are made only on the new copy.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-08-20 12:31:49 +00:00
dependabot[bot]
f6b77f94e2 build(deps): bump sea-query from 1.0.0-rc.9 to 1.0.0-rc.10 (#38796)
Bumps [sea-query](https://github.com/SeaQL/sea-query) from 1.0.0-rc.9 to
1.0.0-rc.10.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/SeaQL/sea-query/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sea-query&package-manager=cargo&previous-version=1.0.0-rc.9&new-version=1.0.0-rc.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 11:33:02 +00:00
dependabot[bot]
a14095c3e3 build(deps): bump winapi-util from 0.1.9 to 0.1.10 (#38795)
Bumps [winapi-util](https://github.com/BurntSushi/winapi-util) from
0.1.9 to 0.1.10.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="29a804c1a0"><code>29a804c</code></a>
0.1.10</li>
<li><a
href="740518024e"><code>7405180</code></a>
cargo: support <code>windows-sys</code> v0.60.0</li>
<li><a
href="b0e2eed9fc"><code>b0e2eed</code></a>
github: add FUNDING</li>
<li>See full diff in <a
href="https://github.com/BurntSushi/winapi-util/compare/0.1.9...0.1.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=winapi-util&package-manager=cargo&previous-version=0.1.9&new-version=0.1.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 10:04:41 +00:00
dependabot[bot]
a597cd5ed4 build(deps): bump cfg-if from 1.0.1 to 1.0.3 (#38794)
Bumps [cfg-if](https://github.com/rust-lang/cfg-if) from 1.0.1 to 1.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cfg-if/releases">cfg-if's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.3</h2>
<ul>
<li>Revert &quot;Remove <code>@__identity</code> rule.&quot;</li>
</ul>
<h2>v1.0.2</h2>
<ul>
<li>Remove <code>@__identity</code> rule.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cfg-if/blob/main/CHANGELOG.md">cfg-if's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/cfg-if/compare/v1.0.2...v1.0.3">1.0.3</a>
- 2025-08-19</h2>
<ul>
<li>Revert &quot;Remove <code>@__identity</code> rule.&quot;</li>
</ul>
<h2><a
href="https://github.com/rust-lang/cfg-if/compare/v1.0.1...v1.0.2">1.0.2</a>
- 2025-08-19</h2>
<ul>
<li>Remove <code>@__identity</code> rule.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9c7bb0bf71"><code>9c7bb0b</code></a>
chore: release v1.0.3 (<a
href="https://redirect.github.com/rust-lang/cfg-if/issues/93">#93</a>)</li>
<li><a
href="145894c77e"><code>145894c</code></a>
Revert &quot;Remove <code>@__identity</code> rule.&quot;</li>
<li><a
href="9f747fecdd"><code>9f747fe</code></a>
chore: release v1.0.2 (<a
href="https://redirect.github.com/rust-lang/cfg-if/issues/88">#88</a>)</li>
<li><a
href="97739b8388"><code>97739b8</code></a>
Fix some ugly and inconsistent formatting.</li>
<li><a
href="4d7a585e4d"><code>4d7a585</code></a>
Remove <code>@__identity</code> rule.</li>
<li><a
href="527acbdbd4"><code>527acbd</code></a>
Bump actions/checkout from 4 to 5</li>
<li>See full diff in <a
href="https://github.com/rust-lang/cfg-if/compare/v1.0.1...v1.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cfg-if&package-manager=cargo&previous-version=1.0.1&new-version=1.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 09:51:01 +00:00
Josh Matthews
636e7211e0 script: Measure heap usage of various ignored fields (#38791)
These changes allow using MallocSizeOf/`#[conditional_malloc_size_of]`
on WebIDL callback values, and then fix a grab bag of places in the
script crate that previously ignored those values. There are also some
commits removing ignored fields that involved Arc/Rc that are not WebIDL
callbacks, since they are now easier to support with the
`#[conditional_malloc_size_of]` attribute.

Testing: Manual testing on about:memory for servo.org.

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-20 08:43:58 +00:00
Josh Matthews
604b6ea26d Upgrade to SpiderMonkey 140. (#38563)
Upgrade to use the latest mozjs upgrade.

Testing: Covered by existing WPT.

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-20 07:35:35 +00:00
Martin Robinson
b6cb57287f dependabot: Exclude two testing directories to depndabot scanning (#38790)
According to dependabot/dependabot-core#4364, dependabot now supports
excluding certain directories from upgrade scanning. This change adds
two problematic directories to the exclusion list.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-20 07:17:13 +00:00
Josh Matthews
ed6bf196c9 constellation: Broadcast preference changes to all content processes (#38716)
Building on the preference observer work from #38649, we now
automatically install an observer when multiprocess mode is enabled.
This observer notifies the constellation of updated preferences, which
in turn notifies each content process so the changes will be reflected
into script/layout as expected. There's a unit test that verifies this
works correctly by checking a preference-gated WebIDL property before
and after the preference is toggled.

Testing: New unit test added.
Fixes: #35966

Depends on #38649.

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-20 06:43:16 +00:00
sagudev
61692b26c2 dependabot: Add egui-file-dialog and accesskit_winit to egui group (#38121)
https://github.com/servo/servo/pull/38119#issuecomment-3079653012

Testing: None, just dependabot config.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-20 06:36:42 +00:00
dependabot[bot]
a28c394495 build(deps): bump tinyvec from 1.9.0 to 1.10.0 (#38785)
Bumps [tinyvec](https://github.com/Lokathor/tinyvec) from 1.9.0 to
1.10.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Lokathor/tinyvec/blob/main/CHANGELOG.md">tinyvec's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>1.10</h2>
<ul>
<li>Minimum rust version is now 1.55, and the non-const-generic impls
are removed.
This reduces build times by over a second on average, which is
significant enough for a library crate.</li>
</ul>
<h2>1.9</h2>
<ul>
<li>Adds a <code>latest_stable_rust</code> cargo feature, which will
automatically pull in
other cargo features related to the latest Stable version of rust.</li>
<li>Adds <code>ArrayVec::try_from_array_len</code></li>
<li>Adds <code>TinyVec::into_vec</code> and
<code>TinyVec::into_boxed_slice</code></li>
<li>Adds support for <code>generic-array</code> crate</li>
<li>Adds support for the <code>borsh</code> crate</li>
</ul>
<h2>1.8.1</h2>
<ul>
<li><a href="https://github.com/e00E">e00E</a> updated the rustc
features so that they all
correctly depend on the lower version feature.
<a href="https://redirect.github.com/Lokathor/tinyvec/pull/199">pr
199</a></li>
</ul>
<h2>1.8</h2>
<ul>
<li><a href="https://github.com/Fuuzetsu">Fuuzetsu</a> added the
<code>ArrayVec::as_inner</code> method.
<a href="https://redirect.github.com/Lokathor/tinyvec/pull/197">pr
197</a></li>
</ul>
<h2>1.7</h2>
<ul>
<li><a href="https://github.com/Fuuzetsu">Fuuzetsu</a> added the
<code>rustc_1_61</code> cargo feature, which adds the
<code>retain_mut</code> method.
<a href="https://redirect.github.com/Lokathor/tinyvec/pull/198">pr
198</a></li>
</ul>
<h2>1.6.1</h2>
<ul>
<li><a href="https://github.com/e00E">e00E</a> fixed the Arbitrary impl
to work on Stable
without using a feature gate.
<a href="https://redirect.github.com/Lokathor/tinyvec/pull/180">pr
180</a></li>
</ul>
<h2>1.6.0</h2>
<ul>
<li><a href="https://github.com/i509VCB">i509VCB</a> added the
<code>try_</code> functions for fallable reallocation.
<a href="https://redirect.github.com/Lokathor/tinyvec/pull/158">pr
158</a></li>
<li><a href="https://github.com/ajtribick">ajtribick</a> added more
error impls to <code>TryFromSliceError</code>.
<a href="https://redirect.github.com/Lokathor/tinyvec/pull/160">pr
160</a></li>
<li>The <code>std</code> feature now automatically enables the
<code>alloc</code> feature as well.</li>
</ul>
<h2>1.5.1</h2>
<ul>
<li><a href="https://github.com/madsmtm">madsmtm</a> fixed an error with
the <code>alloc</code> feature on very old rustc versions.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3c613ad020"><code>3c613ad</code></a>
chore: Release</li>
<li><a
href="2c5f2f6bb6"><code>2c5f2f6</code></a>
changelog</li>
<li><a
href="a44fb04088"><code>a44fb04</code></a>
bump MSRV to 1.55, drop generated Array impls (<a
href="https://redirect.github.com/Lokathor/tinyvec/issues/209">#209</a>)</li>
<li>See full diff in <a
href="https://github.com/Lokathor/tinyvec/compare/v1.9.0...v1.10.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tinyvec&package-manager=cargo&previous-version=1.9.0&new-version=1.10.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 06:36:26 +00:00
dependabot[bot]
25c289e915 build(deps): bump serde_json from 1.0.142 to 1.0.143 (#38787)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.142 to
1.0.143.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/json/releases">serde_json's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.143</h2>
<ul>
<li>Implement Clone and Debug for serde_json::Map iterators (<a
href="https://redirect.github.com/serde-rs/json/issues/1264">#1264</a>,
thanks <a
href="https://github.com/xlambein"><code>@​xlambein</code></a>)</li>
<li>Implement Default for CompactFormatter (<a
href="https://redirect.github.com/serde-rs/json/issues/1268">#1268</a>,
thanks <a href="https://github.com/SOF3"><code>@​SOF3</code></a>)</li>
<li>Implement FromStr for serde_json::Map (<a
href="https://redirect.github.com/serde-rs/json/issues/1271">#1271</a>,
thanks <a
href="https://github.com/mickvangelderen"><code>@​mickvangelderen</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="10102c49bf"><code>10102c4</code></a>
Release 1.0.143</li>
<li><a
href="2a5b85312c"><code>2a5b853</code></a>
Replace super::super with absolute path within crate</li>
<li><a
href="447170bd38"><code>447170b</code></a>
Merge pull request 1271 from
mickvangelderen/mick/impl-from-str-for-map</li>
<li><a
href="ec190d6dfd"><code>ec190d6</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1264">#1264</a>
from xlambein/master</li>
<li><a
href="8be664752f"><code>8be6647</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1268">#1268</a>
from SOF3/compact-default</li>
<li><a
href="ba5b3cccea"><code>ba5b3cc</code></a>
Revert &quot;Pin nightly toolchain used for miri job&quot;</li>
<li><a
href="fd35a02901"><code>fd35a02</code></a>
Implement FromStr for Map&lt;String, Value&gt;</li>
<li><a
href="bea0fe6b3e"><code>bea0fe6</code></a>
Implement Default for CompactFormatter</li>
<li><a
href="0c0e9f6bfa"><code>0c0e9f6</code></a>
Add Clone and Debug impls to map iterators</li>
<li>See full diff in <a
href="https://github.com/serde-rs/json/compare/v1.0.142...v1.0.143">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.142&new-version=1.0.143)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 01:15:30 +00:00
dependabot[bot]
79de227448 build(deps): bump prettyplease from 0.2.36 to 0.2.37 (#38786)
Bumps [prettyplease](https://github.com/dtolnay/prettyplease) from
0.2.36 to 0.2.37.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/prettyplease/releases">prettyplease's
releases</a>.</em></p>
<blockquote>
<h2>0.2.37</h2>
<ul>
<li>Replace <code>~const</code> syntax with <code>[const]</code>
conditionally const syntax (<a
href="https://redirect.github.com/dtolnay/prettyplease/issues/123">#123</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c971184fa8"><code>c971184</code></a>
Release 0.2.37</li>
<li><a
href="484e06077c"><code>484e060</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/prettyplease/issues/123">#123</a>
from dtolnay/conditionallyconst</li>
<li><a
href="38d93c76ae"><code>38d93c7</code></a>
Replace ~const syntax with [const] conditionally const syntax</li>
<li><a
href="e1c92b1bef"><code>e1c92b1</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/prettyplease/issues/122">#122</a>
from dtolnay/negativeinherent</li>
<li><a
href="1f74d4724d"><code>1f74d47</code></a>
Reject negative inherent impls</li>
<li>See full diff in <a
href="https://github.com/dtolnay/prettyplease/compare/0.2.36...0.2.37">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=prettyplease&package-manager=cargo&previous-version=0.2.36&new-version=0.2.37)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 23:34:58 +00:00
Ashwin Naren
d0a8f27241 net: fix indexeddb backend bugs (#38744)
Fix a large number of backend issues that were masking everything else.
There probably is still more, but it'll take more integration/unit
testing to find it.

Testing: WPT
Fixes: #38743

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-19 20:44:49 +00:00
Usman Yahaya Baba
f2294db95b Split devtools network event logic for creating/retrieving network event actors (#38409)
`DevtoolsInstance::find_network_event_actor` silently creates a new
actor if there is not one already known for a given ID. This is
confusing; this PR separates logic for handling network requests (create
a new actor) and network responses (retrieve an existing actor).

Fixes: (https://github.com/servo/servo/issues/37841)

---------

Signed-off-by: uthmaniv <uthmanyahayababa@gmail.com>
2025-08-19 19:13:54 +00:00
Josh Matthews
f1a9ceed4f allocator: Add optional heap allocation measurement tracking. (#38727)
Add an off-by-default allocator mode that tracks all live allocations
with sizes and associated stack traces. We also track if each allocation
is visited as part of a measuring heap usage in `about:memory`, allowing
us to report on allocations that are not tracked yet. Right now the list
of untracked allocations is dumped to stdout; I have a python script
coming in a separate PR which makes it easier to perform analysis on the
massive output.

Testing: Manually tested with `./mach build -d --features
servo_allocator/allocation-tracking` and visiting about:memory.
Part of: #11559

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-19 18:49:27 +00:00
Martin Robinson
2022831e4f fonts: Add font variation support for macOS (#38760)
This change adds font variation support for macOS. The main bulk of the
change is reading the default, min, and max values for each variation
axis from the font and instantiating a new CoreText font with the
appropriate values. 

In addition, fonts with variations are now properly cached in the
CoreText font cache.

Testing: There are no tests for this change as we do not run WPT tests
for
macOS and font variations are currently turned off by default.
Eventually,
when the feature is turned on there will be test for it. These changes
are just laying the groundwork for the full implementation.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-19 15:12:48 +00:00
Euclid Ye
ad3018a921 webxr: Fix compile warning for Windows after upgrade to rustc 1.89 (#38780)
This is a follow up to
https://github.com/servo/servo/pull/36818#issuecomment-3135078567. Since
openxr is a feature only available on Windows, it was omitted.

Testing: Fixes
```
warning: hiding a lifetime that's elided elsewhere is confusing
   --> components\webxr\openxr\input.rs:366:9
    |
366 |         &self,
    |         ^^^^^ the lifetime is elided here
...
371 |     ) -> Vec<Binding> {
    |              ------- the same lifetime is hidden here
    |
    = help: the same lifetime is referred to in inconsistent ways, making the signature confusing
    = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default
help: use `'_` for type paths
    |
371 |     ) -> Vec<Binding<'_>> {
```

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-19 14:23:39 +00:00
Oriol Brufau
b4589134c9 layout: Fix mix-ups between physical and logical aspect ratios (#38778)
`ComputedValuesExt::preferred_aspect_ratio()` was getting the natural
aspect ratio expressed logically as inline over block, but then it was
mixing it with the value of the `aspect-ratio` CSS property, which is
expressed physically as width over height.

Therefore, this changes `ComputedValuesExt::preferred_aspect_ratio()` to
expect the physical natural ratio, and then it takes care to convert the
resulting ratio logically.

Testing: Unneeded. This has no effect in practice because we don't
support `writing-mode` yet.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-19 14:09:17 +00:00
Oriol Brufau
f31edc5d6a layout: Stop making <video> fall back to a preferred aspect ratio of 2 (#38705)
This is simpler, and has been successfully shipped in Blink.
See https://github.com/w3c/csswg-drafts/issues/12053 for more
information.

Testing: Improves WPT tests.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-19 12:09:36 +00:00
Nico Burns
39629560c8 Fix loading raw data from .ttc files on macos (#38753)
# Objective

Ensure that functionality which uses the raw font data (such as
rendering text to canvas) works correctly on macOS when the specified
font is a system font that lives in an OpenType Collection (`.ttc`)
file.

## Changes made

- The `read_data_from_file` in each backend now returns a `index: u32`
in addition to `data: Vec<u8>`
- The `data` field on the `Font` type has been renamed to `raw` and the
`data` method on the `Font` type has been renamed to `raw_font`. This
allows the index to be cached as computing is moderately expensive on
macOS (on the order of 100 microseconds).
- Both of the above now store/return a `struct RawFont` instead of a
`FontData` where `RawFont` is defined as `struct RawFont { data:
FontData, index: u32 }`.
- The users of the `data` method have been updated to use the cached
index from `data` rather than calling `.index()` each time.

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-19 11:57:48 +00:00
webbeef
3225d19907 cargo: Bump rustc to 1.89 (#36818)
Update Rustc to 1.89.

Reviewable by commit.

Leftover work:
- #37330 
- #38777

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Co-authored-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-19 11:07:53 +00:00
Jonathan Schwender
8587536755 Use GenericChannel for script_chan (#38645)
Motivation: 
Using our GenericChannel abstraction allows us to optimize IPC in
single-process mode to just use cross-beam channel.
To keep the diff low, and get early feedback, this PR only tackles a
single channel, but the intention is to port all ipc channels to the
generic channel, which allows us to skip serializing and deserializing
messages in single process mode.

Based on: 
- https://github.com/servo/servo/pull/38638
- https://github.com/servo/servo/pull/38636

Testing: Covered by existing tests

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-19 09:59:20 +00:00
Jonathan Schwender
73e0f2f7e6 uv: Fix warning by adding project table to pyproject.toml (#38774)
`uv` logs a warning if the pyproject.toml does not contain a project
table.
There is not really much point in adding the table, but no downsides
either,
so lets just add the table to make `uv` happy.

Testing: Manual run of `RUST_LOG=warn ./mach run`
Fixes: #38761

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-19 06:38:00 +00:00
Kenzie Raditya Tirtarahardja
60e6fe8cb5 webdriver: Change TickActions to vector rather than hashmap (#38747)
Based on
[spec](https://w3c.github.io/webdriver/#dfn-extract-an-action-sequence),
`TickActions` should be a list. Previously we used Hashmap, which when
iterated has arbitrary order. This causes some tests to be unstable
previously (see the linked issue).

Testing:
`./webdriver/tests/classic/perform_actions/{pointer_contextmenu,
pointer_modifier_click}.py` consistently pass now.
Fixes: https://github.com/servo/servo/issues/38387

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-19 06:25:56 +00:00
dependabot[bot]
ea506140c8 build(deps): bump bitflags from 2.9.1 to 2.9.2 (#38767)
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.9.1 to
2.9.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bitflags/bitflags/releases">bitflags's
releases</a>.</em></p>
<blockquote>
<h2>2.9.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix difference in the spec by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/446">bitflags/bitflags#446</a></li>
<li>Fix up inaccurate docs on bitflags_match by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/453">bitflags/bitflags#453</a></li>
<li>Remove rustc internal crate feature by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/454">bitflags/bitflags#454</a></li>
<li>Prepare for 2.9.2 release by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/456">bitflags/bitflags#456</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/bitflags/bitflags/compare/2.9.1...2.9.2">https://github.com/bitflags/bitflags/compare/2.9.1...2.9.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md">bitflags's
changelog</a>.</em></p>
<blockquote>
<h1>2.9.2</h1>
<h2>What's Changed</h2>
<ul>
<li>Fix difference in the spec by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/446">bitflags/bitflags#446</a></li>
<li>Fix up inaccurate docs on bitflags_match by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/453">bitflags/bitflags#453</a></li>
<li>Remove rustc internal crate feature by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/454">bitflags/bitflags#454</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/bitflags/bitflags/compare/2.9.1...2.9.2">https://github.com/bitflags/bitflags/compare/2.9.1...2.9.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="85b5e603ea"><code>85b5e60</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/456">#456</a>
from KodrAus/cargo/2.9.2</li>
<li><a
href="d185ca57b0"><code>d185ca5</code></a>
remove reference to std internal features</li>
<li><a
href="82a365fc54"><code>82a365f</code></a>
prepare for 2.9.2 release</li>
<li><a
href="f1beb28eb3"><code>f1beb28</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/454">#454</a>
from KodrAus/fix/rustc-dep-of-std</li>
<li><a
href="ef247c5046"><code>ef247c5</code></a>
update more error messages</li>
<li><a
href="c8e235463c"><code>c8e2354</code></a>
update compiler errors</li>
<li><a
href="fe4c49da68"><code>fe4c49d</code></a>
remove rustc internal crate feature</li>
<li><a
href="2b786c963b"><code>2b786c9</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/453">#453</a>
from bitflags/fix/match-docs</li>
<li><a
href="15519b01b8"><code>15519b0</code></a>
fix up inaccurate docs on bitflags_match</li>
<li><a
href="80684fdce6"><code>80684fd</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/446">#446</a>
from bitflags/KodrAus-patch-1</li>
<li>Additional commits viewable in <a
href="https://github.com/bitflags/bitflags/compare/2.9.1...2.9.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bitflags&package-manager=cargo&previous-version=2.9.1&new-version=2.9.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 05:14:57 +00:00
dependabot[bot]
40ac1dad8a build(deps): bump brotli from 8.0.1 to 8.0.2 (#38770)
Bumps [brotli](https://github.com/dropbox/rust-brotli) from 8.0.1 to
8.0.2.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/dropbox/rust-brotli/commits/8.0.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=brotli&package-manager=cargo&previous-version=8.0.1&new-version=8.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 05:14:47 +00:00
dependabot[bot]
97c762ec7c build(deps): bump syn from 2.0.105 to 2.0.106 (#38768)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.105 to 2.0.106.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/syn/releases">syn's
releases</a>.</em></p>
<blockquote>
<h2>2.0.106</h2>
<ul>
<li>Replace <code>~const</code> syntax with <code>[const]</code>
conditionally const syntax in trait bounds (<a
href="https://redirect.github.com/dtolnay/syn/issues/1896">#1896</a>, <a
href="https://redirect.github.com/rust-lang/rust/pull/139858">rust-lang/rust#139858</a>)</li>
<li>Support conditionally const impl Trait types (<a
href="https://redirect.github.com/dtolnay/syn/issues/1897">#1897</a>)</li>
<li>Reject polarity modifier and lifetime binder used in the same trait
bound (<a
href="https://redirect.github.com/dtolnay/syn/issues/1899">#1899</a>, <a
href="https://redirect.github.com/rust-lang/rust/pull/127054">rust-lang/rust#127054</a>)</li>
<li>Parse const trait bounds with bound lifetimes (<a
href="https://redirect.github.com/dtolnay/syn/issues/1902">#1902</a>)</li>
<li>Parse bound lifetimes with lifetime bounds (<a
href="https://redirect.github.com/dtolnay/syn/issues/1903">#1903</a>)</li>
<li>Allow type parameters and const parameters in trait bounds and
generic closures (<a
href="https://redirect.github.com/dtolnay/syn/issues/1904">#1904</a>, <a
href="https://redirect.github.com/dtolnay/syn/issues/1907">#1907</a>, <a
href="https://redirect.github.com/dtolnay/syn/issues/1908">#1908</a>, <a
href="https://redirect.github.com/dtolnay/syn/issues/1909">#1909</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0e4bc64fe1"><code>0e4bc64</code></a>
Release 2.0.106</li>
<li><a
href="4fb776a12e"><code>4fb776a</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/syn/issues/1910">#1910</a>
from dtolnay/traitboundissue</li>
<li><a
href="41b24a588b"><code>41b24a5</code></a>
Fix duplicated async trait bound issue</li>
<li><a
href="a64f024cf8"><code>a64f024</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/syn/issues/1909">#1909</a>
from dtolnay/fortype</li>
<li><a
href="176099e868"><code>176099e</code></a>
Parse type parameter introducer on closures</li>
<li><a
href="b790b39351"><code>b790b39</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/syn/issues/1908">#1908</a>
from dtolnay/genericvsqpath</li>
<li><a
href="96496390a3"><code>9649639</code></a>
Synchronize generics-vs-qpath heuristic with rust parser</li>
<li><a
href="60de3318e0"><code>60de331</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/syn/issues/1907">#1907</a>
from dtolnay/forconst</li>
<li><a
href="2aac6d7607"><code>2aac6d7</code></a>
Allow const parameters in for&lt;&gt;</li>
<li><a
href="11934e5365"><code>11934e5</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/syn/issues/1905">#1905</a>
from dtolnay/unsafestatic</li>
<li>Additional commits viewable in <a
href="https://github.com/dtolnay/syn/compare/2.0.105...2.0.106">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=syn&package-manager=cargo&previous-version=2.0.105&new-version=2.0.106)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 00:28:53 +00:00
dependabot[bot]
d321d8a13d build(deps): bump proc-macro2 from 1.0.97 to 1.0.101 (#38766)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.97
to 1.0.101.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/proc-macro2/releases">proc-macro2's
releases</a>.</em></p>
<blockquote>
<h2>1.0.101</h2>
<ul>
<li>Optimize Span location accessors (<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/519">#519</a>)</li>
</ul>
<h2>1.0.100</h2>
<ul>
<li>Stabilize <code>Span</code> methods on Rust 1.88+:
<code>start</code>, <code>end</code>, <code>line</code>,
<code>column</code>, <code>file</code>, <code>local_file</code> (<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/517">#517</a>,
<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/518">#518</a>)</li>
</ul>
<h2>1.0.99</h2>
<ul>
<li>Prevent Span's unstable API becoming unavailable from a future new
compiler lint (<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/515">#515</a>)</li>
</ul>
<h2>1.0.98</h2>
<ul>
<li>Disallow prefixed identifier as name of lifetime:
<code>'prefix#lifetime</code> (<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/514">#514</a>,
<a
href="https://redirect.github.com/rust-lang/rust/pull/126452">rust-lang/rust#126452</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d3188ea889"><code>d3188ea</code></a>
Release 1.0.101</li>
<li><a
href="cbd1286d36"><code>cbd1286</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/519">#519</a>
from dtolnay/binarysearch</li>
<li><a
href="fab4cb6df2"><code>fab4cb6</code></a>
Convert SourceMap scan to binary search</li>
<li><a
href="f4708a855b"><code>f4708a8</code></a>
Factor out SourceMap linear search to method</li>
<li><a
href="fdc853a4af"><code>fdc853a</code></a>
Release 1.0.100</li>
<li><a
href="848ed0b742"><code>848ed0b</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/518">#518</a>
from dtolnay/spanfile</li>
<li><a
href="76ce1a35a3"><code>76ce1a3</code></a>
Stabilize Span::file and Span::local_file</li>
<li><a
href="b5dd3c6baf"><code>b5dd3c6</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/517">#517</a>
from dtolnay/startend</li>
<li><a
href="1d0ffc026c"><code>1d0ffc0</code></a>
Use Span's start, end, line, column methods on stable 1.88+</li>
<li><a
href="4f5845e1ee"><code>4f5845e</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/516">#516</a>
from dtolnay/probe</li>
<li>Additional commits viewable in <a
href="https://github.com/dtolnay/proc-macro2/compare/1.0.97...1.0.101">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=proc-macro2&package-manager=cargo&previous-version=1.0.97&new-version=1.0.101)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 00:17:20 +00:00
dependabot[bot]
efd0926c63 build(deps): bump hyper from 1.6.0 to 1.7.0 (#38765)
Bumps [hyper](https://github.com/hyperium/hyper) from 1.6.0 to 1.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/hyperium/hyper/releases">hyper's
releases</a>.</em></p>
<blockquote>
<h2>v1.7.0</h2>
<h2>Highlights</h2>
<h3>Features</h3>
<ul>
<li><strong>client:</strong>
<ul>
<li>add a <code>TrySendError::error()</code> method (<a
href="https://redirect.github.com/hyperium/hyper/issues/3885">#3885</a>)
(<a
href="efa0b26958">efa0b269</a>)</li>
<li>add a <code>TrySendError::message()</code> method (<a
href="https://redirect.github.com/hyperium/hyper/issues/3884">#3884</a>)
(<a
href="03fd6aff88">03fd6aff</a>)</li>
</ul>
</li>
<li><strong>error:</strong> add <code>Error::is_shutdown()</code> (<a
href="https://redirect.github.com/hyperium/hyper/issues/3863">#3863</a>)
(<a
href="b8affd8a2e">b8affd8a</a>,
closes <a
href="https://redirect.github.com/hyperium/hyper/issues/2745">#2745</a>)</li>
<li><strong>server:</strong> add
<code>allow_multiple_spaces_in_request_line_delimiters</code> http1
builder method (<a
href="https://redirect.github.com/hyperium/hyper/issues/3929">#3929</a>)
(<a
href="9749184f8a">9749184f</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>server:</strong> improve caching accuracy of Date header (<a
href="https://redirect.github.com/hyperium/hyper/issues/3887">#3887</a>)
(<a
href="436cadd1ac">436cadd1</a>)</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>chore: remove redundant backticks by <a
href="https://github.com/jimmycathy"><code>@​jimmycathy</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3855">hyperium/hyper#3855</a></li>
<li>Gate tests with features they test by <a
href="https://github.com/WhyNotHugo"><code>@​WhyNotHugo</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3864">hyperium/hyper#3864</a></li>
<li>docs(service): add <code>HttpService</code> documentation by <a
href="https://github.com/cratelyn"><code>@​cratelyn</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3869">hyperium/hyper#3869</a></li>
<li>docs(examples): alias Builder for clarity by <a
href="https://github.com/Muffeter"><code>@​Muffeter</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3873">hyperium/hyper#3873</a></li>
<li>docs(MAINTAINERS): add katelyn martin as collaborator by <a
href="https://github.com/seanmonstar"><code>@​seanmonstar</code></a> in
<a
href="https://redirect.github.com/hyperium/hyper/pull/3878">hyperium/hyper#3878</a></li>
<li>chore(proto/h2): fix module-level documentation by <a
href="https://github.com/cratelyn"><code>@​cratelyn</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3881">hyperium/hyper#3881</a></li>
<li>feat(client): add a <code>TrySendError::message()</code> method by
<a href="https://github.com/cratelyn"><code>@​cratelyn</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3884">hyperium/hyper#3884</a></li>
<li>feat(client): add a <code>TrySendError::error()</code> method by <a
href="https://github.com/cratelyn"><code>@​cratelyn</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3885">hyperium/hyper#3885</a></li>
<li>fix(http2): add decriptive error for non-zero connect request by <a
href="https://github.com/samp5"><code>@​samp5</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3886">hyperium/hyper#3886</a></li>
<li>refactor(lib): drop futures-util except in ffi by <a
href="https://github.com/hanna-kruppe"><code>@​hanna-kruppe</code></a>
in <a
href="https://redirect.github.com/hyperium/hyper/pull/3890">hyperium/hyper#3890</a></li>
<li>fix(date): improve caching accuracy by <a
href="https://github.com/Patryk27"><code>@​Patryk27</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3887">hyperium/hyper#3887</a></li>
<li>docs(SECURITY): update policy to use GSA drafts when reporting
vulnerabilities by <a
href="https://github.com/seanmonstar"><code>@​seanmonstar</code></a> in
<a
href="https://redirect.github.com/hyperium/hyper/pull/3894">hyperium/hyper#3894</a></li>
<li>feat: add Error::is_shutdown() by <a
href="https://github.com/joshka"><code>@​joshka</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3863">hyperium/hyper#3863</a></li>
<li>docs: improve ext module overview by <a
href="https://github.com/seanmonstar"><code>@​seanmonstar</code></a> in
<a
href="https://redirect.github.com/hyperium/hyper/pull/3921">hyperium/hyper#3921</a></li>
<li>docs(rt): improve rt module overview by <a
href="https://github.com/seanmonstar"><code>@​seanmonstar</code></a> in
<a
href="https://redirect.github.com/hyperium/hyper/pull/3920">hyperium/hyper#3920</a></li>
<li>chore(ci): update to cargo-check-external-types-0.3.0 by <a
href="https://github.com/tottoto"><code>@​tottoto</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3928">hyperium/hyper#3928</a></li>
<li>test(common): add missing assertion in full_rewind test by <a
href="https://github.com/1911860538"><code>@​1911860538</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3926">hyperium/hyper#3926</a></li>
<li>fix(lib): avoid implicit cargo feature by <a
href="https://github.com/hanna-kruppe"><code>@​hanna-kruppe</code></a>
in <a
href="https://redirect.github.com/hyperium/hyper/pull/3931">hyperium/hyper#3931</a></li>
<li>feat(http1): add allow_multiple_spaces_in_request_line_delimiters h1
builder config method by <a
href="https://github.com/mccordryan"><code>@​mccordryan</code></a> in <a
href="https://redirect.github.com/hyperium/hyper/pull/3929">hyperium/hyper#3929</a></li>
<li>Prepare v1.7.0 by <a
href="https://github.com/seanmonstar"><code>@​seanmonstar</code></a> in
<a
href="https://redirect.github.com/hyperium/hyper/pull/3933">hyperium/hyper#3933</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/jimmycathy"><code>@​jimmycathy</code></a> made
their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3855">hyperium/hyper#3855</a></li>
<li><a href="https://github.com/Muffeter"><code>@​Muffeter</code></a>
made their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3873">hyperium/hyper#3873</a></li>
<li><a href="https://github.com/samp5"><code>@​samp5</code></a> made
their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3886">hyperium/hyper#3886</a></li>
<li><a
href="https://github.com/hanna-kruppe"><code>@​hanna-kruppe</code></a>
made their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3890">hyperium/hyper#3890</a></li>
<li><a href="https://github.com/Patryk27"><code>@​Patryk27</code></a>
made their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3887">hyperium/hyper#3887</a></li>
<li><a href="https://github.com/joshka"><code>@​joshka</code></a> made
their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3863">hyperium/hyper#3863</a></li>
<li><a
href="https://github.com/1911860538"><code>@​1911860538</code></a> made
their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3926">hyperium/hyper#3926</a></li>
<li><a
href="https://github.com/mccordryan"><code>@​mccordryan</code></a> made
their first contribution in <a
href="https://redirect.github.com/hyperium/hyper/pull/3929">hyperium/hyper#3929</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/hyperium/hyper/blob/master/CHANGELOG.md">hyper's
changelog</a>.</em></p>
<blockquote>
<h2>v1.7.0 (2025-08-18)</h2>
<h4>Bug Fixes</h4>
<ul>
<li><strong>server:</strong> improve caching accuracy of Date header (<a
href="https://redirect.github.com/hyperium/hyper/issues/3887">#3887</a>)
(<a
href="436cadd1ac">436cadd1</a>)</li>
</ul>
<h4>Features</h4>
<ul>
<li><strong>client:</strong>
<ul>
<li>add a <code>TrySendError::error()</code> method (<a
href="https://redirect.github.com/hyperium/hyper/issues/3885">#3885</a>)
(<a
href="efa0b26958">efa0b269</a>)</li>
<li>add a <code>TrySendError::message()</code> method (<a
href="https://redirect.github.com/hyperium/hyper/issues/3884">#3884</a>)
(<a
href="03fd6aff88">03fd6aff</a>)</li>
</ul>
</li>
<li><strong>error:</strong> add <code>Error::is_shutdown()</code> (<a
href="https://redirect.github.com/hyperium/hyper/issues/3863">#3863</a>)
(<a
href="b8affd8a2e">b8affd8a</a>,
closes <a
href="https://redirect.github.com/hyperium/hyper/issues/2745">#2745</a>)</li>
<li><strong>server:</strong> add
<code>allow_multiple_spaces_in_request_line_delimiters</code> http1
builder method (<a
href="https://redirect.github.com/hyperium/hyper/issues/3929">#3929</a>)
(<a
href="9749184f8a">9749184f</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="400bdfdace"><code>400bdfd</code></a>
v1.7.0</li>
<li><a
href="9749184f8a"><code>9749184</code></a>
feat(server): add
<code>allow_multiple_spaces_in_request_line_delimiters</code> http1
bu...</li>
<li><a
href="caa166c756"><code>caa166c</code></a>
chore(dependencies): avoid implicit cargo feature of futures-util (<a
href="https://redirect.github.com/hyperium/hyper/issues/3931">#3931</a>)</li>
<li><a
href="8ad2595c03"><code>8ad2595</code></a>
test(common): add missing assertion in full_rewind test (<a
href="https://redirect.github.com/hyperium/hyper/issues/3926">#3926</a>)</li>
<li><a
href="24f0da8636"><code>24f0da8</code></a>
chore(ci): update to cargo-check-external-types-0.3.0 (<a
href="https://redirect.github.com/hyperium/hyper/issues/3928">#3928</a>)</li>
<li><a
href="974289fb3d"><code>974289f</code></a>
docs(rt): improve <code>rt</code> module overview (<a
href="https://redirect.github.com/hyperium/hyper/issues/3920">#3920</a>)</li>
<li><a
href="283fd2381d"><code>283fd23</code></a>
docs: improve <code>ext</code> module overview and <code>Protocol</code>
docs (<a
href="https://redirect.github.com/hyperium/hyper/issues/3921">#3921</a>)</li>
<li><a
href="b8affd8a2e"><code>b8affd8</code></a>
feat(error): add <code>Error::is_shutdown()</code> (<a
href="https://redirect.github.com/hyperium/hyper/issues/3863">#3863</a>)</li>
<li><a
href="c88df7886c"><code>c88df78</code></a>
docs(SECURITY): update policy to use GSA drafts when reporting
vulnerabilitie...</li>
<li><a
href="436cadd1ac"><code>436cadd</code></a>
fix(server): improve caching accuracy of Date header (<a
href="https://redirect.github.com/hyperium/hyper/issues/3887">#3887</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/hyperium/hyper/compare/v1.6.0...v1.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hyper&package-manager=cargo&previous-version=1.6.0&new-version=1.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 23:53:21 +00:00
dependabot[bot]
688ef9e64a build(deps): bump cc from 1.2.32 to 1.2.33 (#38764)
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.2.32 to 1.2.33.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md">cc's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.33">1.2.33</a>
- 2025-08-15</h2>
<h3>Other</h3>
<ul>
<li>Regenerate target info (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1521">#1521</a>)</li>
<li>[win][arm64ec] Add testing for Arm64EC Windows (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1512">#1512</a>)</li>
<li>Fix parsing of nigthly targets (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1517">#1517</a>)</li>
<li>[win][arm64ec] Fix finding assembler and setting is_arm for Arm64EC
(<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1511">#1511</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a92892393c"><code>a928923</code></a>
chore: release v1.2.33 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1522">#1522</a>)</li>
<li><a
href="9c33178e20"><code>9c33178</code></a>
Regenerate target info (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1521">#1521</a>)</li>
<li><a
href="220d772624"><code>220d772</code></a>
Bump actions/checkout from 4 to 5 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1520">#1520</a>)</li>
<li><a
href="9de267d18b"><code>9de267d</code></a>
[win][arm64ec] Add testing for Arm64EC Windows (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1512">#1512</a>)</li>
<li><a
href="37fc899632"><code>37fc899</code></a>
Fix gen-windows-sys-binding (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1518">#1518</a>)</li>
<li><a
href="de7e8d5f32"><code>de7e8d5</code></a>
Fix parsing of nigthly targets (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1517">#1517</a>)</li>
<li><a
href="ca81dcc1a7"><code>ca81dcc</code></a>
[win][arm64ec] Fix finding assembler and setting is_arm for Arm64EC (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1511">#1511</a>)</li>
<li>See full diff in <a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.33">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cc&package-manager=cargo&previous-version=1.2.32&new-version=1.2.33)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 23:47:12 +00:00
Taym Haddadi
c4d2b63ef1 Implement AbortSignal static abort(reason) (#38746)
Implement AbortSignal static abort(reason)

part of #36936

---------

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>
2025-08-18 21:32:39 +00:00
Jonathan Schwender
25fea1e086 Fix ./mach build --no-default-features (#35922)
Fix build failures when building without default features.

Testing: This was manually tested, but this PR does not add automated
tests of `--no-default-features` to CI.

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-18 18:40:08 +00:00
Euclid Ye
ec5872992b webdriver: Reuse JSValue as WebDriverJSValue (#38751)
After #38748, `WebDriverJSValue` is almost same as `JSValue`. Now we
turn "potentially merge into one in the future" into reality. The only
thing we should be cautious is to properly serialize `WebFrame`,
`WebWindow`, `WebElement` for WebDriver.

Testing: No regression. Some error is fixed previously by #38709 which
didn't update test :)
Binary size reduced by 134KB.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-18 17:38:48 +00:00
Simon Wülker
7471ad7730 fonts: Implement CSS font-variation-settings property for FreeType platforms (#38642)
This change adds support for variable fonts via the
[`font-variation-settings`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings)
property.

There are three areas where we need to set the variation values:
* Webrender (`compositor.rs`), for drawing the glyphs
* Harfbuzz (`shaper.rs`), for most shaping tasks
* PlatformFont (`fonts/platform/`), for horizontal advances and kerning

For now, freetype is the only platform shaper that supports variable
fonts. I can't easily test the fonts with non-freetype shapers. Thats
why variable fonts are behind the `layout_variable_fonts_enabled` pref,
which is disabled by default.

<img width="1250" height="710" alt="image"
src="https://github.com/user-attachments/assets/1aee1407-f3a2-42f6-a106-af0443fcd588"
/>

<details><summary>HTML test file</summary>

```html
<style>
@font-face {
  font-family: "Amstelvar VF";
  src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")
    format("woff2-variations");
  font-weight: 300 900;
  font-stretch: 35% 100%;
  font-style: normal;
  font-display: swap;
}

p {
  font:
    1.2em "Amstelvar VF",
    Georgia,
    serif;
  font-size: 4rem;
  margin: 1rem;
  display: inline-block;
}

.p1 {
  font-variation-settings: "wght" 300;
}

.p2 {
  font-variation-settings: "wght" 625;
}

.p3 {
  font-variation-settings: "wght" 900;
}

</style>
<div>
  <p class="p1">Weight</p>
  <span>(font-variation-settings: "wght" 300)</span>
</div>
<div>
  <p class="p2">Weight</p>
  <span>(font-variation-settings: "wght" 625)</span>
</div>
<div>
  <p class="p3">Weight</p>
  <span>(font-variation-settings: "wght" 900)</span>
</div>
</div>
```
</details>



https://github.com/user-attachments/assets/9e21101a-796a-49fe-b82c-8999d8fa9ee1


Testing: Needs decision on whether we want to enable the pref in CI
Works towards https://github.com/servo/servo/issues/37236

Depends on https://github.com/servo/stylo/pull/230

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-18 16:30:14 +00:00
JoeDow
ce16fbce75 script: Mark the entire shadow tree for restyle when its stylesheet is invalidated (#38529)
This change fix this bugs: modify stylesheet of shadow tree does not
take effect if there is no other Dom change within the shadow tree
happens.

Fixes: This change fix some bugs reported by this issue #38211. 
Testing: This fixes some subtests in
`/css/cssom/CSSStyleSheet-constructable.html` and
`/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html`,
and make some subtest failed in
`/css/cssom/CSSStyleSheet-constructable-baseURL.html`, because the
`BaseUrl` is not yet supported for `CSSStylesheet`.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-08-18 12:48:35 +00:00
Martin Robinson
5c885d61ad fonts: Have CoreTextFontCache::core_text_font return a PlatformFont (#38758)
This will make it easier in a followup to include the normalized font
variations in the return value. This also make
`PlatformFont::ensure_h_kern_subtable` work on the instance instead of
being a struct method. Finally, all `PlatformFont` methods are combined
into a single impl block.

Testing: This should not change behavior and is thus covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-18 12:32:20 +00:00
Martin Robinson
6fdf40dce7 layout: Always build Tag and BaseFragmentInfo with ServoThreadSafeLayoutNode (#38680)
This cleanup makes the interface a bit simpler and prevents problems
where the pseudo-element information is not passed by accident.

Testing: This should not change behavior, so is covered by existing
tests.

---------

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-18 12:19:09 +00:00
Martin Robinson
8743a11ba4 tidy: Add a rule ensuring that // comments are followed by a space in Rust (#38698)
This shows up sometimes in code reviews, so it makes sense that tidy
enforces it. `rustfmt` supports this via comment normalization, but it
does many other things and is still an unstable feature (with bugs).

Testing: There are new tidy tests for this change.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-18 12:09:09 +00:00
Simon Wülker
8ca00a3b0c Add missing backtick in README.md (#38757)
The build instructions for OHOS were missing a backtick.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-18 11:53:08 +00:00
Jo Steven Novaryo
7489a0349f layout: Do not include position:fixed children when calculating scrollable overflow for root element (#38618)
Reimplementation of: #35931

For a `FragmentTree` we define a scrollable overflow calculation that
includes the overflow all of it's children `Fragments`. In practice we
are using this calculation for scrolling area of the viewport and
defining the root scroll frames. However, since uncontained fixed
positioned element is located outside of the document and should not be
scrolled, and therefore it would make no sense to include them in the
calculation of its scrollable overflow as well.

Testing: New and existing WPT tests
Fixes: #38617
Fixes: #38182

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-08-18 11:25:31 +00:00
Nico Burns
7dcd89a6f9 Use built-in conversion function from unicode_script to convert script for harfbuzz (#38704)
Replaces a big match statement with a call to `unicode_script`'s
`Script::short_name` method which has the same big match statement. We
special case `Script::Unknown` because the `short_name` method returns
empty string for that variant, but harfbuzz represents it with `Zzzz`.

EDIT: `Zzzz` seems to be in the spec so I've sent a PR to
`unicode_script` to use it:
https://github.com/unicode-rs/unicode-script/pull/23
EDIT: And https://github.com/unicode-rs/unicode-script/pull/24 which
would allow us to remove this method entirely.

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-18 08:16:29 +00:00
Jerens Lensun
788d6db94d script_bindings(python): Add ruff rule for strict typing in function (#38694)
This is part of introducing the Ruff rule [flake8-annotations
(ANN)](https://docs.astral.sh/ruff/rules/#flake8-annotations-ann) into
the script_bindings folder.

Since `codegen.py` has 10k lines of code, the strategy is to introduce
the rule first while ignoring the `ANN` Ruff rule for `codegen.py`. We
will then gradually add type annotations slice by slice to ensure no new
errors occur outside of `ANN`.

Testing: `./mach test-wpt webidl`

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-18 07:26:30 +00:00
Josh Matthews
27dededa65 Enable dom_abort_controller_enabled for AbortController test subdirectory (#38749)
Since we don't enable the preference as part of
`--enable-experimental-web-platform-features` yet, we don't run any
automated tests for the AbortController feature. This change means that
we at least explicitly test the interface.

Testing: Just enabling new tests.
Part of: https://github.com/servo/servo/issues/34866

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-18 07:25:04 +00:00
Jonathan Schwender
2e6219b52d CI: Bump setup-ohos-sdk to 0.2.3 (#38741)
Includes a fix, which deletes the SDK for ohos-native hosts, saving some
disk and cache space, since we only need the version for linux hosts.

Testing: [mach try
ohos](https://github.com/jschwe/servo/actions/runs/17024914816)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-18 05:54:31 +00:00
Euclid Ye
ce9425f3e8 embedder: Remove Int variant from WebDriverJSValue (#38748)
According to
[spec](https://w3c.github.io/webdriver/#dfn-json-deserialize), we should
only care about
[Number](https://262.ecma-international.org/5.1/#sec-4.3.19) that is
f64.

This change also closes the gap between `JSValue` and `WebDriverJSValue`
and potentially merge into one in the future.

Testing: No regression. However, we have lots of TIMEOUT due to
https://github.com/servo/servo/pull/38622.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-18 04:39:52 +00:00
Simon Wülker
e19fade481 Replace FreeTypeFaceHelpers with a safe wrapper struct (#38634)
Testing: only safety annotations change, no tests are required.
Fixes: https://github.com/servo/servo/issues/38627

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-17 21:46:16 +00:00
Tim van der Lippe
4de9a9d100 Implement trusted types for setTimeout/setInterval (#38736)
I had a difficult time figuring out where the relevant steps had to be
added. Therefore, I aggressively commented the spec steps so eventually
I discovered where I should add them.

Part of #36258

---------

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
Signed-off-by: Tim van der Lippe <TimvdLippe@users.noreply.github.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
2025-08-17 20:53:16 +00:00
Tim van der Lippe
a31235e52b Add trusted type checks for global event handler attributes (#38718)
This only covers the global event handlers for now, while I figure out
which others we are missing. We don't seem to be missing the
WindowEventHandlers, but not sure where the others coming from.

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-08-17 19:44:22 +00:00
Josh Matthews
9da8142e2a fonts: Measure more FontContext heap usage. (#38733)
Replace a hand-written MallocSizeOf implementation with an automatically
derived one. This exposes more than 1MB of previously-untracked heap
data on servo.org.

Testing: Compared about:memory output for servo.org before and after.
Part of: #11559

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-17 18:59:30 +00:00
Kingsley Yung
d490c5c06b script: Throw error when lower is greater than upper in IDBKeyRange (#38735)
IDBKeyRange::Bound doesn't check if the lower bound is greater than the
upper bound. When this happens, it should throw DataError.

Reference: Step 5 of
<https://www.w3.org/TR/IndexedDB-2/#dom-idbkeyrange-bound>

Testing: Passing WPT tests that were expected to fail before.

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2025-08-17 15:21:45 +00:00
Jonathan Schwender
ee781b71b4 tests: Vendor blink perf tests (#38654)
Vendors the [blink perf
tests](https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/perf_tests/).
These perf tests are useful to evaluate the performance of servo. 
The license that governs the perf tests is included in the folder. 
Running benchmark cases automatically is left to future work.

The update.py script is taken from mozjs and slightly adapted, so we can
easily filter
(and patch if this should be necessary in the future.

Testing: This PR just adds the perf_tests, but does not use or modify
them in any way.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-17 09:54:04 +00:00
Ashwin Naren
7621332824 tests: Turn on IndexedDB tests and update results (#38722)
Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-17 08:32:29 +00:00
Servo WPT Sync
fbf0eb11a2 Sync WPT with upstream (17-08-2025) (#38721)
Automated downstream sync of changes from upstream as of 17-08-2025
[no-wpt-sync]

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
2025-08-17 01:49:38 +00:00
Rahul Menon
389277fa72 content: Make QuotaExceededError serializable (#38720)
Implements (de)serialization behavior for QuotaExceededError and enables
the annotation on the WebIDL spec.

Testing: Adds its own WPT tests
Fixes: https://github.com/servo/servo/issues/38685

---------

Signed-off-by: Rahul Menon <menonrahul02@gmail.com>
2025-08-16 20:33:37 +00:00
Josh Matthews
e80d36783a script: Ensure JS->webdriver conversions have a non-empty settings stack (#38709)
JS scripts that are evaluated already run with an [entry
script](https://html.spec.whatwg.org/#entry) on the script settings
stack. The codepaths that do something with the return value (eg.
WebDriver and embedder JS evaluation) have the potential to run
additional JS, since they can trigger getters for arbitrary properties
of objects, so they also need an entry script present in case code like
e649b9b91d/components/script/dom/location.rs (L182)
is executed.

Testing: Added a regression unit test.
Fixes: #38692

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-16 17:14:56 +00:00
Josh Matthews
f19b2f6e84 Add preference observer API for runtime webxr preference changes (#38649)
Adds a global preference observer that is notified whenever any
preference value is updated. This is used to support runtime
configuration of WebXR automated testing, which is a prerequisite for
running multiple WPT tests in a single browser session.

Testing: Ran `./mach test-wpt /webxr --product=servodriver`
Fixes: #38647

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-16 08:49:13 +00:00
Ashwin Naren
fc3feceee5 Switch indexeddb backend to sqlite and improve IPC messaging (#38187)
- Use sqlite instead of heed. (one indexed database = one sqlite
database)
- Implement the backend for indexes
- Use keyranges where needed (as specified by the spec)
- Implement `getKey`
- Fix channel error messaging (led to a bunch of changes to how async
requests are handled)

Note: `components/net/indexeddb/engines/sqlite/serialize.rs` is unused;
I can delete it if needed.

Testing: Switching to sqlite eliminated many panics (exposing some new
failures).
Fixes: #38040

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-16 07:27:17 +00:00
Ashwin Naren
f4bbdf8010 net: Fix possible indexeddb key range singleton related panic (#38281)
If `.as_singleton()` was called on a range that had both lower and upper
set as `None`, it would have panicked.

Testing: Nothing seems to have changed
Fixes: #37647

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-08-16 03:19:01 +00:00
Servo WPT Sync
e649b9b91d Sync WPT with upstream (16-08-2025) (#38706)
Automated downstream sync of changes from upstream as of 16-08-2025
[no-wpt-sync]

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
2025-08-16 00:46:10 +00:00
sagudev
85588fada0 compositing: Use webrender_api::ExternalImageSource instead of WebRenderImageSource (#38703)
There is no reason to roll our own type.

Motivation: I need `ExternalImageSource::Invalid`.

Testing: Covered by existing WPT tests, but it's just refactor

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-15 19:39:07 +00:00
Tim van der Lippe
18e05d3aab Implement trusted types for setAttribute (#38700)
Callers now call `set_attribute` directly, to avoid the trusted types
machinery, as well as skip validation. That's not required by spec as
well.

This implements part of the DOM integration from
https://github.com/whatwg/dom/pull/1268

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-08-15 19:18:19 +00:00
dependabot[bot]
8290761066 build(deps): bump async-trait from 0.1.88 to 0.1.89 (#38702)
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.88
to 0.1.89.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/async-trait/releases">async-trait's
releases</a>.</em></p>
<blockquote>
<h2>0.1.89</h2>
<ul>
<li>Improve IDE functionality (<a
href="https://redirect.github.com/dtolnay/async-trait/issues/293">#293</a>,
thanks <a
href="https://github.com/Veykril"><code>@​Veykril</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a7e91e98a5"><code>a7e91e9</code></a>
Release 0.1.89</li>
<li><a
href="fbcfcaca02"><code>fbcfcac</code></a>
Merge pull request 293 from Veykril/lw/quote_spanned</li>
<li><a
href="fd93990620"><code>fd93990</code></a>
Improve use of spans in <code>quote_spanned</code></li>
<li><a
href="a5093fe762"><code>a5093fe</code></a>
Add type-mismatch ui test</li>
<li><a
href="6d12b44116"><code>6d12b44</code></a>
Revert &quot;Pin nightly toolchain used for miri job&quot;</li>
<li><a
href="dd9e4bad1b"><code>dd9e4ba</code></a>
Hide unused_variables warning in consider-restricting.rs ui test</li>
<li><a
href="b454fc869a"><code>b454fc8</code></a>
Update ui test suite to nightly-2025-08-03</li>
<li><a
href="9c880e85d8"><code>9c880e8</code></a>
Update ui test suite to nightly-2025-07-30</li>
<li><a
href="7ca751d0b3"><code>7ca751d</code></a>
Ignore unused_parens warning in test</li>
<li><a
href="2bccfeb461"><code>2bccfeb</code></a>
Update ui test suite to nightly-2025-05-28</li>
<li>Additional commits viewable in <a
href="https://github.com/dtolnay/async-trait/compare/0.1.88...0.1.89">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=async-trait&package-manager=cargo&previous-version=0.1.88&new-version=0.1.89)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 17:25:36 +00:00
dependabot[bot]
3f2bb0eda3 build(deps): bump the egui-related group with 7 updates (#38701)
Bumps the egui-related group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [egui](https://github.com/emilk/egui) | `0.32.0` | `0.32.1` |
| [egui-winit](https://github.com/emilk/egui) | `0.32.0` | `0.32.1` |
| [egui_glow](https://github.com/emilk/egui) | `0.32.0` | `0.32.1` |
| [ecolor](https://github.com/emilk/egui) | `0.32.0` | `0.32.1` |
| [emath](https://github.com/emilk/egui) | `0.32.0` | `0.32.1` |
| [epaint](https://github.com/emilk/egui) | `0.32.0` | `0.32.1` |
| [epaint_default_fonts](https://github.com/emilk/egui) | `0.32.0` |
`0.32.1` |

Updates `egui` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">egui's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">egui's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="707a339047"><code>707a339</code></a>
Bump version numbers to 0.32.1</li>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="4fcd1d13e3"><code>4fcd1d1</code></a>
Update to winit 0.30.12 (<a
href="https://redirect.github.com/emilk/egui/issues/7420">#7420</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `egui-winit` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">egui-winit's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">egui-winit's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li><a
href="85c4b1dce7"><code>85c4b1d</code></a>
Fix glyph rendering: clamp coverage to [0, 1] (<a
href="https://redirect.github.com/emilk/egui/issues/7415">#7415</a>)</li>
<li><a
href="2f7bcf26f2"><code>2f7bcf2</code></a>
Fix multi-line <code>TextShape</code> rotation (<a
href="https://redirect.github.com/emilk/egui/issues/7404">#7404</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `egui_glow` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">egui_glow's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">egui_glow's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li><a
href="85c4b1dce7"><code>85c4b1d</code></a>
Fix glyph rendering: clamp coverage to [0, 1] (<a
href="https://redirect.github.com/emilk/egui/issues/7415">#7415</a>)</li>
<li><a
href="2f7bcf26f2"><code>2f7bcf2</code></a>
Fix multi-line <code>TextShape</code> rotation (<a
href="https://redirect.github.com/emilk/egui/issues/7404">#7404</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `ecolor` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">ecolor's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">ecolor's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="707a339047"><code>707a339</code></a>
Bump version numbers to 0.32.1</li>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="4fcd1d13e3"><code>4fcd1d1</code></a>
Update to winit 0.30.12 (<a
href="https://redirect.github.com/emilk/egui/issues/7420">#7420</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `emath` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">emath's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">emath's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li><a
href="85c4b1dce7"><code>85c4b1d</code></a>
Fix glyph rendering: clamp coverage to [0, 1] (<a
href="https://redirect.github.com/emilk/egui/issues/7415">#7415</a>)</li>
<li><a
href="2f7bcf26f2"><code>2f7bcf2</code></a>
Fix multi-line <code>TextShape</code> rotation (<a
href="https://redirect.github.com/emilk/egui/issues/7404">#7404</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `epaint` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">epaint's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">epaint's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li><a
href="85c4b1dce7"><code>85c4b1d</code></a>
Fix glyph rendering: clamp coverage to [0, 1] (<a
href="https://redirect.github.com/emilk/egui/issues/7415">#7415</a>)</li>
<li><a
href="2f7bcf26f2"><code>2f7bcf2</code></a>
Fix multi-line <code>TextShape</code> rotation (<a
href="https://redirect.github.com/emilk/egui/issues/7404">#7404</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `epaint_default_fonts` from 0.32.0 to 0.32.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/releases">epaint_default_fonts's
releases</a>.</em></p>
<blockquote>
<h2>0.32.1 - Misc bug fixes</h2>
<p>egui is an easy-to-use immediate mode GUI for Rust that runs on both
web and native.</p>
<p>Try it now: <a
href="https://www.egui.rs/">https://www.egui.rs/</a></p>
<p>egui development is sponsored by <a
href="https://www.rerun.io/">Rerun</a>, a startup building an SDK for
visualizing streams of multimodal data.</p>
<h1>egui changelog</h1>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
<h1>eframe changelog</h1>
<ul>
<li>Enable wgpu default features in eframe / egui_wgpu default features
<a href="https://redirect.github.com/emilk/egui/pull/7344">#7344</a> by
<a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Request a redraw when the url change through the
<code>popstate</code> event listener <a
href="https://redirect.github.com/emilk/egui/pull/7403">#7403</a> by <a
href="https://github.com/irevoire"><code>@​irevoire</code></a></li>
</ul>
<h1>egui_kittest changelog</h1>
<ul>
<li>Fix <code>UPDATE_SNAPSHOTS</code>: only update if we didn't pass the
test <a
href="https://redirect.github.com/emilk/egui/pull/7455">#7455</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/emilk/egui/blob/main/CHANGELOG.md">epaint_default_fonts's
changelog</a>.</em></p>
<blockquote>
<h2>0.32.1 - 2025-08-15 - Misc bug fixes</h2>
<h3> Added</h3>
<ul>
<li>Add <code>ComboBox::popup_style</code> <a
href="https://redirect.github.com/emilk/egui/pull/7360">#7360</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
</ul>
<h3>🐛 Fixed</h3>
<ul>
<li>Fix glyph rendering: clamp coverage to [0, 1] <a
href="https://redirect.github.com/emilk/egui/pull/7415">#7415</a> by <a
href="https://github.com/emilk"><code>@​emilk</code></a></li>
<li>Fix manual <code>Popup</code> not closing <a
href="https://redirect.github.com/emilk/egui/pull/7383">#7383</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>WidgetText::Text</code> ignoring fallback font and
overrides <a
href="https://redirect.github.com/emilk/egui/pull/7361">#7361</a> by <a
href="https://github.com/lucasmerlin"><code>@​lucasmerlin</code></a></li>
<li>Fix <code>override_text_color</code> priority <a
href="https://redirect.github.com/emilk/egui/pull/7439">#7439</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
<li>Fix debug-panic in ScrollArea if contents fit without scrolling <a
href="https://redirect.github.com/emilk/egui/pull/7440">#7440</a> by <a
href="https://github.com/YgorSouza"><code>@​YgorSouza</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ead6926895"><code>ead6926</code></a>
Update changelogs</li>
<li><a
href="03fb443669"><code>03fb443</code></a>
Only update snapshot if we didn't pass (<a
href="https://redirect.github.com/emilk/egui/issues/7455">#7455</a>)</li>
<li><a
href="d739a4b880"><code>d739a4b</code></a>
Request a redraw when the url change through the <code>popstate</code>
event listener (#...</li>
<li><a
href="b732992f69"><code>b732992</code></a>
Fix debug-panic in ScrollArea if contents fit without scrolling (<a
href="https://redirect.github.com/emilk/egui/issues/7440">#7440</a>)</li>
<li><a
href="7036d6a982"><code>7036d6a</code></a>
Fix <code>override_text_color</code> priority (<a
href="https://redirect.github.com/emilk/egui/issues/7439">#7439</a>)</li>
<li><a
href="97fe124443"><code>97fe124</code></a>
Fix <code>WidgetText::Text</code> ignoring fallback font and overrides
(<a
href="https://redirect.github.com/emilk/egui/issues/7361">#7361</a>)</li>
<li><a
href="1ad374f255"><code>1ad374f</code></a>
Fix manual <code>Popup</code> not closing (<a
href="https://redirect.github.com/emilk/egui/issues/7383">#7383</a>)</li>
<li><a
href="1afbb9791a"><code>1afbb97</code></a>
Add <code>ComboBox::popup_style</code> (<a
href="https://redirect.github.com/emilk/egui/issues/7360">#7360</a>)</li>
<li><a
href="85c4b1dce7"><code>85c4b1d</code></a>
Fix glyph rendering: clamp coverage to [0, 1] (<a
href="https://redirect.github.com/emilk/egui/issues/7415">#7415</a>)</li>
<li><a
href="2f7bcf26f2"><code>2f7bcf2</code></a>
Fix multi-line <code>TextShape</code> rotation (<a
href="https://redirect.github.com/emilk/egui/issues/7404">#7404</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/emilk/egui/compare/0.32.0...0.32.1">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 17:13:58 +00:00
Martin Robinson
8d60353d14 script: Add presentation attributes as part of a single PropertyDeclarationBlock (#38684)
Instead of making a block for each attribute, use a single block as
described in a `FIXME` comment by @emilio. This also switch to using
`map` and `and_then` in more places to make the code a bit more concise.

Testing: This should not change behavior other than to incraese
efficiency a
bit and is thus covered by existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-15 16:26:00 +00:00
Nico Burns
046dbd86a1 fonts: Use skrifa to get raw font table data on Linux (#38690)
Use skrifa instead of freetype for extracting raw table data. Allows us
to replace unsafe Freetype code with safe Skrifa code. Also allows us to
avoid copying the table data. Instead we return Arc'd data.

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-15 11:30:29 +00:00
Jerens Lensun
a4fdbe8be3 mach: add type annotation in function for 'python/servo' folder (#38592)
This part of function strictly typed in python by adding ANN rule in
ruff, similiar to #38531.

Testing: `./mach test-tidy`
Fixes: Not related to any issues

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-15 10:37:24 +00:00
Euclid Ye
494493ceb7 Rename InputEvent::MouseLeave to InputEvent::MouseLeftViewport (#38695)
1. `InputEvent::MouseLeave` indicates that mouse has left the viewport
(fired by embedder) or iframe (synthesized in Constellation
f24f225db8/components/constellation/constellation_webview.rs (L119-L122)).
Its handler in script is named as `handle_mouse_leave_event`, which is
very misleading as we have DOM event
[mouseleave](https://w3c.github.io/uievents/#event-type-mouseleave). I
rename it to `MouseLeftViewport` to be consistent with
`WindowEvent::CursorLeft`:
f24f225db8/ports/servoshell/desktop/headed_window.rs (L632-L638)
2. Add doc and rename function, such as `handle_mouse_move_event` to
`handle_native_mouse_move_event` to be closer to
[spec](https://w3c.github.io/uievents/#handle-native-mouse-move).

Testing: Just renaming + skipping unnecessary hit-test in simple case.
Fixes: Nothing but preparing for #38670 and #38435.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-15 09:54:54 +00:00
JoeDow
8b574539d1 script: Ensure notify_invalidations() is always called when modifying stylesheets (#38530)
This change supplements the missing stylesheet invalidation
notifications to fix some bugs that the modification of stylesheet does
not take effect. Additionally, this PR add a RAII thing to mark the
modification scope of stylesheet rules, which will facilitate to add
extra logic before the modification happens.

Fixes: there is relevant issue #38211 , but it can't be fixed by this
PR.
Testing: This fixes some subtests in
`/css/cssom/CSSStyleSheet-constructable.html`.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-08-15 09:14:34 +00:00
Euclid Ye
aca4bde93d servoshell: Do not send mouse button events to Servo that happen outside the WebView (#38696)
`webview_relative_mouse_point` can have negative y-coordinate when at
toolbar, but we still would notify compositor the native mousebutton
event to do hit-test.

Testing: This fixes the way that mouse events are passed from servoshell
to Servo,
but there is currently no way to test those kind of interactions.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-15 08:55:25 +00:00
Usman Yahaya Baba
02dca0fb21 net: Send ResponseContentObj to Devtools (#38625)
Currently, the response tab for a request's detail in devtools does not
show the available data, this was due to how the content is being
structured (not the way firefox's devtools client expects it) and also
the body being discarded and not stored in the actor.
This PR stores the body in the actor , which is then retrieved in
`getResponseContent` and then use it to instantiate the new struct
`ResponseContentObj` which matches the format firefox's expects

Fixes: https://github.com/servo/servo/issues/38128

---------

Signed-off-by: uthmaniv <uthmanyahayababa@gmail.com>
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
2025-08-15 08:26:24 +00:00
minghuaw
d409137e4c Script: Implement TextEncoderStream (#38466)
This implements the `TextEncoderStream` WebIDL interface. 

Testing: Existing WPT tests should be sufficient
Fixes: #37724

---------

Signed-off-by: minghuaw <wuminghua7@huawei.com>
2025-08-15 07:57:00 +00:00
batu_hoang
f24f225db8 webdriver: Implement the "Get Window Handles" command (#38622)
Implment get window handles according to
[spec](https://w3c.github.io/webdriver/#dfn-window-handles).
- Window handles are supposed to identify `browsing context`. However,
based on `get window handle command` and `get window handles command`,
we only need to care about top level browsing context.
- Back then, we use a random generated uuid for eacch webview id, it is
not correct but still work because all commands depend on `webview id`
and `browsing context id`. The only case we need window handle is is
when webdriver gets window object with js script. Since the object is
converted to the id of window's document node, `get window handle`
should return the same thing.

Action run (with updated expectation):
https://github.com/longvatrong111/servo/actions/runs/16957610535
https://github.com/longvatrong111/servo/actions/runs/16957612027

Some tests may sporadically timeout due to unstable hit test.

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
2025-08-15 03:30:56 +00:00
Euclid Ye
dafb0abf31 script: Stop handling native mousedown and mouseup for disabled elements (#38671)
According to spec of
[hit-test](https://w3c.github.io/uievents/#hit-test) for native mouse
event, mousedown/mouseup should also be excluded when interacting with
disabled element, even tho it may be the frontmost of
[elementFromPoint](https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint).

Testing: Now it matches the behaviour of other browsers in #38670 for
disabled element. Also testdriver test:
`tests\wpt\tests\html\semantics\disabled-elements\disabled-event-dispatch.tentative.html`
has 4 more passing tests.
Fixes: Part of #38670.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-15 01:24:47 +00:00
dependabot[bot]
89bd01a3b4 build(deps): bump rayon from 1.10.0 to 1.11.0 (#38686)
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.10.0 to 1.11.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rayon-rs/rayon/blob/main/RELEASES.md">rayon's
changelog</a>.</em></p>
<blockquote>
<h1>Release rayon 1.11.0 / rayon-core 1.13.0 (2025-08-12)</h1>
<ul>
<li>The minimum supported <code>rustc</code> is now 1.80.</li>
<li><code>iter::repeatn</code> has been renamed to
<code>iter::repeat_n</code> to match the name
stabilized in the standard library. The old name still exists as a
deprecated
function for compatibility.</li>
<li>Fixed a bug in <code>in_place_scope</code> when the default global
registry uses the
current thread, like on WebAssembly without threading support.</li>
<li><code>binary_heap::Iter</code> no longer requires a temporary
allocation.</li>
<li>Relaxed trait bounds on many of the public structs.</li>
<li>Implemented <code>IntoParallelIterator for Box&lt;[T]&gt;</code> and
its references.</li>
<li>Implemented <code>FromParallelIterator&lt;_&gt; for
Box&lt;str&gt;</code> via <code>String</code>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6236214d71"><code>6236214</code></a>
Merge <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1031">#1031</a></li>
<li><a
href="652f11176a"><code>652f111</code></a>
Release rayon 1.7.0 and rayon-core 1.11.0</li>
<li><a
href="7df001da1b"><code>7df001d</code></a>
Tweak plumbing consumer description</li>
<li><a
href="322dfe87e4"><code>322dfe8</code></a>
Merge <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1026">#1026</a></li>
<li><a
href="099241d410"><code>099241d</code></a>
Merge <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1030">#1030</a></li>
<li><a
href="a17bcb90aa"><code>a17bcb9</code></a>
Fix inappropriate use of slice::as_mut_ptr</li>
<li><a
href="98077fe07c"><code>98077fe</code></a>
Be more cautious about drain drops</li>
<li><a
href="706969515c"><code>7069695</code></a>
Merge <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1028">#1028</a></li>
<li><a
href="874ff73125"><code>874ff73</code></a>
Fix docs for the yield <code>None</code> case on
<code>ThreadPool</code></li>
<li><a
href="58f7b7ec9d"><code>58f7b7e</code></a>
Be careful comparing <code>job_ref.execute_fn</code></li>
<li>Additional commits viewable in <a
href="https://github.com/rayon-rs/rayon/compare/rayon-core-v1.10.0...rayon-core-v1.11.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rayon&package-manager=cargo&previous-version=1.10.0&new-version=1.11.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 23:11:20 +00:00
Sebastian C
027da6580e tests: Reintroduce CookieStore WPT test expectations (#38689)
CookieStore spec graduated from WICG and the test suite in WPT got
renamed to `cookiestore` from `cookie-store`. This readds the baseline
CookieStore expectations in preparation for landing an implementation.

Testing: Reenables tests and readds expectations that were removed.

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
2025-08-14 20:57:11 +00:00
Jonathan Schwender
40e57e95b2 base: Remove last usage of static_assertions crate (#38687)
Replace static_assertions in the `size_of_test` macro with `const
assert`s. The error message is still informative.
Additionally also remove the `cfg` guard, which caused the assertion to
only be enabled on 64 bit platforms, which is something one would not
expect given the name `size_of_test` of the macro.
`cargo tree -i static_assertions` now points to only `stylo` using
`static_assertions`, so we should be able to remove this dependency
fairly easy, since the macro doesn't seem to be used there either at
first glance.

Testing: Covered by existing tests.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-14 19:02:37 +00:00
Jonathan Schwender
f492cbf8c1 base: Allow sending GenericReceiver (#38636)
Implement Serialize and Deserialize for GenericReceiver to also allow
the Receiver to be sent across
ipc channels.
This is necessary to allow using the GenericChannel in more places.

Testing: Manually tested on follow-up feature branch. Does not require
new tests.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-08-14 18:28:58 +00:00
Rahul Menon
b5932e5abf script: Implement QuotaExceededError WebIDL interface (#38507)
Implements the new WebIDL interface for QuotaExceededError and uses it
in appropriate places.

Testing: WPT tests. Now passing many more in
`tests/wpt/tests/WebCryptoAPI/getRandomValues.any.js` and
`tests/wpt/tests/webstorage/storage_session_setitem_quotaexceedederr.window.js`.
Fixes: #38489

---------

Signed-off-by: Rahul Menon <menonrahul02@gmail.com>
2025-08-14 17:58:50 +00:00
Jonathan Schwender
fad247c802 mozjs: Remove unneeded icu_capi features (#38666)
Reduces the binary size by around 16 MiB (ohos-production-stripped, 74M
vs 100M).

Testing: try run:
https://github.com/jschwe/servo/actions/runs/16968926330/job/48103382790

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-14 16:55:32 +00:00
Simon Wülker
6e7226961b layout: Set color and text decoration on <select> elements by default (#38570)
This makes the default style for `<select>` elements match that of gecko
(https://searchfox.org/mozilla-central/rev/a1f4cb9fc03d81be41ca2ba81294592df784364d/layout/style/res/forms.css#217-243),
with some small modifications because servo does not have fancy things
like `color: -moz-ComboboxText;`.

Testing: Includes a new web platform test
Fixes: https://github.com/servo/servo/issues/37895

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-14 14:17:48 +00:00
Martin Robinson
99ce81cf64 layout: Support storing layout data for two-level nested pseudo-elements (#38678)
Add basic support for storing layout data for pseudo-elements nested to
up to two levels. This removes the last unstored layout result and fixes
a double-borrow issue. This change does not add properly parsing nor
styling of these element types, but does prepare for those changes which
must come from stylo.

Testing: This fixes a intermittent panic in
`tests/wpt/tests/css/css-lists/nested-marker-styling.html`
Fixes: #38177. 
Closes: #38183.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-14 13:41:34 +00:00
Simon Wülker
43da933247 script: Implement CSS.registerProperty (#38682)
The implementation is mostly equivalent to
https://searchfox.org/mozilla-central/source/servo/ports/geckolib/glue.rs#9480.

Testing: New web platform tests start to pass

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-14 13:34:02 +00:00
Simon Wülker
3a0e8fefde script: Always throw when trying to setProperty on a readonly style CSSStyleDeclaration (#38677)
Previously,
[`SetProperty`](https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty)
implemented step 3 before calling into the inner `set_property` method,
which implements step 1. Therefore if step 3 returned then step 1 never
runs, and can't throw an exception.


Testing: A new web platform test starts to pass

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-14 13:27:39 +00:00
lumiscosity
b747589286 script: Strip javascript URL scheme using Position::AfterScheme rather than Position::BeforePath (#38599)
This makes the initial split match step 2 of
https://html.spec.whatwg.org/multipage/browsing-the-web.html#evaluate-a-javascript%3A-url.

Testing: Covered by WPT tests.
Fixes: #38547

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
2025-08-14 12:49:07 +00:00
Gregory Terzian
be5e7a982b script: check if the canvas is paintable before measuring text (#38664)
The `Canvas2dMsg::MeasureText` is dropped inside `send_canvas_2d_msg` if
the canvas is not paintable,
leading to a panic on the receiving end. Checking the paint-ability
before sending the message prevents this panic, and if the canvas is not
pain-table, a default text metrics is used.

Testing: Manual testing of the minimal test case in the associated
issue, and crash test added.
Fixes: https://github.com/servo/servo/issues/36845

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-14 12:48:56 +00:00
sagudev
cf866d12b4 chore: Pin cargo-deny to 0.18.3 (#38681)
cargo-deny@0.18.4 requires Rust 1.88

Testing: Fixes CI.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-14 12:15:15 +00:00
Jerens Lensun
797db25c4e mach: Enable ANN rules (type annotations) for ruff Python linter (#38531)
This changes will introduce [flake8-annotations
(ANN)](https://docs.astral.sh/ruff/rules/#flake8-annotations-ann) for
python type annotation, this will make all thing related to function
strictly typed in python

This rule will start to affected this directory from now:
- /python -> Root directory
- /python/tidy
- /python/wpt

Testing: `./mach test-tidy`
Fixes: Not related to any issues

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-14 10:36:17 +00:00
Timothy Elems
9c1ee4be83 bencher cli flags rename (#38661)
Renames `--branch-start-point`, `--branch-start-point-hash`, and
`--branch-reset`, in favor of `--start-point`, `--start-point-hash`, and
`--start-point-reset`, according to [Bencher's
Docs](https://bencher.dev/docs/reference/changelog/#v0421).

Fixes: #38501

---------

Signed-off-by: Timmy <timmy@Timmys-Mac-mini.local>
Signed-off-by: Timothy Elems <timmy@Timmys-Mac-mini.local>
Signed-off-by: elemstimothy@protonmail.com <timmy@Timmys-Mac-mini.local>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Timmy <timmy@Timmys-Mac-mini.local>
Co-authored-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
2025-08-14 07:25:55 +00:00
Jonathan Schwender
d2ccce6052 opts: Use OnceLock for Options (#38638)
Currently, the options are only initialized once and then never changed.
Making this explicit allows optimizing accesses, e.g. caching opt
values.
For some options like `multiprocess` it is obvious that we would never
want to support changing the value at runtime, however there are other
options where it could be conceivable that we want to change them at
runtime. However, imho such options might be better suited to put into a
different datastructure, so that it is explicit which options can be
changed at runtime, and which are fixed.

Testing: Covered by existing tests (and manually running servo in
multiprocess mode).

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-14 07:05:11 +00:00
Euclid Ye
cd3d982a2a script: Remove duplicate context menu trigger (#38669)
#38584 moves input event handling to new `DocumentEventHandler`, but
probably reintroduced some removed code when resolving conflict with
#38589.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-14 03:47:15 +00:00
lumiscosity
97a114b248 mach: remove ubuntu version warning (#38656)
Removes the unsupported ubuntu version warning from mach. Also removes
the related version values on the Linux object, as they're not used
anywhere else nor are they maintained.

Testing: Only removes a warning, no test necessary.
Fixes: #38505.

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
2025-08-13 18:56:38 +00:00
Josh Matthews
f3f981030f Enable experimental features in servodriver. (#38648)
Applies the change from https://github.com/servo/servo/pull/36335 to the
non-default servodriver WPT executor as well.

Testing: Tested manually with `--product=servodriver` on the webxr
directory.
Part of #34683

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-13 18:55:52 +00:00
Simon Wülker
dac34c0dfd fonts: Unify letter-spacing/word-spacing implementations for harfbuzz and fast path (#38657)
Servo two shapers: harfbuzz and a "fast path" for simple cases that
don't have kerning or substitution tables. Both currently implement
`letter-spacing` and `word-spacing` seperately. Theres also a slight
difference, harfbuzz will apply word spacing to preserved newlines and
the fast path won't.

This change unifies the two implementations.

Testing: Covered by existing tests

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-13 18:55:28 +00:00
dependabot[bot]
c741e45b21 build(deps): bump clap from 4.5.44 to 4.5.45 (#38662)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.44 to 4.5.45.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.45</h2>
<h2>[4.5.45] - 2025-08-12</h2>
<h3>Fixes</h3>
<ul>
<li><em>(unstable-v5)</em> <code>ValueEnum</code> variants now use the
full doc comment, not summary, for <code>PossibleValue::help</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.45] - 2025-08-12</h2>
<h3>Fixes</h3>
<ul>
<li><em>(unstable-v5)</em> <code>ValueEnum</code> variants now use the
full doc comment, not summary, for <code>PossibleValue::help</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="246d972a6c"><code>246d972</code></a>
chore: Release</li>
<li><a
href="a35a0761ae"><code>a35a076</code></a>
docs: Update changelog</li>
<li><a
href="9b985a3c17"><code>9b985a3</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5912">#5912</a>
from epage/takes</li>
<li><a
href="389fbe87d2"><code>389fbe8</code></a>
feat(builder): Allow flags to take num_args=0..=1</li>
<li><a
href="c395d02703"><code>c395d02</code></a>
test(parser): Show flag behavior</li>
<li><a
href="32c119efa6"><code>32c119e</code></a>
refactor(assert): Be more specific than action.takes_values</li>
<li><a
href="80ea3e7c24"><code>80ea3e7</code></a>
fix(assert): Clean up num_args/action assert</li>
<li><a
href="2bc0f45fe5"><code>2bc0f45</code></a>
fix(builder): Make ValueRange display independent of usize::MAX</li>
<li><a
href="a0187c6f3b"><code>a0187c6</code></a>
test(assert): Verify num_args/action compat</li>
<li><a
href="a8f9885250"><code>a8f9885</code></a>
refactor(builder): Be more explicit in how takes_values is used</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.44...clap_complete-v4.5.45">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.5.44&new-version=4.5.45)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 18:22:35 +00:00
dependabot[bot]
591b107b3a build(deps): bump rayon-core from 1.12.1 to 1.13.0 (#38663)
Bumps [rayon-core](https://github.com/rayon-rs/rayon) from 1.12.1 to
1.13.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rayon-rs/rayon/blob/main/RELEASES.md">rayon-core's
changelog</a>.</em></p>
<blockquote>
<h1>Release rayon 1.11.0 / rayon-core 1.13.0 (2025-08-12)</h1>
<ul>
<li>The minimum supported <code>rustc</code> is now 1.80.</li>
<li><code>iter::repeatn</code> has been renamed to
<code>iter::repeat_n</code> to match the name
stabilized in the standard library. The old name still exists as a
deprecated
function for compatibility.</li>
<li>Fixed a bug in <code>in_place_scope</code> when the default global
registry uses the
current thread, like on WebAssembly without threading support.</li>
<li><code>binary_heap::Iter</code> no longer requires a temporary
allocation.</li>
<li>Relaxed trait bounds on many of the public structs.</li>
<li>Implemented <code>IntoParallelIterator for Box&lt;[T]&gt;</code> and
its references.</li>
<li>Implemented <code>FromParallelIterator&lt;_&gt; for
Box&lt;str&gt;</code> via <code>String</code>.</li>
</ul>
<h1>Release rayon 1.10.0 (2024-03-23)</h1>
<ul>
<li>The new methods <code>ParallelSlice::par_chunk_by</code> and
<code>ParallelSliceMut::par_chunk_by_mut</code> work like the slice
methods <code>chunk_by</code>
and <code>chunk_by_mut</code> added in Rust 1.77.</li>
</ul>
<h1>Release rayon 1.9.0 (2024-02-27)</h1>
<ul>
<li>The new methods
<code>IndexedParallelIterator::by_exponential_blocks</code> and
<code>by_uniform_blocks</code> allow processing items in smaller groups
at a time.</li>
<li>The new <code>iter::walk_tree</code>, <code>walk_tree_prefix</code>,
and <code>walk_tree_postfix</code>
functions enable custom parallel iteration over tree-like
structures.</li>
<li>The new method <code>ParallelIterator::collect_vec_list</code>
returns items as a linked
list of vectors, which is an efficient mode of parallel collection used
by
many of the internal implementations of <code>collect</code>.</li>
<li>The new methods
<code>ParallelSliceMut::par_split_inclusive_mut</code>,
<code>ParallelSlice::par_split_inclusive</code>, and
<code>ParallelString::par_split_inclusive</code> all work like a normal
split but
keeping the separator as part of the left slice.</li>
<li>The new <code>ParallelString::par_split_ascii_whitespace</code>
splits only on ASCII
whitespace, which is faster than including Unicode multi-byte
whitespace.</li>
<li><code>OsString</code> now implements
<code>FromParallelIterator&lt;_&gt;</code> and
<code>ParallelExtend&lt;_&gt;</code>
for a few item types similar to the standard <code>FromIterator</code>
and <code>Extend</code>.</li>
<li>The internal <code>Pattern</code> trait for string methods is now
implemented for
<code>[char; N]</code> and <code>&amp;[char; N]</code>, matching any of
the given characters.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7af20d7692"><code>7af20d7</code></a>
Merge pull request <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1265">#1265</a>
from cuviper/release-1.11.0</li>
<li><a
href="c86161af76"><code>c86161a</code></a>
Release rayon 1.11.0 / rayon-core 1.13.0</li>
<li><a
href="72345cbfdc"><code>72345cb</code></a>
Merge pull request <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1264">#1264</a>
from cuviper/mem-prelude</li>
<li><a
href="b4c58afc5a"><code>b4c58af</code></a>
Merge pull request <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1263">#1263</a>
from cuviper/boxed</li>
<li><a
href="8fdeaf34aa"><code>8fdeaf3</code></a>
Use <code>size_of</code>/<code>size_of_val</code> from the prelude</li>
<li><a
href="2a85fbf8f8"><code>2a85fbf</code></a>
<code>impl FromParallelIterator\&lt;_&gt; for Box\&lt;str&gt;</code> via
<code>String</code></li>
<li><a
href="760a97c624"><code>760a97c</code></a>
<code>impl\&lt;T&gt; IntoParallelIterator for Box\&lt;[T]&gt;</code> and
its refs</li>
<li><a
href="dcea6644cf"><code>dcea664</code></a>
Merge pull request <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1262">#1262</a>
from cuviper/relax-bounds</li>
<li><a
href="3d63a8715a"><code>3d63a87</code></a>
Relax trait bounds on many structs</li>
<li><a
href="0baaff5072"><code>0baaff5</code></a>
Merge pull request <a
href="https://redirect.github.com/rayon-rs/rayon/issues/1261">#1261</a>
from cuviper/compounds</li>
<li>Additional commits viewable in <a
href="https://github.com/rayon-rs/rayon/compare/rayon-core-v1.12.1...rayon-core-v1.13.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rayon-core&package-manager=cargo&previous-version=1.12.1&new-version=1.13.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 18:12:01 +00:00
dependabot[bot]
026f00f027 build(deps): bump syn from 2.0.104 to 2.0.105 (#38660)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.104 to 2.0.105.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/syn/releases">syn's
releases</a>.</em></p>
<blockquote>
<h2>2.0.105</h2>
<ul>
<li>Disallow &quot;negative&quot; inherent impls like <code>impl !T
{}</code> (<a
href="https://redirect.github.com/dtolnay/syn/issues/1881">#1881</a>, <a
href="https://redirect.github.com/rust-lang/rust/pull/144386">rust-lang/rust#144386</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9a8cc49b7b"><code>9a8cc49</code></a>
Release 2.0.105</li>
<li><a
href="0f8962b2e9"><code>0f8962b</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/syn/issues/1881">#1881</a>
from dtolnay/negativeinherent</li>
<li><a
href="337aeac6d7"><code>337aeac</code></a>
Reject negative inherent impls</li>
<li><a
href="95c5e5a51f"><code>95c5e5a</code></a>
Update test suite to nightly-2025-08-13</li>
<li><a
href="20e155af00"><code>20e155a</code></a>
Revert &quot;Pin nightly toolchain used for miri job&quot;</li>
<li><a
href="bef1da9f56"><code>bef1da9</code></a>
Resolve implicit_clone pedantic clippy lint</li>
<li><a
href="aa9d9924a9"><code>aa9d992</code></a>
Update test suite to nightly-2025-07-24</li>
<li><a
href="3a2005c22d"><code>3a2005c</code></a>
Update test suite to nightly-2025-07-18</li>
<li><a
href="7ee23a7bed"><code>7ee23a7</code></a>
Update to toml 0.9</li>
<li><a
href="6b30c4b035"><code>6b30c4b</code></a>
Sort tests/common/eq.rs imports</li>
<li>Additional commits viewable in <a
href="https://github.com/dtolnay/syn/compare/2.0.104...2.0.105">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=syn&package-manager=cargo&previous-version=2.0.104&new-version=2.0.105)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 17:41:37 +00:00
Gregory Terzian
70be996a29 net: Remove CoreResourceThread from FetchThread state (#38422)
In single process mode, there is a race condition on the initialization
of the global fetch thread: once initialized the global fetch thread
will always use a given core resource thread, and this will be
determined by the component who first initializes it. For example, if
the canvas paint thread first does an async fetch, then this will set
the public core resource as used for all future fetches, including those
coming from a pipeline in private mode.

In multi-process mode, there is a race condition per window event-loop:
the first pipeline to use the fetch will set the core resource thread
for all others.

To ensure the fetch thread uses the correct core resource thread(private
vs public), we need to
pass the core resource thread to each fetch thread operation for which
is it needed.

Testing: It should not break existing fetch WPT tests. The race
condition is not something that can be tested reliably, but it seems to
be based on solid logic.
Fixes: follow-up from
https://github.com/servo/servo/pull/38421/files#r2248950924

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-13 17:40:10 +00:00
Martin Robinson
5ff084a688 layout: Use the PseudoElement from ServoThreadSafeLayoutNode in NodeAndStyleInfo (#38630)
Instead of storing the non-pseudo version of the node in
`NodeAndStyleInfo`, store the pseudo version and use that to query the
`PseudoElement` that a `NodeAndStyleInfo` refers to.

This is a step on the way toward fixing nested pseudo-elements in Servo.

Testing: This should not change behavior and is thus covered by existing
WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-13 17:31:57 +00:00
Martin Robinson
ee7c1d9109 layout: Use ServoThreadSafeLayoutNode in more places (#38626)
Use `ServoThreadSafeLayoutNode` in more places in layout rather than
`ServoLayoutNode`. The former is meant to be used during layout, but
layout 2020 was written against the latter. In general, this reduces the
amount of conversion to the thread-safe version in many places in
layout.

In addition, an unused iterator from the `script` crate
`ServoThreadSafeLayoutNodeChildrenIterator` is replaced with the child
iterator from `layout`. The `layout` version must be directly in
`script` now as it uses the dangerous variants of `next_sibling` and
`first_child`, which allow encapsulating the unsafe bits into one
module.

This will ultimately be useful for storing the layout data of
pseudo-element children of pseudo-elements properly.

Testing: This should not change any behavior and thus is covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-13 14:55:19 +00:00
Martin Robinson
20ad1ce84e Revert "ci: Run devtools tests whenever we run unit tests (#38614)"
This reverts commit 47aa9ea8cf.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-13 07:53:20 -07:00
dependabot[bot]
18f0d92e99 build(deps): bump the napi-ohos-related group with 4 updates (#38555)
Bumps the napi-ohos-related group with 4 updates:
[napi-derive-ohos](https://github.com/ohos-rs/ohos-rs),
[napi-ohos](https://github.com/ohos-rs/ohos-rs),
[napi-derive-backend-ohos](https://github.com/ohos-rs/ohos-rs) and
[napi-sys-ohos](https://github.com/ohos-rs/ohos-rs).

Updates `napi-derive-ohos` from 1.0.4 to 1.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="eaba5da8af"><code>eaba5da</code></a>
Merge pull request <a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/139">#139</a>
from ohos-rs/feat-0719</li>
<li><a
href="165538b516"><code>165538b</code></a>
fix: lint warning</li>
<li><a
href="19530e3598"><code>19530e3</code></a>
Bump version</li>
<li><a
href="ed8d46d825"><code>ed8d46d</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="7c3dc968c1"><code>7c3dc96</code></a>
perf(napi): optimize HashMap allocation in FromNapiValue implementation
for H...</li>
<li><a
href="06a48c5e18"><code>06a48c5</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="9f0dc581be"><code>9f0dc58</code></a>
chore(release): publish</li>
<li><a
href="cf6d8b0334"><code>cf6d8b0</code></a>
fix(cli): rename options (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2804">#2804</a>)</li>
<li><a
href="5816924e6e"><code>5816924</code></a>
chore(release): publish</li>
<li><a
href="c6cf127e7e"><code>c6cf127</code></a>
chore(cli): upgrade all NAPI-RS projects (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2803">#2803</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/ohos-rs/ohos-rs/compare/ohrs@1.0.4...ohrs@1.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `napi-ohos` from 1.0.4 to 1.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="eaba5da8af"><code>eaba5da</code></a>
Merge pull request <a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/139">#139</a>
from ohos-rs/feat-0719</li>
<li><a
href="165538b516"><code>165538b</code></a>
fix: lint warning</li>
<li><a
href="19530e3598"><code>19530e3</code></a>
Bump version</li>
<li><a
href="ed8d46d825"><code>ed8d46d</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="7c3dc968c1"><code>7c3dc96</code></a>
perf(napi): optimize HashMap allocation in FromNapiValue implementation
for H...</li>
<li><a
href="06a48c5e18"><code>06a48c5</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="9f0dc581be"><code>9f0dc58</code></a>
chore(release): publish</li>
<li><a
href="cf6d8b0334"><code>cf6d8b0</code></a>
fix(cli): rename options (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2804">#2804</a>)</li>
<li><a
href="5816924e6e"><code>5816924</code></a>
chore(release): publish</li>
<li><a
href="c6cf127e7e"><code>c6cf127</code></a>
chore(cli): upgrade all NAPI-RS projects (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2803">#2803</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/ohos-rs/ohos-rs/compare/ohrs@1.0.4...ohrs@1.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `napi-derive-backend-ohos` from 1.0.4 to 1.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="eaba5da8af"><code>eaba5da</code></a>
Merge pull request <a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/139">#139</a>
from ohos-rs/feat-0719</li>
<li><a
href="165538b516"><code>165538b</code></a>
fix: lint warning</li>
<li><a
href="19530e3598"><code>19530e3</code></a>
Bump version</li>
<li><a
href="ed8d46d825"><code>ed8d46d</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="7c3dc968c1"><code>7c3dc96</code></a>
perf(napi): optimize HashMap allocation in FromNapiValue implementation
for H...</li>
<li><a
href="06a48c5e18"><code>06a48c5</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="9f0dc581be"><code>9f0dc58</code></a>
chore(release): publish</li>
<li><a
href="cf6d8b0334"><code>cf6d8b0</code></a>
fix(cli): rename options (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2804">#2804</a>)</li>
<li><a
href="5816924e6e"><code>5816924</code></a>
chore(release): publish</li>
<li><a
href="c6cf127e7e"><code>c6cf127</code></a>
chore(cli): upgrade all NAPI-RS projects (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2803">#2803</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/ohos-rs/ohos-rs/compare/ohrs@1.0.4...ohrs@1.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `napi-sys-ohos` from 1.0.4 to 1.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ohos-rs/ohos-rs/releases">napi-sys-ohos's
releases</a>.</em></p>
<blockquote>
<h2>ohrs@1.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: allow build and artifact skip libs logic by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/119">ohos-rs/ohos-rs#119</a></li>
<li>fix: remove ArkRuntime Send and Sync implenment by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/120">ohos-rs/ohos-rs#120</a></li>
<li>feat 0306 by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/121">ohos-rs/ohos-rs#121</a></li>
<li>feat: add export default for template by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/128">ohos-rs/ohos-rs#128</a></li>
<li>fix: should ignore some path and convert path to absolute path by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/129">ohos-rs/ohos-rs#129</a></li>
<li>Merge code by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/133">ohos-rs/ohos-rs#133</a></li>
<li>feat: add vscode setting by default by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/134">ohos-rs/ohos-rs#134</a></li>
<li>feat(cli): support dtsCache flag by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/135">ohos-rs/ohos-rs#135</a></li>
<li>feat: Sync napi-rs by <a
href="https://github.com/richerfu"><code>@​richerfu</code></a> in <a
href="https://redirect.github.com/ohos-rs/ohos-rs/pull/139">ohos-rs/ohos-rs#139</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/ohos-rs/ohos-rs/compare/ohrs@1.0.4...ohrs@1.1.0">https://github.com/ohos-rs/ohos-rs/compare/ohrs@1.0.4...ohrs@1.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="eaba5da8af"><code>eaba5da</code></a>
Merge pull request <a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/139">#139</a>
from ohos-rs/feat-0719</li>
<li><a
href="165538b516"><code>165538b</code></a>
fix: lint warning</li>
<li><a
href="19530e3598"><code>19530e3</code></a>
Bump version</li>
<li><a
href="ed8d46d825"><code>ed8d46d</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="7c3dc968c1"><code>7c3dc96</code></a>
perf(napi): optimize HashMap allocation in FromNapiValue implementation
for H...</li>
<li><a
href="06a48c5e18"><code>06a48c5</code></a>
Merge branch 'main' into feat-0719</li>
<li><a
href="9f0dc581be"><code>9f0dc58</code></a>
chore(release): publish</li>
<li><a
href="cf6d8b0334"><code>cf6d8b0</code></a>
fix(cli): rename options (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2804">#2804</a>)</li>
<li><a
href="5816924e6e"><code>5816924</code></a>
chore(release): publish</li>
<li><a
href="c6cf127e7e"><code>c6cf127</code></a>
chore(cli): upgrade all NAPI-RS projects (<a
href="https://redirect.github.com/ohos-rs/ohos-rs/issues/2803">#2803</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/ohos-rs/ohos-rs/compare/ohrs@1.0.4...ohrs@1.1.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-13 12:52:00 +00:00
Jonathan Schwender
d0d952d1fa build(deps): bump nix from 0.29.0 to 0.30.1 (#38650)
Bumps [nix](https://github.com/nix-rust/nix) from 0.29.0 to 0.30.1.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.29.0...v0.30.1)

Closes: #38145
Testing: Covered by existing tests

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-13 09:51:40 +00:00
Martin Robinson
069ad40872 script: Move the majority of the input event handling code to DocumentEventHandler (#38584)
This moves the majority of the input event handler code to the
`DocumentEventHandler` helper structure. It better encapsulates event
handling, hiding most of the details from both `ScriptThread` and
`Document`. The benefit here is that the majority of the functions can
become private and `Document` is over 1000 lines shorter.

Testing: This should not change any behavior so is covered by existing
WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-13 09:38:58 +00:00
Josh Matthews
bd9bb77295 script_bindings: Assert that serializable/transferable types have accurate WebIDL annotations (#38615)
These changes add compile-time assertions that:
* any type that implements the Serializable/Transferable trait has a
`[Serializable]` or `[Transferable]` annotation in the interface WebIDL
* any WebIDL interface with the `[Serializable]` or `[Transferable]`
annotation implements the corresponding trait

This is useful because it means that WebIDL definitions will be less
confusing if you're trying to figure out whether Servo supports
serializing/transferring a particular interface type. It also makes
fixing #21715 in the future a little bit easier, because the annotations
will remain up to date.

Testing: compile-time only; no point in writing tests for this since it
involves webidl codegen.

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-13 08:36:04 +00:00
dependabot[bot]
0d6d434e59 build(deps): bump anyhow from 1.0.98 to 1.0.99 (#38635)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.98 to 1.0.99.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f2b963a759"><code>f2b963a</code></a>
Release 1.0.99</li>
<li><a
href="2c64c15e75"><code>2c64c15</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/anyhow/issues/420">#420</a>
from dtolnay/enotempty</li>
<li><a
href="8cf66f7936"><code>8cf66f7</code></a>
Allow build-script cleanup failure with NFSv3 output directory to be
non-fatal</li>
<li><a
href="f5e145c683"><code>f5e145c</code></a>
Revert &quot;Pin nightly toolchain used for miri job&quot;</li>
<li><a
href="1d7ef1db54"><code>1d7ef1d</code></a>
Update ui test suite to nightly-2025-06-30</li>
<li><a
href="69295727ce"><code>6929572</code></a>
Update ui test suite to nightly-2025-06-18</li>
<li><a
href="37224e3142"><code>37224e3</code></a>
Ignore mismatched_lifetime_syntaxes lint</li>
<li><a
href="11f0e81aaf"><code>11f0e81</code></a>
Pin nightly toolchain used for miri job</li>
<li><a
href="d04c999d63"><code>d04c999</code></a>
Raise required compiler for backtrace feature to rust 1.82</li>
<li><a
href="219d16330d"><code>219d163</code></a>
Update test suite to nightly-2025-05-01</li>
<li>See full diff in <a
href="https://github.com/dtolnay/anyhow/compare/1.0.98...1.0.99">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=anyhow&package-manager=cargo&previous-version=1.0.98&new-version=1.0.99)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 06:58:27 +00:00
Euclid Ye
6029976365 webdriver: Synchronize "close window" command & Return correct error type (#38620)
1. Synchronize [Close
Window](https://w3c.github.io/webdriver/#dfn-close-window) command to
reduce intermittency
2. There was a update last month exposing that we are not returning
correct error type for Session getter.
https://github.com/web-platform-tests/wpt/pull/53735
3. Other trivial fix

Testing: 
- `/webdriver/tests/classic/close_window/close.py` can now fully pass.
- `/webdriver/tests/classic/delete_session/*` can now fully pass.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-13 04:06:39 +00:00
dependabot[bot]
7fc2f31309 build(deps): bump clap from 4.5.43 to 4.5.44 (#38641)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [clap](https://github.com/clap-rs/clap) from 4.5.43 to 4.5.44.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.44</h2>
<h2>[4.5.44] - 2025-08-11</h2>
<h3>Features</h3>
<ul>
<li>Add <code>Command::mut_subcommands</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.44] - 2025-08-11</h2>
<h3>Features</h3>
<ul>
<li>Add <code>Command::mut_subcommands</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8e2c2c5678"><code>8e2c2c5</code></a>
chore: Release</li>
<li><a
href="427d4fc0ea"><code>427d4fc</code></a>
docs: Update changelog</li>
<li><a
href="3b99593a1e"><code>3b99593</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5896">#5896</a>
from epage/env</li>
<li><a
href="f5fc7651ee"><code>f5fc765</code></a>
feat(complete): Provide a way to disable env completions</li>
<li>See full diff in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.43...clap_complete-v4.5.44">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.5.43&new-version=4.5.44)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 00:47:45 +00:00
dependabot[bot]
6381c76f64 build(deps): bump proc-macro2 from 1.0.96 to 1.0.97 (#38639)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.96
to 1.0.97.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/proc-macro2/releases">proc-macro2's
releases</a>.</em></p>
<blockquote>
<h2>1.0.97</h2>
<ul>
<li>Allow build-script cleanup failure with NFSv3 output directory to be
non-fatal (<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/505">#505</a>,
<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/512">#512</a>,
thanks <a
href="https://github.com/davvid"><code>@​davvid</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b61379bb76"><code>b61379b</code></a>
Release 1.0.97</li>
<li><a
href="50fc51474b"><code>50fc514</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/512">#512</a>
from dtolnay/enotempty</li>
<li><a
href="7cc389b769"><code>7cc389b</code></a>
Restore support for rustc older than 1.83</li>
<li><a
href="ad5915822d"><code>ad59158</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/505">#505</a>
from davvid/ignore-cleanup</li>
<li><a
href="19cf120b83"><code>19cf120</code></a>
build: allow the cleanup step to be non-fatal</li>
<li>See full diff in <a
href="https://github.com/dtolnay/proc-macro2/compare/1.0.96...1.0.97">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=proc-macro2&package-manager=cargo&previous-version=1.0.96&new-version=1.0.97)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 00:11:50 +00:00
dependabot[bot]
319909782c build(deps): bump the servo-media-related group with 12 updates (#38640)
Bumps the servo-media-related group with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [servo-media](https://github.com/servo/media) | ``4f36494`` |
``a57b0e6`` |
| [servo-media-dummy](https://github.com/servo/media) | ``4f36494`` |
``a57b0e6`` |
| [servo-media-gstreamer](https://github.com/servo/media) | ``4f36494``
| ``a57b0e6`` |
| servo-media-audio | ``4f36494`` | ``a57b0e6`` |
| servo-media-derive | ``4f36494`` | ``a57b0e6`` |
| servo-media-gstreamer-render | ``4f36494`` | ``a57b0e6`` |
| servo-media-gstreamer-render-android | ``4f36494`` | ``a57b0e6`` |
| servo-media-gstreamer-render-unix | ``4f36494`` | ``a57b0e6`` |
| servo-media-player | ``4f36494`` | ``a57b0e6`` |
| servo-media-streams | ``4f36494`` | ``a57b0e6`` |
| servo-media-traits | ``4f36494`` | ``a57b0e6`` |
| servo-media-webrtc | ``4f36494`` | ``a57b0e6`` |

Updates `servo-media` from `4f36494` to `a57b0e6`
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a57b0e67b3"><code>a57b0e6</code></a>
initial stub for ohos backend (<a
href="https://redirect.github.com/servo/media/issues/444">#444</a>)</li>
<li>See full diff in <a
href="4f36494e4d...a57b0e67b3">compare
view</a></li>
</ul>
</details>
<br />

Updates `servo-media-dummy` from `4f36494` to `a57b0e6`
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a57b0e67b3"><code>a57b0e6</code></a>
initial stub for ohos backend (<a
href="https://redirect.github.com/servo/media/issues/444">#444</a>)</li>
<li>See full diff in <a
href="4f36494e4d...a57b0e67b3">compare
view</a></li>
</ul>
</details>
<br />

Updates `servo-media-gstreamer` from `4f36494` to `a57b0e6`
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a57b0e67b3"><code>a57b0e6</code></a>
initial stub for ohos backend (<a
href="https://redirect.github.com/servo/media/issues/444">#444</a>)</li>
<li>See full diff in <a
href="4f36494e4d...a57b0e67b3">compare
view</a></li>
</ul>
</details>
<br />

Updates `servo-media-audio` from `4f36494` to `a57b0e6`

Updates `servo-media-derive` from `4f36494` to `a57b0e6`

Updates `servo-media-gstreamer-render` from `4f36494` to `a57b0e6`

Updates `servo-media-gstreamer-render-android` from `4f36494` to
`a57b0e6`

Updates `servo-media-gstreamer-render-unix` from `4f36494` to `a57b0e6`

Updates `servo-media-player` from `4f36494` to `a57b0e6`

Updates `servo-media-streams` from `4f36494` to `a57b0e6`

Updates `servo-media-traits` from `4f36494` to `a57b0e6`

Updates `servo-media-webrtc` from `4f36494` to `a57b0e6`


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 23:02:12 +00:00
dependabot[bot]
c0eb999dbe build(deps): bump ab_glyph_rasterizer from 0.1.9 to 0.1.10 (#38637)
Bumps [ab_glyph_rasterizer](https://github.com/alexheretic/ab-glyph)
from 0.1.9 to 0.1.10.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="971e5a9b33"><code>971e5a9</code></a>
Release ab_glyph_rasterizer 0.1.10</li>
<li><a
href="f2e376ac04"><code>f2e376a</code></a>
Fix remaining <code>draw_line_scalar</code> index oob panic scenarios
(<a
href="https://redirect.github.com/alexheretic/ab-glyph/issues/120">#120</a>)</li>
<li><a
href="c476792ab6"><code>c476792</code></a>
Fix 1.89 clippy lints</li>
<li><a
href="727737fbe9"><code>727737f</code></a>
Update dev-dependencies</li>
<li><a
href="f963bcaf08"><code>f963bca</code></a>
Release ab_glyph 0.2.31</li>
<li><a
href="a216ede621"><code>a216ede</code></a>
Add &quot;gvar-alloc&quot; feature enabled by default (<a
href="https://redirect.github.com/alexheretic/ab-glyph/issues/118">#118</a>)</li>
<li>See full diff in <a
href="https://github.com/alexheretic/ab-glyph/compare/ab-glyph-rasterizer-0.1.9...ab-glyph-rasterizer-0.1.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ab_glyph_rasterizer&package-manager=cargo&previous-version=0.1.9&new-version=0.1.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 20:53:33 +00:00
Kenzie Raditya Tirtarahardja
bcd569f116 webdriver: Add handle user prompt for some command (#38591)
- When taking element screenshot, we should handle the user prompt
before getting the element bounding rectangle
- Handle user prompt for `is element enabled` and `is element selected`

Testing:
`./tests/wpt/tests/webdriver/tests/classic/{take_element_screenshot,
is_element_enabled, is_element_selected}/user_prompts.py`

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-12 20:31:43 +00:00
Jonathan Schwender
4b91dc6d82 CI: Check MSRV in CI (#37152)
As previously proposed on zulip and discussed in the coordination
meeting, add a check to CI to see if
servo still compiles with our minimum supported Rust version.
To avoid requiring changes, we define our MSRV as the current version we
are using now (1.85.0).

This does not prevent us from updating the default compiler version,
which we should still do, to
get benefits like faster compile times, newer lints and making sure
crown stays up-to-date.

We simply test that libservo compiles in CI, since libservo (and
dependencies) is what embedders would care about. We also don't need
mach (or bootstrap!) for this, so we just use cargo build.

Testing: This PR adds a CI test. [`./mach try windows-build-libservo
linux-build-libservo
mac-build-libservo`](https://github.com/jschwe/servo/actions/runs/16901171766)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-12 19:19:45 +00:00
Martin Robinson
6ff5c4ef99 tidy: Remove dead and redundant code from Servo Rust tidy check (#38632)
- Remove all handling of comments and attributes. This was not affecting
  the checks in any way.
- Remove `is_associated_type()` as it was unused.
- Remove the check for `&String`, `&Vec`, operators at the end of the
  line, and the unit return type as clippy already checks these (and
  handles the mutable variants).

Testing: This is covered by tidy script tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-12 17:57:35 +00:00
Jonathan Schwender
2ea251117a script: Remove unnecessary Arc<Mutex> (#38631)
The `Arc<Mutex<>>` wrapper around the IpcSender does not appear to be
needed anymore.

Testing: No functional changes. Covered by existing tests

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-12 17:48:28 +00:00
shuppy
47aa9ea8cf ci: Run devtools tests whenever we run unit tests (#38614)
this patch updates linux.yml, mac.yml, and windows.yml to run the
devtools test suite (#36325), whenever unit tests are enabled in those
workflows. plus three changes that speed up the tests from 73 → 65 → 56
→ 51 seconds:

- we replace the hardcoded sleep(1) after starting servoshell with a
loop that waits until the devtools port is open (this also fixes
intermittent failures when servoshell starts too slowly, especially on
macOS)
- we start the internal web servers once, and reuse them across all
tests
- we run servoshell in headless mode (this is also required because most
CI runners have no GUI)

finally we fix two bugs that cause very noisy but not very interesting
error messages:

- in the test code, we use a [context
manager](https://docs.python.org/3/reference/datamodel.html#context-managers)
to ensure the devtools client is disconnected unconditionally, even if
test methods or assert helper methods raise exceptions (this was causing
errors on all platforms)
- in the devtools server, we treat “connection reset” errors when
reading from the client like a normal EOF, rather than as a failure
(this was causing errors on Windows)

on self-hosted linux builds, there are still spurious error messages
like the following, but we can fix them later:

```
error: XDG_RUNTIME_DIR not set in the environment.
libEGL warning: egl: failed to create dri2 screen
```

Testing: this patch effectively adds 44 tests to CI
Fixes: #36325

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Signed-off-by: atbrakhi <atbrakhi@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-12 15:10:45 +00:00
Oriol Brufau
141413d52e layout: Improve sizing for inline SVG (#38603)
The metadata provided by usvg has unreliable sizes. Ignore it, and rely
on the `width`, `height` and `viewBox` attributes instead.

Note that inline SVG with a natural aspect ratio but no natural sizes
should stretch to the containing block. This is left for a follow-up.

Bumps Stylo to https://github.com/servo/stylo/pull/229

Testing: Improves several WPT.

---------

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
2025-08-12 12:45:15 +00:00
shuppy
319f4f0e38 devtools: Fix getBreakpointListActor handler in watcher actor (#38624)
the Firefox devtools client often sends multiple
`getBreakpointListActor` requests to the watcher actor when connecting,
and the Firefox devtools server returns the same breakpoint list actor
every time, but Servo returns a new breakpoint list actor each time.
this patch aligns Servo’s behaviour with Firefox.

Testing: this patch adds a devtools test

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-12 12:26:59 +00:00
Euclid Ye
b3978439ad test config: Add escape character (#38619)
Add escape character for config. Otherwise cannot run webdriver test.

Fixes: `wptrunner.wptmanifest.parser.ParseError: Junk before EOL ]`

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-12 08:43:15 +00:00
Rodion Borovyk
e5a73ede17 script: Send JS evaluation errors to constellation (#38602)
Send JS evaluation errors to constellation in
`script_thread::handle_evaluate_javascript` instead of silently
swallowing.

Testing: New test cases in `tests/webview.rs`: one for compilation
failure, another for evaluation failure.
Fixes: #37810

Signed-off-by: Rodion Borovyk <rodion.borovyk@gmail.com>
2025-08-12 07:19:39 +00:00
batu_hoang
9effdce5a1 Implement webdriver extract script arguments (#38357)
Fix script parsing step.
Implement webdriver `extract script arguments`.
Implement `deserialize_web_element` and `deserialize_shadow_root` from
script command argument.

Testing:
`/tests/wpt/tests/webdriver/tests/classic/execute_script/`
`/tests/wpt/tests/webdriver/tests/classic/execute_async_script/`

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
2025-08-12 06:57:19 +00:00
Kenzie Raditya Tirtarahardja
c0d884ddb2 script: Implement getModifierState for mouse event (#38535)
Implement missing idl function `getModifierState` for mouse event. The
implementation is exactly the same as in keyboard event
https://w3c.github.io/uievents/#dom-keyboardevent-getmodifierstate.


6651f37c05/components/script/dom/keyboardevent.rs (L267-L283)

Testing: Fix
`./tests/wpt/tests/infrastructure/testdriver/actions/actionsWithKeyPressed.html`

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-12 06:33:15 +00:00
Abdelrahman Hossam
2e2bfc6067 Basic webdriver support to servoshell on ohos (#38386)
Adding basic webdriver support to servoshell on ohos and basic
communication between the ohos device and script.

Testing: Manual testing and tracing logs

Signed-off-by: abdelrahman1234567 <abdelrahman.hossameldin.awadalla@huawei.com>
2025-08-12 06:26:53 +00:00
shuppy
f5b631e270 devtools: Show clients where they can set breakpoints (#37667)
devtools clients query source actors to determine where the user can set
breakpoints in a source. there are two relevant requests here:
`getBreakableLines` controls which line numbers can be clicked in the
margin, and once a line number is clicked,
`getBreakpointPositionsCompressed` controls where to show breakpoint
buttons within that line.

this patch handles those requests by querying the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/) for that
information:
- devtools sends its script thread a GetPossibleBreakpoints message for
the source’s
[`spidermonkey_id`](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#id)
- the script thread fires a `getPossibleBreakpoints` event into its
debugger global
- the debugger script looks up the
[root](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewscript-script-global)
[Debugger.Script](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getpossiblebreakpoints-query)
for that source, calls
[getPossibleBreakpoints()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getpossiblebreakpoints-query),
and returns the result via
DebuggerGlobalScope#getPossibleBreakpointsResult()
- that method takes the pending result sender, and sends the result back
to devtools
- devtools massages the result into the format required by the request,
and replies to the client

as a result, users of the Firefox devtools client can now set
breakpoints, though they don’t have any effect.

Testing: this patch adds new devtools tests
Fixes: part of #36027

<img width="1433" height="1328" alt="image"
src="https://github.com/user-attachments/assets/f0cd31e0-742f-44d3-8c5d-ceedd9a2706d"
/>

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-12 04:53:53 +00:00
Usman Yahaya Baba
1995e22e19 net: Send Cookies to Devtools (#38601)
The current behaviour in dev tools network monitor is missing cookies in
the cookies tab in Request Details, displaying "No cookies for this
request" even for requests with cookies.
The changes in this PR refactors the `request_cookies` and
`response_cookies` to check and retrieve the cookies if available
Also, the structure in which the cookies is sent to devtools is been
refactored to match the format firefox's devtools client expects.
With these changes, we now have the cookies displayed for requests that
have one.

Fixes: https://github.com/servo/servo/issues/38127

---------

Signed-off-by: uthmaniv <uthmanyahayababa@gmail.com>
2025-08-12 04:42:28 +00:00
dependabot[bot]
122069cf43 build(deps): bump libc from 0.2.174 to 0.2.175 (#38608)
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.174 to 0.2.175.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/libc/releases">libc's
releases</a>.</em></p>
<blockquote>
<h2>0.2.175</h2>
<h3>Added</h3>
<ul>
<li>AIX: Add <code>getpeereid</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4524">#4524</a>)</li>
<li>AIX: Add <code>struct ld_info</code> and friends (<a
href="https://redirect.github.com/rust-lang/libc/pull/4578">#4578</a>)</li>
<li>AIX: Retore <code>struct winsize</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4577">#4577</a>)</li>
<li>Android: Add UDP socket option constants (<a
href="https://redirect.github.com/rust-lang/libc/pull/4619">#4619</a>)</li>
<li>Android: Add <code>CLONE_CLEAR_SIGHAND</code> and
<code>CLONE_INTO_CGROUP</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4502">#4502</a>)</li>
<li>Android: Add more <code>prctl</code> constants (<a
href="https://redirect.github.com/rust-lang/libc/pull/4531">#4531</a>)</li>
<li>FreeBSD Add further TCP stack-related constants (<a
href="https://redirect.github.com/rust-lang/libc/pull/4196">#4196</a>)</li>
<li>FreeBSD x86-64: Add <code>mcontext_t.mc_tlsbase </code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4503">#4503</a>)</li>
<li>FreeBSD15: Add <code>kinfo_proc.ki_uerrmsg</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4552">#4552</a>)</li>
<li>FreeBSD: Add <code>in_conninfo</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4482">#4482</a>)</li>
<li>FreeBSD: Add <code>xinpgen</code> and related types (<a
href="https://redirect.github.com/rust-lang/libc/pull/4482">#4482</a>)</li>
<li>FreeBSD: Add <code>xktls_session</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4482">#4482</a>)</li>
<li>Haiku: Add functionality from <code>libbsd</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4221">#4221</a>)</li>
<li>Linux: Add <code>SECBIT_*</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4480">#4480</a>)</li>
<li>NetBSD, OpenBSD: Export <code>ioctl</code> request generator macros
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4460">#4460</a>)</li>
<li>NetBSD: Add <code>ptsname_r</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4608">#4608</a>)</li>
<li>RISCV32: Add time-related syscalls (<a
href="https://redirect.github.com/rust-lang/libc/pull/4612">#4612</a>)</li>
<li>Solarish: Add <code>strftime*</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4453">#4453</a>)</li>
<li>linux: Add <code>EXEC_RESTRICT_*</code> and <code>EXEC_DENY_*</code>
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4545">#4545</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>AIX: Add <code>const</code> to signatures to be consistent with
other platforms (<a
href="https://redirect.github.com/rust-lang/libc/pull/4563">#4563</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>AIX: Fix the type of <code>struct statvfs.f_fsid</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4576">#4576</a>)</li>
<li>AIX: Fix the type of constants for the <code>ioctl</code>
<code>request</code> argument (<a
href="https://redirect.github.com/rust-lang/libc/pull/4582">#4582</a>)</li>
<li>AIX: Fix the types of <code>stat{,64}.st_*tim</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4597">#4597</a>)</li>
<li>AIX: Use unique <code>errno</code> values (<a
href="https://redirect.github.com/rust-lang/libc/pull/4507">#4507</a>)</li>
<li>Build: Fix an incorrect <code>target_os</code> -&gt;
<code>target_arch</code> check (<a
href="https://redirect.github.com/rust-lang/libc/pull/4550">#4550</a>)</li>
<li>FreeBSD: Fix the type of <code>xktls_session_onedir.ifnet</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4552">#4552</a>)</li>
<li>Mips64 musl: Fix the type of <code>nlink_t</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4509">#4509</a>)</li>
<li>Mips64 musl: Use a special MIPS definition of <code>stack_t</code>
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4528">#4528</a>)</li>
<li>Mips64: Fix <code>SI_TIMER</code>, <code>SI_MESGQ</code> and
<code>SI_ASYNCIO</code> definitions (<a
href="https://redirect.github.com/rust-lang/libc/pull/4529">#4529</a>)</li>
<li>Musl Mips64: Swap the order of <code>si_errno</code> and
<code>si_code</code> in <code>siginfo_t</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4530">#4530</a>)</li>
<li>Musl Mips64: Use a special MIPS definition of <code>statfs</code>
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4527">#4527</a>)</li>
<li>Musl: Fix the definition of <code>fanotify_event_metadata</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4510">#4510</a>)</li>
<li>NetBSD: Correct <code>enum fae_action</code> to be
<code>#[repr(C)]</code> (<a
href="60a8cfd564">#60a8cfd5</a>)</li>
<li>PSP: Correct <code>char</code> -&gt; <code>c_char</code> (<a
href="eaab4fc3f0">eaab4fc3</a>)</li>
<li>PowerPC musl: Fix <code>termios</code> definitions (<a
href="https://redirect.github.com/rust-lang/libc/pull/4518">#4518</a>)</li>
<li>PowerPC musl: Fix the definition of <code>EDEADLK</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4517">#4517</a>)</li>
<li>PowerPC musl: Fix the definition of <code>NCCS</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4513">#4513</a>)</li>
<li>PowerPC musl: Fix the definitions of <code>MAP_LOCKED</code> and
<code>MAP_NORESERVE</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4516">#4516</a>)</li>
<li>PowerPC64 musl: Fix the definition of <code>shmid_ds</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4519">#4519</a>)</li>
</ul>
<h3>Deprecated</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/libc/blob/0.2.175/CHANGELOG.md">libc's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/libc/compare/0.2.174...0.2.175">0.2.175</a>
- 2025-08-10</h2>
<h3>Added</h3>
<ul>
<li>AIX: Add <code>getpeereid</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4524">#4524</a>)</li>
<li>AIX: Add <code>struct ld_info</code> and friends (<a
href="https://redirect.github.com/rust-lang/libc/pull/4578">#4578</a>)</li>
<li>AIX: Retore <code>struct winsize</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4577">#4577</a>)</li>
<li>Android: Add UDP socket option constants (<a
href="https://redirect.github.com/rust-lang/libc/pull/4619">#4619</a>)</li>
<li>Android: Add <code>CLONE_CLEAR_SIGHAND</code> and
<code>CLONE_INTO_CGROUP</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4502">#4502</a>)</li>
<li>Android: Add more <code>prctl</code> constants (<a
href="https://redirect.github.com/rust-lang/libc/pull/4531">#4531</a>)</li>
<li>FreeBSD Add further TCP stack-related constants (<a
href="https://redirect.github.com/rust-lang/libc/pull/4196">#4196</a>)</li>
<li>FreeBSD x86-64: Add <code>mcontext_t.mc_tlsbase </code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4503">#4503</a>)</li>
<li>FreeBSD15: Add <code>kinfo_proc.ki_uerrmsg</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4552">#4552</a>)</li>
<li>FreeBSD: Add <code>in_conninfo</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4482">#4482</a>)</li>
<li>FreeBSD: Add <code>xinpgen</code> and related types (<a
href="https://redirect.github.com/rust-lang/libc/pull/4482">#4482</a>)</li>
<li>FreeBSD: Add <code>xktls_session</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4482">#4482</a>)</li>
<li>Haiku: Add functionality from <code>libbsd</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4221">#4221</a>)</li>
<li>Linux: Add <code>SECBIT_*</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4480">#4480</a>)</li>
<li>NetBSD, OpenBSD: Export <code>ioctl</code> request generator macros
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4460">#4460</a>)</li>
<li>NetBSD: Add <code>ptsname_r</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4608">#4608</a>)</li>
<li>RISCV32: Add time-related syscalls (<a
href="https://redirect.github.com/rust-lang/libc/pull/4612">#4612</a>)</li>
<li>Solarish: Add <code>strftime*</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4453">#4453</a>)</li>
<li>linux: Add <code>EXEC_RESTRICT_*</code> and <code>EXEC_DENY_*</code>
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4545">#4545</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>AIX: Add <code>const</code> to signatures to be consistent with
other platforms (<a
href="https://redirect.github.com/rust-lang/libc/pull/4563">#4563</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>AIX: Fix the type of <code>struct statvfs.f_fsid</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4576">#4576</a>)</li>
<li>AIX: Fix the type of constants for the <code>ioctl</code>
<code>request</code> argument (<a
href="https://redirect.github.com/rust-lang/libc/pull/4582">#4582</a>)</li>
<li>AIX: Fix the types of <code>stat{,64}.st_*tim</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4597">#4597</a>)</li>
<li>AIX: Use unique <code>errno</code> values (<a
href="https://redirect.github.com/rust-lang/libc/pull/4507">#4507</a>)</li>
<li>Build: Fix an incorrect <code>target_os</code> -&gt;
<code>target_arch</code> check (<a
href="https://redirect.github.com/rust-lang/libc/pull/4550">#4550</a>)</li>
<li>FreeBSD: Fix the type of <code>xktls_session_onedir.ifnet</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4552">#4552</a>)</li>
<li>Mips64 musl: Fix the type of <code>nlink_t</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4509">#4509</a>)</li>
<li>Mips64 musl: Use a special MIPS definition of <code>stack_t</code>
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4528">#4528</a>)</li>
<li>Mips64: Fix <code>SI_TIMER</code>, <code>SI_MESGQ</code> and
<code>SI_ASYNCIO</code> definitions (<a
href="https://redirect.github.com/rust-lang/libc/pull/4529">#4529</a>)</li>
<li>Musl Mips64: Swap the order of <code>si_errno</code> and
<code>si_code</code> in <code>siginfo_t</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4530">#4530</a>)</li>
<li>Musl Mips64: Use a special MIPS definition of <code>statfs</code>
(<a
href="https://redirect.github.com/rust-lang/libc/pull/4527">#4527</a>)</li>
<li>Musl: Fix the definition of <code>fanotify_event_metadata</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4510">#4510</a>)</li>
<li>NetBSD: Correct <code>enum fae_action</code> to be
<code>#[repr(C)]</code> (<a
href="60a8cfd564">#60a8cfd5</a>)</li>
<li>PSP: Correct <code>char</code> -&gt; <code>c_char</code> (<a
href="eaab4fc3f0">eaab4fc3</a>)</li>
<li>PowerPC musl: Fix <code>termios</code> definitions (<a
href="https://redirect.github.com/rust-lang/libc/pull/4518">#4518</a>)</li>
<li>PowerPC musl: Fix the definition of <code>EDEADLK</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4517">#4517</a>)</li>
<li>PowerPC musl: Fix the definition of <code>NCCS</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4513">#4513</a>)</li>
<li>PowerPC musl: Fix the definitions of <code>MAP_LOCKED</code> and
<code>MAP_NORESERVE</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4516">#4516</a>)</li>
<li>PowerPC64 musl: Fix the definition of <code>shmid_ds</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/4519">#4519</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="84e26e6b16"><code>84e26e6</code></a>
Update the lockfile</li>
<li><a
href="4d04aee906"><code>4d04aee</code></a>
chore: release libc 0.2.175</li>
<li><a
href="94a7f32972"><code>94a7f32</code></a>
cleanup: Format a file that was missed</li>
<li><a
href="172527344a"><code>1725273</code></a>
Rename the ctest file from <code>main</code> to <code>ctest</code></li>
<li><a
href="e9b021b7cd"><code>e9b021b</code></a>
freebsd adding further TCP stack related constants.</li>
<li><a
href="9606a2918b"><code>9606a29</code></a>
freebsd15: Add ki_uerrmsg to struct kinfo_proc</li>
<li><a
href="2816bc2f66"><code>2816bc2</code></a>
libc-test: include sys/ktls.h on freebsd</li>
<li><a
href="adfe283365"><code>adfe283</code></a>
libc-test: Account for xktls_session_onedir::gen (freebsd)</li>
<li><a
href="4cc1bf4331"><code>4cc1bf4</code></a>
freebsd: Document avoidance of reserved name <code>gen</code></li>
<li><a
href="7cdcaa6239"><code>7cdcaa6</code></a>
freebsd: Fix type of struct xktls_session_onedir, field ifnet</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/libc/compare/0.2.174...0.2.175">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=libc&package-manager=cargo&previous-version=0.2.174&new-version=0.2.175)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 03:14:29 +00:00
dependabot[bot]
b8f160c85b build(deps): bump webrender_api from ae2477d to 15318d6 (#38604)
Bumps [webrender_api](https://github.com/servo/webrender) from `ae2477d`
to `15318d6`.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="15318d6627"><code>15318d6</code></a>
Expose FastTransform so that it can be used in Servo</li>
<li>See full diff in <a
href="ae2477d9a6...15318d6627">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 02:45:07 +00:00
Euclid Ye
5d21234872 script: Focus on mousedown instead of mouse click according to spec (#38589)
- Focus on mousedown instead of mouse click according to
[spec](https://w3c.github.io/uievents/#handle-native-mouse-down)
- Refactor to follow spec closer and make things more clear.
- Add some spec link.
- Remove some dead spec link.

Still some preparation before implementing #38435.
Testing: No regression in WebDriver & WPT. But update some outdated
test.
Fixes: #38588

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-12 02:40:21 +00:00
dependabot[bot]
d2122c8bd8 build(deps): bump uuid from 1.17.0 to 1.18.0 (#38611)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.17.0 to 1.18.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/uuid-rs/uuid/releases">uuid's
releases</a>.</em></p>
<blockquote>
<h2>v1.18.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix up mismatched_lifetime_syntaxes lint by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/837">uuid-rs/uuid#837</a></li>
<li>Conversions between <code>Timestamp</code> and
<code>std::time::SystemTime</code> by <a
href="https://github.com/dcormier"><code>@​dcormier</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/835">uuid-rs/uuid#835</a></li>
<li>Wrap the error type used in time conversions by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/838">uuid-rs/uuid#838</a></li>
<li>Prepare for 1.18.0 release by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/839">uuid-rs/uuid#839</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/dcormier"><code>@​dcormier</code></a>
made their first contribution in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/835">uuid-rs/uuid#835</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/uuid-rs/uuid/compare/v1.17.0...v1.18.0">https://github.com/uuid-rs/uuid/compare/v1.17.0...v1.18.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="60a49eb94f"><code>60a49eb</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/839">#839</a> from
uuid-rs/cargo/v1.18.0</li>
<li><a
href="eb8c697083"><code>eb8c697</code></a>
prepare for 1.18.0 release</li>
<li><a
href="281f26fcd9"><code>281f26f</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/838">#838</a> from
uuid-rs/chore/time-conversion</li>
<li><a
href="2d67ab2b5e"><code>2d67ab2</code></a>
don't use allocated values in errors</li>
<li><a
href="c284ed562f"><code>c284ed5</code></a>
wrap the error type used in time conversions</li>
<li><a
href="87a4359f25"><code>87a4359</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/835">#835</a> from
dcormier/main</li>
<li><a
href="8927396625"><code>8927396</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/837">#837</a> from
uuid-rs/fix/lifetime-syntaxes</li>
<li><a
href="6dfb4b135c"><code>6dfb4b1</code></a>
Conversions between <code>Timestamp</code> and
<code>std::time::SystemTime</code></li>
<li><a
href="b508383aff"><code>b508383</code></a>
fix up mismatched_lifetime_syntaxes lint</li>
<li>See full diff in <a
href="https://github.com/uuid-rs/uuid/compare/v1.17.0...v1.18.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=uuid&package-manager=cargo&previous-version=1.17.0&new-version=1.18.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 02:06:23 +00:00
dependabot[bot]
faa50bcba4 build(deps): bump glob from 0.3.2 to 0.3.3 (#38610)
Bumps [glob](https://github.com/rust-lang/glob) from 0.3.2 to 0.3.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/glob/releases">glob's
releases</a>.</em></p>
<blockquote>
<h2>v0.3.3</h2>
<ul>
<li>Optimize memory allocations (<a
href="https://redirect.github.com/rust-lang/glob/pull/147">#147</a>)</li>
<li>Bump the MSRV to 1.63 (<a
href="https://redirect.github.com/rust-lang/glob/pull/172">#172</a>)</li>
<li>Fix spelling in pattern documentation (<a
href="https://redirect.github.com/rust-lang/glob/pull/164">#164</a>)</li>
<li>Fix version numbers and some formatting (<a
href="https://redirect.github.com/rust-lang/glob/pull/157">#157</a>)</li>
<li>Style fixes (<a
href="https://redirect.github.com/rust-lang/glob/pull/137">#137</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/glob/blob/master/CHANGELOG.md">glob's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/glob/compare/v0.3.2...v0.3.3">0.3.3</a>
- 2025-08-11</h2>
<ul>
<li>Optimize memory allocations (<a
href="https://redirect.github.com/rust-lang/glob/pull/147">#147</a>)</li>
<li>Bump the MSRV to 1.63 (<a
href="https://redirect.github.com/rust-lang/glob/pull/172">#172</a>)</li>
<li>Fix spelling in pattern documentation (<a
href="https://redirect.github.com/rust-lang/glob/pull/164">#164</a>)</li>
<li>Fix version numbers and some formatting (<a
href="https://redirect.github.com/rust-lang/glob/pull/157">#157</a>)</li>
<li>Style fixes (<a
href="https://redirect.github.com/rust-lang/glob/pull/137">#137</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="952da299a3"><code>952da29</code></a>
chore: release v0.3.3 (<a
href="https://redirect.github.com/rust-lang/glob/issues/155">#155</a>)</li>
<li><a
href="bfcd9a4760"><code>bfcd9a4</code></a>
Optimize memory allocations (<a
href="https://redirect.github.com/rust-lang/glob/issues/147">#147</a>)</li>
<li><a
href="e78862d913"><code>e78862d</code></a>
Bump the MSRV to 1.63 (<a
href="https://redirect.github.com/rust-lang/glob/issues/172">#172</a>)</li>
<li><a
href="97e5ee935a"><code>97e5ee9</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-lang/glob/issues/164">#164</a>
from jonboulle/patch-1</li>
<li><a
href="4da20e6a0c"><code>4da20e6</code></a>
Fix spelling in pattern documentation</li>
<li><a
href="1cf0f30d25"><code>1cf0f30</code></a>
Fix version numbers and some formatting</li>
<li><a
href="7a17f11fcd"><code>7a17f11</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-lang/glob/issues/153">#153</a>
from tgross35/clippy-ci</li>
<li><a
href="56619abe82"><code>56619ab</code></a>
Run clippy checks in CI</li>
<li><a
href="51363fa53c"><code>51363fa</code></a>
Disallow warnings in CI</li>
<li><a
href="1649a9a940"><code>1649a9a</code></a>
Apply remaining clippy suggestions</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/glob/compare/v0.3.2...v0.3.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=glob&package-manager=cargo&previous-version=0.3.2&new-version=0.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 01:44:18 +00:00
dependabot[bot]
ab66c087c0 build(deps): bump rustversion from 1.0.21 to 1.0.22 (#38609)
Bumps [rustversion](https://github.com/dtolnay/rustversion) from 1.0.21
to 1.0.22.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/rustversion/releases">rustversion's
releases</a>.</em></p>
<blockquote>
<h2>1.0.22</h2>
<ul>
<li>Turn off clippy incompatible_msrv in rustversion-conditional code
(<a
href="https://redirect.github.com/dtolnay/rustversion/issues/63">#63</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9e86f839b6"><code>9e86f83</code></a>
Release 1.0.22</li>
<li><a
href="a27fffdbb7"><code>a27fffd</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/rustversion/issues/64">#64</a>
from dtolnay/incompatiblemsrv</li>
<li><a
href="935837211f"><code>9358372</code></a>
Allow clippy incompatible_msrv</li>
<li><a
href="0379843c3c"><code>0379843</code></a>
Revert &quot;Pin nightly toolchain used for miri job&quot;</li>
<li><a
href="1491c256c8"><code>1491c25</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/rustversion/issues/62">#62</a>
from dtolnay/ci</li>
<li><a
href="49ad7c5242"><code>49ad7c5</code></a>
Add -Zfmt-debug=none test in CI</li>
<li>See full diff in <a
href="https://github.com/dtolnay/rustversion/compare/1.0.21...1.0.22">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rustversion&package-manager=cargo&previous-version=1.0.21&new-version=1.0.22)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 01:41:58 +00:00
dependabot[bot]
dd1da149d9 build(deps): bump webrender from ae2477d to 15318d6 (#38607)
Bumps [webrender](https://github.com/servo/webrender) from `ae2477d` to
`15318d6`.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="15318d6627"><code>15318d6</code></a>
Expose FastTransform so that it can be used in Servo</li>
<li>See full diff in <a
href="ae2477d9a6...15318d6627">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 00:00:42 +00:00
dependabot[bot]
6bd1564280 build(deps): bump wr_malloc_size_of from ae2477d to 15318d6 (#38606)
Bumps [wr_malloc_size_of](https://github.com/servo/webrender) from
`ae2477d` to `15318d6`.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="15318d6627"><code>15318d6</code></a>
Expose FastTransform so that it can be used in Servo</li>
<li>See full diff in <a
href="ae2477d9a6...15318d6627">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 23:50:51 +00:00
dependabot[bot]
340a2ecc39 build(deps): bump proc-macro2 from 1.0.95 to 1.0.96 (#38605)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.95
to 1.0.96.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/proc-macro2/releases">proc-macro2's
releases</a>.</em></p>
<blockquote>
<h2>1.0.96</h2>
<ul>
<li>Simplify how rustdoc flags are decided during docs.rs builds (<a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/511">#511</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="21f89e7dcf"><code>21f89e7</code></a>
Release 1.0.96</li>
<li><a
href="fe7d2faa1c"><code>fe7d2fa</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/proc-macro2/issues/511">#511</a>
from dtolnay/docsrs</li>
<li><a
href="7be6983e35"><code>7be6983</code></a>
Revert &quot;Work around doc build failure due to docs.rs flags
change&quot;</li>
<li><a
href="6f40165f9f"><code>6f40165</code></a>
Revert &quot;Pin nightly toolchain used for miri job&quot;</li>
<li><a
href="fc2ae0f869"><code>fc2ae0f</code></a>
Update ui test suite to nightly-2025-06-18</li>
<li><a
href="1ccc5a28e5"><code>1ccc5a2</code></a>
Ignore mismatched_lifetime_syntaxes lint</li>
<li><a
href="1091cc2218"><code>1091cc2</code></a>
Pin nightly toolchain used for miri job</li>
<li><a
href="0e82beadfb"><code>0e82bea</code></a>
Ignore ignore_without_reason pedantic clippy lint in test</li>
<li>See full diff in <a
href="https://github.com/dtolnay/proc-macro2/compare/1.0.95...1.0.96">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=proc-macro2&package-manager=cargo&previous-version=1.0.95&new-version=1.0.96)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 23:43:31 +00:00
sagudev
68ad03c40a canvas: trace/instrument canvas messages processing (#38600)
Add some tracing/instrumenting for canvas messages processing.

Testing: We have not tests for tracing code.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-11 18:21:49 +00:00
Narfinger
3f7f9ba6cd canvas: Use Transform2D<f64> instead of Transform2D<f32> (#38316)
I mostly did dumb replacement so please check that this is correct. In
addition, `canvas_data::draw_with_shadow` needs some a bit of a closer
look.

Testing: This is covered by existing tests, and it is likely that WPT
tests are unaffected by the precision of the transform, even though it
is technically more correct.
Fixes: #38256

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2025-08-11 16:46:25 +00:00
Oriol Brufau
4ff1e8dbd9 layout: Use FastTransform for hit testing (#38554)
`FastTransform` provides faster matrix operations when the involved
transforms are just translations.

Testing: Not needed (no change in behavior)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-08-11 16:23:17 +00:00
Narfinger
0d40547bec Update mozjs dependency. (#38596)
Dependency update for mozjs which includes the faster latin1 decoding.

Testing: Dependency updates have no tests.

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2025-08-11 13:58:23 +00:00
Tim van der Lippe
3976fa77bc Implement Trusted Types for ShadowRoot (#38595)
Also make TrustedHTML work the same as TrustedScript by
only taking 1 `&str` to make things easier.

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-08-11 13:39:55 +00:00
Simon Wülker
abc549eff7 Reduce the size of some unsafe blocks in shaper.rs (#38597)
This change ensures that safe operations are done outside of unsafe
blocks as often as possible.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-11 13:39:07 +00:00
Martin Robinson
b75c3feb97 script/compositor: Send mouseleave events when cursor moves between <iframe>s (#38539)
Properly send `mouseleave` events when the cursor moves between
`<iframe>`s. This allows a better handling of cursor changes and status
text updates. Specifically, we do not need to continuously update the
cursor and the value can be cached in the `Document`. In addition,
status updates can now be sent properly when moving focus between
`<iframe>`s.

Note that style updates for `:hover` values are still broken, but less
so than before. Now the hover state on the `Node` is updated, but for
some
reason the restyle isn't taking place properly. This maintains the
status quo as far as behavior goes when hover moves between `<iframe>`s.

This change also adds a helper data structure to `Document` which will
eventually be responsible for event handling.

Testing: Cursor and status change are currently very hard to test as
the API test harness makes this difficult at the moment.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-11 12:31:54 +00:00
Tim van der Lippe
82ca2b92cd Implement Trusted Type eval checks (#37834)
It implements the new codeForEvalGets callback to retrieve the
value for a trusted script object. Additionally, it implements
the new logic in can-compile-strings to call the policy
factory if required.

Note that parameter and argument checks aren't implemented yet,
as they require updates to binding generation (see TODO in
script_runtime).

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-08-11 12:26:56 +00:00
Mukilan Thiyagarajan
4c05758ded script: support inline SVG by serializing the subtree (#38188)
This patch adds support for rendering static inline SVG documents in the
DOM tree by serializing the SVGElement's subtree and leveraging the
existing resvg based SVG stack for rendering. Serialiing the subtree is
necessary as resvg's tree representation (roxmltree) is immutable, so we
can't construct the tree incrementally.

Few other design choices here:
1. The `SVGSVGElement` is now treated as a replaced element and the
   layout code is responsible for plumbing the serialized SVG source
   (encoded as a base64 data: url) into the image cache, much like how
   background images are handled.
2. The serialization is done on the script thread after an initial
   layout pass. This is necessary because the serialization code asserts
that it is invoked from script thread i.e we can't call it from layout
   workers.
3. The serialized SVG data: url is cached to avoid recomputing it on
   subsequent layouts. The cache is invalidated when the SVGSVGElement's
   subtree is mutated.

The original SVGSVGElement code was behind the `dom_svg_enabled` pref.
This patch also removes the preference and make SVG support using resvg
available unconditionally.

Below is the analysis of the new test failures:

These tests use inline SVG but used to pass by accident.
They now fail because they contain SVG with no intrinsic
sizing which is not handled by resvg in a way that would
allows us to distinguish it from the sized case. The same
limitation applies to non-inline SVG.

 - /css/CSS2/positioning/absolute-replaced-width-003a.xht
 - /css/CSS2/positioning/absolute-replaced-width-003b.xht
 - /css/CSS2/positioning/absolute-replaced-width-003c.xht

These tests employ CSS styles in the HTML that
target the elements in inline SVG, which is not currently
supported.

-
/css/compositing/mix-blend-mode/mix-blend-mode-plus-lighter-svg-basic.html
 - /css/compositing/mix-blend-mode/mix-blend-mode-plus-lighter-svg.html

This is a tentative test that uses the unsupported 'border-shape' CSS
property. The ref uses SVG, so it used to pass accidentally. The ref
still doesn't render correctly since it also relies on styling SVG
elements using CSS classes in the HTML (instead of inline in SVG).

 - /css/css-borders/tentative/border-shape/border-shape-stroke.html

These tests use the attribute 'clip-path=circle(...)' in the
test, but this doesn't seem to work in resvg.

 - /css/css-masking/clip-path/clip-path-borderBox-1b.html
 - /css/css-masking/clip-path/clip-path-contentBox-1b.html
 - /css/css-masking/clip-path/clip-path-contentBox-1c.html
 - /css/css-masking/clip-path/clip-path-fillBox-1b.html
 - /css/css-masking/clip-path/clip-path-marginBox-1a.html
 - /css/css-masking/clip-path/clip-path-paddingBox-1b.html
 - /css/css-masking/clip-path/clip-path-strokeBox-1b.html
 - /css/css-masking/clip-path/clip-path-strokeBox-1c.html
 - /css/css-masking/clip-path/clip-path-viewBox-1a.html
 - /css/css-masking/clip-path/clip-path-viewBox-1b.html
 - /css/css-masking/clip-path/clip-path-viewBox-1d.html
 - /css/css-masking/clip-path/svg-clip-path-circle-offset.html
 - /css/css-masking/clip-path/svg-clip-path-ellipse-offset.html

Additionally, the below two tests use a `foreignObject` SVG element
which
embeds a html div fragment. This is also not supported by resvg.

 - /css/css-masking/clip-path/clip-path-viewBox-1d.html
 - /css/css-masking/clip-path/clip-path-fillBox-1b.html

The following test fails because of apparent pixel differences
between a circle rendered purely using CSS clip-path vs a circle
rendered in SVG using resvg.

 - /css/css-masking/clip-path/clip-path-contentBox-1c.html

These tests style the inline SVG elements using CSS in the HTML or
separate stylesheet. This is not supported by this implementation.

 - /css-transforms/document-styles/svg-document-styles-{001..004}.html
 - /css-transforms/document-styles/svg-document-styles-012.html
 - /css-transforms/external-styles/svg-external-styles-{001..004}.html
 - /css-transforms/external-styles/svg-external-styles-014.html

These tests seem like they should pass, but they fail because of what
seems like an anti-aliasing issue in the rendering engine. The
transformed element has a thin outline which is causing pixel difference
with the ref:

 - /css/css-transforms/group/svg-transform-group-008.html
 - /css/css-transforms/group/svg-transform-group-009.html
 - /css/css-transforms/group/svg-transform-nested-009.html
 - /css/css-transforms/group/svg-transform-nested-013.html
 - /css/css-transforms/group/svg-transform-nested-014.html
 - /css/css-transforms/group/svg-transform-nested-018.html
 - /css/css-transforms/group/svg-transform-nested-019.html
 - /css/css-transforms/group/svg-transform-nested-008.html

The below tests fail because resvg is calculating the wrong size for the
'rect' inside the SVG. The dimensions of the SVG are established via the
CSS in the HTML, so it seems resvg is using incorrect coordinates for
the children of the svg when explict width/height are not specified in
the root svg element.

 - /css/css-transforms/group/svg-transform-group-011.html
 - /css/css-transforms/group/svg-transform-nested-021.html
 - /css/css-transforms/group/svg-transform-nested-029.html

All these tests use an SVG that doesn't have width nor height attributes
and this causes resvg to use incorrect coordinates for the SVG's
children. In addition, the following tests use the CSS syntax for
transforms inside the SVG (using style attribute) which is not supported
by resvg (it only supports the SVG 1.1 transform syntax).

 - /css/css-transforms/inline-styles/svg-inline-styles-{001..004}.html
 - /css/css-transforms/inline-styles/svg-inline-styles-012.html

In the case of these four tests, the `style` attribute specifies an
invalid transform, but resvg doesn't fallback to the transform specified
via the `transform`  attribute on the same element.

 - /css/css-transforms/inline-styles/svg-inline-styles-005.html
 - /css/css-transforms/inline-styles/svg-inline-styles-006.html
 - /css/css-transforms/inline-styles/svg-inline-styles-010.html
 - /css/css-transforms/inline-styles/svg-inline-styles-013.html

The following test fails because of the lack of width/height in SVG as
described above but it also exposes gaps in our CSS tranform
implementation.

 - /css/css-transforms/preserve3d-and-filter-with-perspective.html

These tests failure because resvg doesn't handle the SVG without
explicit width and height, but specified via CSS in the HTML. In
addition, there are pixel differences between the ref due to
antialiasing issues.

 - /css/css-transforms/matrix/svg-matrix-{005...008}.html
 - /css/css-transforms/matrix/svg-matrix-010.html
 - /css/css-transforms/matrix/svg-matrix-012.html
 - /css/css-transforms/matrix/svg-matrix-{015..069}.html
 - /css/css-transforms/rotate/svg-rotate-angle-45-001.html
 - /css/css-transforms/rotate/svg-rotate-angle-45-011.html
 - /css/css-transforms/rotate/svg-rotate-angle-45-022.html
 - /css/css-transforms/scale/svg-scale-006.html
 - /css/css-transforms/scale/svg-scale-007.html

These tests seem to be failing due to some sort of antialiasing issue,
where a transformed SVG element has a thin border that causes pixel
differences compared to the solid colored reference.

 - /css/css-transforms/skewX/svg-skewx-001.html
 - /css/css-transforms/skewX/svg-skewx-006.html
 - /css/css-transforms/skewX/svg-skewx-011.html
 - /css/css-transforms/skewX/svg-skewx-016.html
 - /css/css-transforms/skewX/svg-skewx-021.html
 - /css/css-transforms/skewX/svg-skewxy-001.html
 - /css/css-transforms/skewY/svg-skewy-001.html
 - /css/css-transforms/skewY/svg-skewy-006.html
 - /css/css-transforms/skewY/svg-skewy-011.html
 - /css/css-transforms/skewY/svg-skewy-016.html
 - /css/css-transforms/skewY/svg-skewy-021.html

These tests specify several SVG attributes such as transform,
vector-effect etc via CSS in the HTML (rather than inline in SVG). The
current implementation doesn't support this.

 - /css/css-transforms/transform-box/stroke-box-mutation-001.html
 - /css/css-transforms/transform-box/stroke-box-mutation-002.html
 - /css/css-transforms/transform-box/stroke-box-mutation-003.html
 - /css/css-transforms/transform-box/stroke-box-mutation-004.html
 - /css/css-transforms/transform-box/svgbox-stroke-box-002.html
 - /css/css-transforms/transform-box/svgbox-stroke-box-003.html
 - /css/css-transforms/transform-box/svgbox-stroke-box-004.html
 - /css/css-transforms/transform-box/svgbox-stroke-box-005.html

These tests depend on 'transform-origin' specified on an element inside
an SVG, but this transform is influenced by the 'tranform-box' set via
CSS in the HTML itself (not the SVG). The current implementation doesn't
support styling the SVG using document styles, so these tests just fail.

- /css/css-transforms/transform-origin/svg-origin-relative-length-*.html

These tests check the fallback behaviour when invalid syntax is
encountered in the 'transform-origin' value. resvg doesn't correctly
fallback to 0,0 causing the tests to fail.

-
/css/css-transforms/transform-origin/svg-origin-relative-length-invalid-001.html
-
/css/css-transforms/transform-origin/svg-origin-relative-length-invalid-002.html
-
/css/css-transforms/transform-origin/svg-origin-relative-length-invalid-003.html
-
/css/css-transforms/transform-origin/svg-origin-relative-length-invalid-004.html

These tests use unimplemented Canvas APIs like 'beginLayer' and
the 'CanvasFilter' constructor and hence fail at runtime.

-
/html/canvas/element/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.html
-
/html/canvas/element/filters/2d.filter.layers.gaussianBlur.tentative.html
-
/html/canvas/element/layers/2d.layer.anisotropic-blur.isotropic.tentative.html
-
/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-x.tentative.html
-
/html/canvas/element/layers/2d.layer.anisotropic-blur.mostly-y.tentative.html
-
/html/canvas/element/layers/2d.layer.anisotropic-blur.x-only.tentative.html
-
/html/canvas/element/layers/2d.layer.anisotropic-blur.y-only.tentative.html
-
/html/canvas/element/layers/2d.layer.css-filters.blur-and-shadow.tentative.html
 - /html/canvas/element/layers/2d.layer.css-filters.blur.tentative.html
- /html/canvas/element/layers/2d.layer.css-filters.shadow.tentative.html
 - /html/canvas/element/layers/2d.layer.ctm.layer-filter.tentative.html
-
/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.tentative.html
-
/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.gaussianBlur.tentative.html
-
/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.tentative.html
-
/html/canvas/offscreen/filters/2d.filter.layers.gaussianBlur.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.isotropic.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-x.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.mostly-y.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.x-only.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.anisotropic-blur.y-only.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.css-filters.blur-and-shadow.tentative.html
- /html/canvas/offscreen/layers/2d.layer.css-filters.blur.tentative.html
-
/html/canvas/offscreen/layers/2d.layer.css-filters.shadow.tentative.html
- /html/canvas/offscreen/layers/2d.layer.ctm.layer-filter.tentative.html

These tests fail because resvg doesn't seem to honour the 'translate'
CSS property specified on an SVG element using an inline 'style'
attribute.

 - /css/css-transforms/translate/svg-translate-with-units.html
-
/css/css-transforms/translate/translate-and-transform-attribute-in-svg.html
-
/css/css-transforms/translate/translate-and-transform-css-property-in-svg.html
 - /css/css-transforms/translate/translate-in-svg.html

These tests seem to fail due to the filter effect implementation in
resvg either not being complete or spec compliant.

 - /css/filter-effects/feconvolve-divisor.html
 - /css/filter-effects/feconvolve-region-001.html
 - /css/filter-effects/feconvolve-region-002.html
 - /css/filter-effects/filter-subregion-01.html
 - /css/filter-effects/svg-feimage-002.html
 - /css/filter-effects/svg-feimage-003.html
 - /css/filter-effects/svg-feimage-004.html
 - /css/filter-effects/svg-feoffset-001.html

The test /css/filter-effects/svg-feimage-004.html should ideally PASS
but currently fails because we don't propagate height/width set using
CSS in HTML element to the root SVG, so resvg uses the wrong dimensions
when rendering the children of the SVG.

These failures are due to deficienies in our current implementation
i.e we don't support styling SVG elements using CSS in HTML.

-
/css/css-transforms/gradientTransform/svg-gradientTransform-combination-001.html
 - /css/selectors/sharing-in-svg-use.html

The below test fails as our current implementation relies on resvg to
tell us the intrinsic ratio of the SVG, but this doesn't always work
correctly.

 - /css/css-sizing/svg-intrinsic-size-005.html

This failure is due to lack of proper fallback to no-op transform in
resvg when the `rotate()` syntax is specified with an invalid list e.g
`rotate(90,)`.

 - /css/css-transforms/rotate/svg-rotate-3args-invalid-002.html

This test only passes in CI and based on the raw log output, it seems
that no text inside the SVG is rendered in the CI. This could be an font
stack related issue.

 - /css/css-display/display-contents-svg-elements.html

This test asserts that the CSP blocks loads triggered using `use`
elements in SVG. It used to TIMEOUT as without inline SVG support, no
CSP violation event was triggered. It fails now since the event is now
triggered for the load of the SVG itself (our current implementation
loads inline SVGs as serialized base64 data: urls). This doesn't match
the blocked URL in the use element though.

 - /content-security-policy/img-src/svg-use-blocked.tentative.html

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
2025-08-11 11:07:59 +00:00
Martin Robinson
be7625fc1e tidy: Replace custom panic/unwrap lint with clippy lint (#38593)
This change replaces our custom `panic` / `unwrap` lint with the one
from clippy. This rule as not properly applied in servoshell, so this
change fixes some clippy errors raised by the new configuration.

Testing: This change removes the tidy tests for the custom lints, but
otherwise the behavior is tested as part of clippy itself.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-11 11:04:11 +00:00
Martin Robinson
005164df4a script: Move point_in_initial_containing_block calculation to script (#38520)
Instead of calculating this value in the compositor, calculate it in
`ScriptThread` now that it is straightforward to get this value from the
layout spatial tree. This allows removing some tricky callback code in
the Compositor.

Testing: This shouldn't change any observable behavior so is covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-11 09:32:38 +00:00
Martin Robinson
9d4004135b script: Properly fire input events for clipboard use in input elements (#37100)
When using the clipboard to paste or modify the contents of an input
element the specification says says[^1] to

> Queue tasks to fire any events that should fire due to the
> modification, see § 5.3 Integration with other scripts and events for
> details.

This change does that, by turning `handle_text_clipboard_action` into
`TextInput::handle_clipboard_event` and having the caller responsible
for executing events. In addition, when content is changed, the node is
dirtied, forcing a relayout.

[^1]: https://www.w3.org/TR/clipboard-apis/#paste-action

Testing: This is difficult to test because we do not have test harness
support for input events currently. There is a manual test for this in
the linked bug which is now passing.
Fixes: #37074.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-11 09:23:58 +00:00
Adam
6ddcce4889 Replace Rc<RefCell<Option<RootedTraceableBox<Heap<JSVal>>>>> with Rc<RefCell<Option<Heap<JSVal>>>> in PipeTo::shutdown_error (#38562)
#38024 adds a rooted box around the Heap for safety, but this is not
necessary anymore after #38385. The solution is to replace
`Rc<RefCell<Option<RootedTraceableBox<Heap<JSVal>>>>>` with
`Rc<RefCell<Option<Heap<JSVal>>>>`. This changes replaces this type.

Signed-off-by: amotaouakkil123 <adam.motaouakkil@mail.mcgill.ca>
2025-08-11 08:58:54 +00:00
Jerens Lensun
3ab56b16de mach(test-tidy): Remove alphabetical order and line length rule from tidy (#38538)
As we plan to adopt more rules from Ruff and rustfmt, we would like to
retire the following rules:

1. `Line length check`, as this is already handled by Ruff and rustfmt
configurations.
2. `Alphabetical order`


Testing: `./mach test-tidy --no-progress --all`
Fixes: #37121

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-11 08:54:50 +00:00
shuppy
4784668fa9 devtools: Create source actors from Debugger API notifications (#38334)
currently our devtools impl creates source actors in script, when
executing scripts in HTMLScriptElement or DedicatedWorkerGlobalScope.
this approach is cumbersome, and it means that many pathways to running
scripts are missed, such as imported ES modules.

with the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), we can pick
up all of the scripts and all of their sources without any extra code,
as long as we tell it about every global we create (#38333, #38551).
this patch adds a [Debugger#onNewScript()
hook](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewscript-script-global)
to the debugger script, which calls
DebuggerGlobalScope#notifyNewSource() to notify our script system when a
new script runs. if the source is relevant to the file tree in the
Sources tab, script tells devtools to create a source actor.

Testing: adds several new automated devtools tests
Fixes: part of #36027

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-11 06:04:51 +00:00
Kenzie Raditya Tirtarahardja
de73d4a25c script(webdriver): Check if element is disabled based on WebDriver specification (#38490)
Checking if an element is disabled based on
[spec](https://w3c.github.io/webdriver/#dfn-disabled). Fix the command
`Is Element Enabled`.

Testing: Covered in WPT
(`./tests/wpt/tests/webdriver/tests/classic/is_element_enabled/enabled.py`)

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-11 04:41:04 +00:00
Tim van der Lippe
2e5f5e7d1c Update Nom to 8.0.0 (#38585)
There are two relevant breaking changes:
* instead of `)(input)`, we now need to call `).parse(input)`
* tuples are no longer a call, but a `()` call.

There is one other usage of nom 7, however that's in the build
dependencies list of mozangle via bindgen. Therefore, we won't have
duplicate nom versions in Servo binary.

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-08-10 21:25:37 +00:00
Rodion Borovyk
4f8731d562 script: Return a Result from GlobalScope::evaluate_script_on_global_with_result (#38549)
Make GlobalScope::evaluate_script_on_global_with_result return a Result
instead of a boolean. This is the first step to resolve issue #37810.

Testing: Should not break or fix any existing tests

---------

Signed-off-by: Rodion Borovyk <rodion.borovyk@gmail.com>
2025-08-10 16:51:46 +00:00
Servo WPT Sync
86c37a380b Sync WPT with upstream (10-08-2025) (#38583)
Automated downstream sync of changes from upstream as of 10-08-2025
[no-wpt-sync]

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
2025-08-10 11:09:48 +00:00
sagudev
2f0afb0ec0 python: Update pyOpenSSL to 24 (#38582)
WPT updated something and we need to update pyOpenSSL to 24 to make deps
resolvable.
This fixes WPT Imports:
https://github.com/servo/servo/actions/runs/16855379913/job/47753105229

Testing: WPT import run:
https://github.com/servo/servo/actions/runs/16859665646

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-10 10:31:01 +00:00
Simon Wülker
a60b53eded script: Convert CSS from a IDL interface with static methods to a namespace (#38579)
This change updates our implementation to match the spec. Per
https://drafts.csswg.org/cssom/#issue-24739c22 this is observable.

Testing: Covered by existing web platform tests.

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-10 03:35:25 +00:00
Oriol Brufau
7ff8724eaf layout: Move sizing logic from geom.rs to sizing.rs (#38568)
Moves `Size`, `SizeConstraint`, `Sizes` and `LazySizeData`.

Testing: Not needed, no change in behavior.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-09 20:43:40 +00:00
Martin Robinson
d5d3ad6949 script: Replace allow(unused_imports) with conditional use statements (#38576)
A few places in the code are using a blanket `allow(unused_imports)`
compiler directive to silence errors about unused imports when the
`webxr` feature is disabled. Since this can hide other kinds of unused
imports, replace these directives with conditional `use` statements.

Testing: No behavior change, so existing tests suffice.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-09 20:19:12 +00:00
Simon Wülker
4d7a0d3863 script: Tell devtools whether a node is displayed or not (#38575)
Doing so makes the devtools inspector display the nodes in gray, as it
is the case in firefox.
The relevant node parameter already exists but is hardcoded.

Before:
<img width="1108" height="408" alt="image"
src="https://github.com/user-attachments/assets/4a442fc9-92db-4a97-9e70-3b02f994a9d1"
/>


After:
<img width="1169" height="404" alt="image"
src="https://github.com/user-attachments/assets/ec1674a4-c025-4ceb-93c8-0cc3f695ddc7"
/>


Testing: We don't have tests for the devtools inspector.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-08-09 18:12:20 +00:00
shuppy
c4044e17bb script: Rename DebuggerEvent to DebuggerAddDebuggeeEvent (#38571)
the next debugger script event, `getPossibleBreakpoints` (#37667), will
contain a single attribute `unsigned long spidermonkeyId`, so it will
have nothing in common with `addDebuggee`. this patch renames the latter
accordingly.

Testing: no behaviour changes other than the rename, so no tests needed
Fixes: part of #36027

Signed-off-by: Delan Azabani <dazabani@igalia.com>
2025-08-09 18:01:00 +00:00
Oriol Brufau
c49d53b8dd layout: Paint flex and grid items like inline blocks (#38574)
As specified in https://drafts.csswg.org/css-flexbox-1/#painting and
https://drafts.csswg.org/css-grid/#z-order

Testing: Makes some WPT pass.
Fixes: #38573

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-09 17:42:12 +00:00
Labros
7b057be780 script: Replace usage of IntersectionObserverRootMargin with Stylo'sIntersectionObserverMargin (#38519)
@stevennovaryo 
Created wrapper for Stylo's IntersectionObserverMargin and cleaned up
repeated code.
Testing: Code compiles and `./mach test-unit tests/unit/style/` doesn't
have any errors. intersectionobserver.rs is able to utilize the struct.
Fixes: https://github.com/servo/servo/issues/35907

---------------------------
Signed-off-by: samir <samir.khan720a@gmail.com>

---------

Signed-off-by: samir <samir.khan720a@gmail.com>
2025-08-09 17:31:44 +00:00
sagudev
0b2c0cd055 chore: Update vello(_cpu) (#38569)
I need this to properly evaluate multi threading in vello_cpu.

Testing: Existing WPT tests, but it's just dep bump.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-09 17:00:30 +00:00
minghuaw
ad18638534 script_bindings: Remove jsstring_to_str (#38527)
This PR removes `jsstring_to_str`, which is replaced with
`jsstr_to_string`, and updates `mozjs` to
6f3dcb99a7.

Given that servo now always replaces unpaired surrogate since
https://github.com/servo/servo/pull/35381, the internal conversion
function `jsstring_to_str` is functionally the same as `jsstr_to_string`
from `mozjs`. This PR removes `jsstring_to_str` and replaces with
`jsstr_to_string` with conversions to `DOMString` where necessary.

Testing: Passes all unit test. No regression was found in WPT test (see
try run: https://github.com/minghuaw/servo/actions/runs/16821156583)

---------

Signed-off-by: minghuaw <wuminghua7@huawei.com>
Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Co-authored-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-09 11:50:14 +00:00
shuppy
a3e0a34802 script: Add new worker globals as debuggees (#38551)
to debug workers in a page with the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), we need to
pass the worker’s global object to
[Debugger.prototype.**addDebuggee()**](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#adddebuggee-global).
we could pick up the global via the [onNewGlobalObject()
hook](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewglobalobject-global),
but if our script system passes the global to the debugger script
instead, we can later add details like the PipelineId that will help
servo identify debuggees that the API is notifying us about (#38334).

this patch creates a debugger global in worker threads, runs the
debugger script in those new globals, and plumbs new worker globals from
those threads into addDebuggee() via the debugger script. since worker
threads can’t generate PipelineId values, but they only ever run workers
on behalf of one pipeline, we use that pipeline’s PipelineId as the
PipelineId of the debugger global, rather than generating a unique
PipelineId like we do in script threads.

Testing: will undergo many automated tests in #38334
Fixes: part of #36027

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-09 11:28:06 +00:00
Euclid Ye
d50f02fa73 script: Cleanup unused import in htmlscriptelement.rs (#38567)
Testing: No behaviour change.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-09 08:12:48 +00:00
Euclid Ye
589d188a3f script: Change signature of Event::dispatch to match the spec and simplify things (#38566)
- [Dispatch Event](https://dom.spec.whatwg.org/#concept-event-dispatch)
should return a Boolean. This function is used frequently in spec and
the change makes things easier to follow.
- Remove `enum EventStatus` and related functions.
- Update some dead spec link.
- Update some steps.

This is intended as cleanup before working on #38435 and reduces binary
size by 488KB in Release profile.

Testing: No behaviour change.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-09 08:04:31 +00:00
Mukilan Thiyagarajan
21717158eb script: mark innerHTML as fallible in ShadowRoot (#38565)
This is a follow-up to #38532 which simply defaulted to an empty string
as the spec's WebIDL doesn't mark `innerHTML` as `Throws`. However,
since the absence of `Throws` in the spec doesn't imply no-throw, we can
mark this as fallible in our WebIDL. This makes the `ShadowRoot`'s
implementation match `Element`'s `innerHTML`.

Testing: Same as #38532.

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
2025-08-09 06:45:52 +00:00
arthmis
86d7f4c793 add implementation for Path2D addPath method (#37838)
Add implementation for Path2D addPath method
Spec:
https://html.spec.whatwg.org/multipage/canvas.html#dom-path2d-addpath

Testing: WPT test -
`tests/wpt/tests/css/geometry/DOMMatrix2DInit-validate-fixup.html`
Fixes: #37695

---------

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>
Signed-off-by: arthmis <artmis9@protonmail.com>
2025-08-09 05:06:20 +00:00
shuppy
6471587fb4 script: Set correct introductionType values in more places (#38550)
to use the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/) as the single
source of truth about scripts and their sources for devtools purposes
(servo/servo#38334), we need to keep track of whether scripts come from
an actual file or from things like setTimeout(), because for some
[introductionType](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#introductiontype)
[values](https://firefox-source-docs.mozilla.org/devtools-user/debugger-api/debugger.source/#accessor-properties-of-the-debugger-source-prototype-object),
we want to disregard the script unless it has a [`//# sourceURL=`
override](https://tc39.es/ecma426/#sec-linking-eval)
([displayURL](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#displayurl)).

this patch builds on #38363, setting the correct introductionType value
in several more cases.

Testing: will undergo many automated tests in #38334
Fixes: part of #36027

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-09 05:05:54 +00:00
Jonathan Schwender
ff4971012f bump mozjs to 137.0-1 (#38561)
This bump downgrades icu_capi to 1.5.0, to match the version vendored in
spidermonkey.

Previous mozjs PRs:
- https://github.com/servo/mozjs/pull/606
- https://github.com/servo/mozjs/pull/605


Testing: Covered by existing tests

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-08 20:19:45 +00:00
Kenzie Raditya Tirtarahardja
d33ae1549d script(webdriver): Fix element clear for file (#38536)
When clearing input file with WebDriver, we should set input's filelist
with empty list instead of setting it with None.

Testing:
`./tests/wpt/tests/webdriver/tests/classic/element_send_keys/file_upload.py`

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-08 18:38:35 +00:00
dependabot[bot]
29222c2770 build(deps): bump slab from 0.4.10 to 0.4.11 (#38559)
Bumps [slab](https://github.com/tokio-rs/slab) from 0.4.10 to 0.4.11.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/slab/releases">slab's
releases</a>.</em></p>
<blockquote>
<h2>v0.4.11</h2>
<ul>
<li>Fix <code>Slab::get_disjoint_mut</code> out of bounds (<a
href="https://redirect.github.com/tokio-rs/slab/issues/152">#152</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md">slab's
changelog</a>.</em></p>
<blockquote>
<h1>0.4.11 (August 8, 2025)</h1>
<ul>
<li>Fix <code>Slab::get_disjoint_mut</code> out of bounds (<a
href="https://redirect.github.com/tokio-rs/slab/issues/152">#152</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2e5779f8eb"><code>2e5779f</code></a>
Release v0.4.11 (<a
href="https://redirect.github.com/tokio-rs/slab/issues/153">#153</a>)</li>
<li><a
href="2d65c514bc"><code>2d65c51</code></a>
Fix get_disjoint_mut error condition (<a
href="https://redirect.github.com/tokio-rs/slab/issues/152">#152</a>)</li>
<li>See full diff in <a
href="https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.11">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=slab&package-manager=cargo&previous-version=0.4.10&new-version=0.4.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 17:20:16 +00:00
dependabot[bot]
4eafb9837b build(deps): bump cfg-expr from 0.20.1 to 0.20.2 (#38560)
Bumps [cfg-expr](https://github.com/EmbarkStudios/cfg-expr) from 0.20.1
to 0.20.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/EmbarkStudios/cfg-expr/releases">cfg-expr's
releases</a>.</em></p>
<blockquote>
<h2>0.20.2</h2>
<h3>Changed</h3>
<ul>
<li><a
href="https://redirect.github.com/EmbarkStudios/cfg-expr/pull/81">PR#81</a>
updated the builtin target list to 1.89.0.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/EmbarkStudios/cfg-expr/blob/main/CHANGELOG.md">cfg-expr's
changelog</a>.</em></p>
<blockquote>
<h2>[0.20.2] - 2025-08-07</h2>
<h3>Changed</h3>
<ul>
<li><a
href="https://redirect.github.com/EmbarkStudios/cfg-expr/pull/81">PR#81</a>
updated the builtin target list to 1.89.0.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6be48f7e40"><code>6be48f7</code></a>
Release 0.20.2</li>
<li><a
href="319b059f17"><code>319b059</code></a>
Update to 1.89 (<a
href="https://redirect.github.com/EmbarkStudios/cfg-expr/issues/81">#81</a>)</li>
<li>See full diff in <a
href="https://github.com/EmbarkStudios/cfg-expr/compare/0.20.1...0.20.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cfg-expr&package-manager=cargo&previous-version=0.20.1&new-version=0.20.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 17:19:28 +00:00
Martin Robinson
931025c16e script: Wrap unsafe code in components/script/bindings in unsafe {} (#38544)
Clippy now checks to see if unsafe code is wrapped in unsafe blocks. We
have this lint disabled for `script` and `script_bindings` because of a
lot of legacy code that doesn't do this. The lint is useful though as it
makes it more obvious what code is unsafe. This is an incremental step
toward being able to turn this lint on for `script`.

This has the benefit of silencing warnings that show up in some IDEs
that use rust-analyzer.

Testing: This should not change behavior at all and is thus covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-08 17:18:30 +00:00
dependabot[bot]
fef104cff7 build(deps): bump cc from 1.2.31 to 1.2.32 (#38558)
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.2.31 to 1.2.32.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md">cc's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.31...cc-v1.2.32">1.2.32</a>
- 2025-08-08</h2>
<h3>Fixed</h3>
<ul>
<li>fix new clippy lint introduced in rust 1.89.0 (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1509">#1509</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>clarify cargo default if no rerun emitted (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1508">#1508</a>)</li>
<li>extract compile_objects_sequential (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1507">#1507</a>)</li>
<li>Windows <code>find_tools</code>: add support for finding Clang (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1506">#1506</a>)</li>
<li>Add m68k-unknown-linux-gnu cross-compile target (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1505">#1505</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="94a7cc349e"><code>94a7cc3</code></a>
chore: release v1.2.32 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1510">#1510</a>)</li>
<li><a
href="33b616dcf7"><code>33b616d</code></a>
doc: clarify cargo default if no rerun emitted (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1508">#1508</a>)</li>
<li><a
href="3046b0af8d"><code>3046b0a</code></a>
fix new clippy lint introduced in rust 1.89.0 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1509">#1509</a>)</li>
<li><a
href="b8959a21ad"><code>b8959a2</code></a>
Refactor: extract compile_objects_sequential (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1507">#1507</a>)</li>
<li><a
href="3f62db136f"><code>3f62db1</code></a>
Windows <code>find_tools</code>: add support for finding Clang (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1506">#1506</a>)</li>
<li><a
href="5e210bd8fb"><code>5e210bd</code></a>
Add m68k-unknown-linux-gnu cross-compile target (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1505">#1505</a>)</li>
<li>See full diff in <a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.31...cc-v1.2.32">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cc&package-manager=cargo&previous-version=1.2.31&new-version=1.2.32)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 17:14:49 +00:00
dependabot[bot]
405cc2d073 build(deps): bump bytemuck from 1.23.1 to 1.23.2 (#38557)
Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.23.1 to
1.23.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Lokathor/bytemuck/blob/main/changelog.md">bytemuck's
changelog</a>.</em></p>
<blockquote>
<h2>1.23.2</h2>
<ul>
<li>bump <code>derive</code> minimum version.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c069a2f6a4"><code>c069a2f</code></a>
chore: Release bytemuck version 1.23.2</li>
<li><a
href="8b48ef35bf"><code>8b48ef3</code></a>
.</li>
<li><a
href="7fef8d93ba"><code>7fef8d9</code></a>
bump derive minimum version. should be a semver patch effect.</li>
<li><a
href="2524f62b76"><code>2524f62</code></a>
chore: Release bytemuck_derive version 1.10.1</li>
<li><a
href="c4873c9634"><code>c4873c9</code></a>
.</li>
<li><a
href="374df78558"><code>374df78</code></a>
Make the <code>Pod</code>/<code>NoUninit</code> derive macros use
<code>assert!</code> to check size equality,...</li>
<li><a
href="6633a5113f"><code>6633a51</code></a>
Fix a typo (<a
href="https://redirect.github.com/Lokathor/bytemuck/issues/319">#319</a>)</li>
<li><a
href="1c9e7ba196"><code>1c9e7ba</code></a>
chore: Release bytemuck_derive version 1.10.0</li>
<li><a
href="5e58cf7182"><code>5e58cf7</code></a>
changelog</li>
<li><a
href="0e11472150"><code>0e11472</code></a>
add support for deriving NoUninit on enums with fields (<a
href="https://redirect.github.com/Lokathor/bytemuck/issues/292">#292</a>)</li>
<li>See full diff in <a
href="https://github.com/Lokathor/bytemuck/compare/v1.23.1...v1.23.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bytemuck&package-manager=cargo&previous-version=1.23.1&new-version=1.23.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 16:59:44 +00:00
dependabot[bot]
11f6a424e0 build(deps): bump hashbrown from 0.15.4 to 0.15.5 (#38556)
Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.15.4 to
0.15.5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/hashbrown/releases">hashbrown's
releases</a>.</em></p>
<blockquote>
<h2>v0.15.5</h2>
<h3>Added</h3>
<ul>
<li>Added <code>Entry::or_default_entry</code> and
<code>Entry::or_insert_entry</code>.</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Re-implemented likely/unlikely with <code>#[cold]</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md">hashbrown's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/hashbrown/compare/v0.15.4...v0.15.5">0.15.5</a>
- 2025-08-07</h2>
<h3>Added</h3>
<ul>
<li>Added <code>Entry::or_default_entry</code> and
<code>Entry::or_insert_entry</code>.</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Re-implemented likely/unlikely with <code>#[cold]</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b751eef8e9"><code>b751eef</code></a>
Update CHANGELOG.md</li>
<li><a
href="da4c80e8e0"><code>da4c80e</code></a>
chore: release v0.15.5</li>
<li><a
href="43584833e6"><code>4358483</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-lang/hashbrown/issues/631">#631</a>
from DaniPopes/readd-likely</li>
<li><a
href="64bd7db1d1"><code>64bd7db</code></a>
Re-implement likely/unlikely with <code>#[cold]</code></li>
<li><a
href="670213fb32"><code>670213f</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-lang/hashbrown/issues/601">#601</a>
from braddunbar/or-default-entry</li>
<li><a
href="99761e4817"><code>99761e4</code></a>
fix clippy issues</li>
<li><a
href="e44ee2d34c"><code>e44ee2d</code></a>
Merge branch 'master' of <a
href="https://github.com/rust-lang/hashbrown">https://github.com/rust-lang/hashbrown</a>
into or-defau...</li>
<li><a
href="e514afefa9"><code>e514afe</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-lang/hashbrown/issues/624">#624</a>
from rust-lang/release-plz-2025-05-28T15-42-25Z</li>
<li>See full diff in <a
href="https://github.com/rust-lang/hashbrown/compare/v0.15.4...v0.15.5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hashbrown&package-manager=cargo&previous-version=0.15.4&new-version=0.15.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 16:56:22 +00:00
Oriol Brufau
93c9fc14f1 layout: Let stretch on flex item cross size stretch to the line (#38526)
We were instead stretching to the containing block, which implied that
the behaviors of a `stretch` size and `stretch` alignment weren't
consistent.

As resolved by the CSSWG, the behavior will now be:
 - If the cross size of the line is known, stretch to the line.
 - Otherwise, stretch to the containing block.

See https://github.com/w3c/csswg-drafts/issues/11784

This aligns us with Blink, which has already shipped this new behavior.

Testing: Improves existing WPT and adds a new test.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-08 15:45:30 +00:00
shuppy
9866ca7e59 devtools: Warn when sending common protocol errors (#38548)
#37686 fixed an entire class of devtools protocol bugs that led to
protocol desyncs with warnings in the Servo log, but it also removed the
warnings, making it harder to spot where our devtools impl is
incomplete.

this patch reintroduces a warning whenever some Actor::handle_message()
returns Err(ActorError).

Testing: debug logging only, so not really worth testing

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-08-08 14:11:23 +00:00
Martin Robinson
4257db643b Revert "Include the scrollable overflow of a child box if either its parent or child has overflow: visible (#38443)" (#38546)
This reverts commit dcb90bb85e.

This broke scrollable overflow calculation in the following case:

```
<div id="foo" style="width: 200px; height: 200px; outline: solid; overflow: auto;">
    <div style="height: 5000px; background: pink;">hello</div>
</div>
```

In this case the overflow is propagating through the `overflow: auto`
`<div>` and into the parents. When dumping the flow tree I see the root
node being 5000 pixels tall. It's unclear why this change didn't break
any tests, so it's likely that we need to add a test for this case.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-08 13:37:14 +00:00
Martin Robinson
5c307a38df script_bindings Start wrapping unsafe code in unsafe {} (#38545)
This is useful to better isolate `unsafe` code. Once all unsafe calls
are wrapped we can enable the Rust warning.  This also explicitly
disables the warning for generated code, which is a much more difficult
task. After this change there are 211 warnings left in
`script_bindings`.

Testing: This should not change behavior and is thus covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-08 12:21:31 +00:00
shuppy
c9541f2906 devtools: Expose introductionType to devtools clients (#38541)
in the devtools protocol, [source
forms](https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#loading-script-sources)
announced in `resources-available-array` messages can include the
`introductionType`, which more or less mirrors the field of the same
name in SpiderMonkey’s CompileOptions.

this patch exposes `introductionType` accordingly, allowing us to check
for the correct values in automated tests.

Testing: new coverage in devtools tests
Fixes: part of #36027

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-08 12:20:30 +00:00
Mukilan Thiyagarajan
23c0947072 script: make Node::xml_serialize fallible. (#38532)
Testing: [Try run][1] did not reveal any test failures. There doesn't
seem to be any straightforward failure scenarios that can be triggered
in `xml5ever` that are not IO errors and the xml_serialize method simply
serializes to a String buffer.

[1]:
https://github.com/servo/servo/actions/runs/16824267959/job/47657275606l

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
2025-08-08 11:52:59 +00:00
Jerens Lensun
8f52e28225 mach(test-tidy): Make wpt lint logic servo-tidy.toml and optimize checking for changed files (#38511)
Fix wpt lint logic to respect servo-tidy.toml ignores and --all flag

Testing: Should be covered on CI
Fixes: #38510  #37991

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-08 10:34:42 +00:00
Euclid Ye
31acac316d webdriver: Rename wait_for_script_response to wait_for_ipc_response (#38537)
As titled. Initially I want to include this as part of another PR but
looks so messy..
The motivation is that we are no longer just waiting for script
response.

Testing: No behaviour change.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-08 09:49:57 +00:00
Martin Robinson
6651f37c05 script/compositor: Handle cursor updates from script (#38518)
Instead of using WebRender hit testing to update the cursor, base it on
layout hit tests. This allows removing the majority of WebRender hit
test items and finally opens up the possibility of adding support for
custom cursors. In addition, this change fixes an issue where cursors
were not set properly on areas of the viewport that extended past the
page content.

Testing: This is difficult to test as verifying that the cursor changed
properly is beyond the capabilities of Servo's test harnesses.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-07 18:49:38 +00:00
Oriol Brufau
87538282db layout: Floor content-box size by zero when stretching flex item (#38521)
When stretching the cross size of a flex item to its flex line, we were
computing the stretch size by subtracting padding, border and margin
from the line size. However, this could result in a negative amount for
the content-box cross size. Therefore, this floors it by zero.

Testing: Adding new tests
Fixes: #38517

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-07 18:19:22 +00:00
Oriol Brufau
cf99d437fb layout: Refactor FlexItemLayoutResult (#38515)
This performs various refactorings:

- Turns `content_size` into `content_block_size`, losing the inline
component, since it was the same as `containing_block_inline_size`.
- Merges `containing_block_inline_size` and
`containing_block_block_size` into `containing_block_size`.
- Removes `has_child_which_depends_on_block_constraints` since this
information should already be in `depends_on_block_constraints`.
- `FlexItem::layout()` is no longer responsible for trying to reuse the
previous result. Therefore it no longer returns an `Option`, and no
longer accepts the previous result as a parameter.
- `FlexItemLayoutResult::compatible_with_containing_block_size()` is
removed, and a simplified version is inlined into the relevant caller of
`FlexItem::layout()`.

Testing: Not needed (no change in behavior)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-07 18:18:46 +00:00
dependabot[bot]
e40be635bd build(deps): bump keyboard-types from 0.8.0 to 0.8.1 (#38523)
Bumps [keyboard-types](https://github.com/pyfisch/keyboard-types) from
0.8.0 to 0.8.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pyfisch/keyboard-types/releases">keyboard-types's
releases</a>.</em></p>
<blockquote>
<h2>v0.8.1</h2>
<h2>Added</h2>
<ul>
<li>Added <code>impl From&lt;NamedKey&gt; for Key</code>: <a
href="https://redirect.github.com/rust-windowing/keyboard-types/pull/81">rust-windowing/keyboard-types#81</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/rust-windowing/keyboard-types/compare/v0.8.0...v0.8.1">https://github.com/rust-windowing/keyboard-types/compare/v0.8.0...v0.8.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f82b9e4a8a"><code>f82b9e4</code></a>
Release v0.8.1</li>
<li><a
href="6b939e28b3"><code>6b939e2</code></a>
Make it clear in the docs that space is not a named key</li>
<li><a
href="25d8e98baa"><code>25d8e98</code></a>
Add <code>impl From\&lt;NamedKey&gt; for Key</code></li>
<li><a
href="763483400d"><code>7634834</code></a>
Doc tweaks / improvements</li>
<li><a
href="29852c9eca"><code>29852c9</code></a>
no_std: Don't use the <code>std</code> prelude</li>
<li>See full diff in <a
href="https://github.com/pyfisch/keyboard-types/compare/v0.8.0...v0.8.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=keyboard-types&package-manager=cargo&previous-version=0.8.0&new-version=0.8.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 18:07:47 +00:00
dependabot[bot]
71de910c8c build(deps): bump bytemuck_derive from 1.10.0 to 1.10.1 (#38522)
Bumps [bytemuck_derive](https://github.com/Lokathor/bytemuck) from
1.10.0 to 1.10.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2524f62b76"><code>2524f62</code></a>
chore: Release bytemuck_derive version 1.10.1</li>
<li><a
href="c4873c9634"><code>c4873c9</code></a>
.</li>
<li><a
href="374df78558"><code>374df78</code></a>
Make the <code>Pod</code>/<code>NoUninit</code> derive macros use
<code>assert!</code> to check size equality,...</li>
<li><a
href="6633a5113f"><code>6633a51</code></a>
Fix a typo (<a
href="https://redirect.github.com/Lokathor/bytemuck/issues/319">#319</a>)</li>
<li>See full diff in <a
href="https://github.com/Lokathor/bytemuck/compare/bytemuck_derive-v1.10.0...bytemuck_derive-v1.10.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bytemuck_derive&package-manager=cargo&previous-version=1.10.0&new-version=1.10.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 18:07:16 +00:00
dependabot[bot]
f7fd913b19 build(deps): bump clap from 4.5.42 to 4.5.43 (#38524)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.42 to 4.5.43.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.43</h2>
<h2>[4.5.43] - 2025-08-06</h2>
<h3>Fixes</h3>
<ul>
<li><em>(help)</em> In long help, list Possible Values before defaults,
rather than after, for a more consistent look</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.43] - 2025-08-06</h2>
<h3>Fixes</h3>
<ul>
<li><em>(help)</em> In long help, list Possible Values before defaults,
rather than after, for a more consistent look</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c4105bd90c"><code>c4105bd</code></a>
chore: Release</li>
<li><a
href="a029b20be6"><code>a029b20</code></a>
docs: Update changelog</li>
<li><a
href="cf15d48b59"><code>cf15d48</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5893">#5893</a>
from 8LWXpg/patch-2</li>
<li><a
href="7e54542de9"><code>7e54542</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5892">#5892</a>
from 8LWXpg/patch-1</li>
<li><a
href="6ffc88f8c9"><code>6ffc88f</code></a>
fix(complete): Check if help string is empty</li>
<li><a
href="7d8470ed9c"><code>7d8470e</code></a>
fix(complete): Fix single quote escaping in PowerShell</li>
<li><a
href="eadcc8f66c"><code>eadcc8f</code></a>
chore: Release</li>
<li><a
href="7ce0f7bea3"><code>7ce0f7b</code></a>
docs: Update changelog</li>
<li><a
href="fea7c5487b"><code>fea7c54</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5888">#5888</a>
from epage/tut</li>
<li><a
href="c297ddd56e"><code>c297ddd</code></a>
docs(tutorial): Experiment with a flat layout</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.42...clap_complete-v4.5.43">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.5.42&new-version=4.5.43)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 18:06:04 +00:00
Josh Matthews
842dd99698 Update to SpiderMonkey 137. (#37077)
Incorporates the updates from https://github.com/servo/mozjs/pull/584.

Testing: Existing WPT coverage is enough.
Fixes: Part of #36258

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-07 16:47:27 +00:00
Nico Burns
fd20a5df42 layout(grid): implement named grid lines and areas (#38306)
### Changes made

This implements named grid lines (line names in `grid-template-*`),
named grid areas (`grid-template-areas`), and the ability to target
those using `grid-{row,column}-{start,end}`. It also includes a bunch of
miscelaneous fixes for `repeat(auto-fill | auto-fit, ...)` syntax as
that interacts with the specification of line names.

The actual layout implementation is in Taffy. The bulk of this PR is
updating Servo to translate (CSS Grid-related) Stylo types into Taffy
types using a new iterator-based API which uses iterators and lazy
translation for efficiency (which is more important now that we're
dealing with string data, even though they're `Atom`s).

### Testing

This functionality has lots of WPT tests. It fixes some seemingly random
CSS Grid tests that use named lines/areas even though that's not what
they're testing.

### Screenshots

wikipedia.org

<img width="1624" height="1056" alt="Screenshot 2025-07-27 at 20 03 16"
src="https://github.com/user-attachments/assets/2c50b96f-ae36-4405-ac48-b771bfdcb515"
/>

bbc.co.uk:

<img width="1624" height="1056" alt="Screenshot 2025-07-27 at 20 32 57"
src="https://github.com/user-attachments/assets/ba84e211-65d2-4411-95fb-7b9b91bea31c"
/>

theguardian.com:

<img width="1624" height="1056" alt="Screenshot 2025-07-27 at 20 33 29"
src="https://github.com/user-attachments/assets/e85daaa6-5fb0-45d4-b9ec-b22b38b087ec"
/>

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-07 14:41:19 +00:00
shuppy
a9664c4199 devtools: Reland devtools test fixes missing from #38359 (#38516)
in #38359, we intended to land some fixes to the devtools tests, but we
failed to actually include those fixes in the patch. this patch adds a
second internal web server, then reworks test_sources_list() to load
scripts from the two distinct origins of our internal web servers:
<http://127.0.0.1:10000> and <http://127.0.0.1:10001>.

Testing: covered by existing devtools tests, which now actually pass
Fixes: part of #36325

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-07 13:38:48 +00:00
shuppy
8cfd82f55c mach: Allow filtering tests in mach test-devtools (#38514)
`mach test-devtools` uses
[unittest](https://docs.python.org/3/library/unittest.html) without
[unittest.main()](https://docs.python.org/3/library/unittest.html#unittest.main),
so we can parse arguments and set up the test runner how we want, but
this means there’s currently no way to filter tests on the command line.

this patch plumbs the positional arguments (if any) into the
TestLoader’s
[testNamePatterns](https://docs.python.org/3/library/unittest.html#unittest.TestLoader.testNamePatterns),
with extra logic to treat any pattern `pattern` not containing `*` like
`*pattern*`. this is the same behaviour as unittest.main()
[`-k`](https://docs.python.org/3/library/unittest.html#cmdoption-unittest-k):

```
$ ./mach test-devtools domparser responsexml
Running devtools tests...
Running 2 tests:
- test_source_content_inline_script_with_domparser (servo.devtools_tests.DevtoolsTests.test_source_content_inline_script_with_domparser)
- test_source_content_inline_script_with_responsexml (servo.devtools_tests.DevtoolsTests.test_source_content_inline_script_with_responsexml)

test_source_content_inline_script_with_domparser (servo.devtools_tests.DevtoolsTests.test_source_content_inline_script_with_domparser) ... ok
test_source_content_inline_script_with_responsexml (servo.devtools_tests.DevtoolsTests.test_source_content_inline_script_with_responsexml) ... ok

----------------------------------------------------------------------
Ran 2 tests in 4.055s

OK
```

Testing: not really worth automated testing, but tested manually above
Fixes: part of #36325

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
2025-08-07 13:23:53 +00:00
Nico Burns
52ba8facc2 stylo: use simplified restyle damage macros (#38465)
Servo PR for https://github.com/servo/stylo/pull/222

Fixes: #38506
Testing: adding new tests

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-07 10:41:22 +00:00
Euclid Ye
616b86fb6d webdriver: Check if container is obscured for "Element Click" (#38497)
Implement step 7 of [Element
Click](https://w3c.github.io/webdriver/#element-click): check whether
container is obscured.

Testing: `/webdriver/tests/classic/element_click/interactability.py` can
now fully pass. Other changes are combined effect with
17a269a8ad due to script execution, and
exposes new problem:
https://github.com/servo/servo/pull/38497#discussion_r2257866612

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-07 09:16:50 +00:00
Gregory Terzian
b23cf9c6cd net: clean shutdown of fetch thread (#38421)
The fetch thread is currently not shut down, meaning it is one of the
threads counted in the "threads are still running after shutdown (bad)"
counter shown when Servo has shut-down.

This PR introduces a mechanism to start, and shut-down, one fetch thread
per process that requires one.

Testing: WPT tests and unit tests(for font context). Also manually
tested loading and closing "about:blank": this change indeed brings down
the count of threads still running after shutdown by one.

Fixes: https://github.com/servo/servo/issues/34887

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-07 09:01:30 +00:00
Martin Robinson
ad805e3110 compositor/layout: Rely on layout for fine-grained input event hit testing (#38480)
Before, the compositor was responsible for doing the hit testing during
input events within a page. This change moves that hit testing to
layout.  With this change, epoch mismatches are no longer a bit deal and
we can simply ignore them, as the Constellation and Script will take
care of ignoring hit tests against scroll nodes and browsing contexts
that no longer exist. This means that hit testing retry support can be
removed.

Add the concept of a Script `HitTest` that transforms the coarse-grained
renderer hit test into one that hit tests against the actual layout
items.

Testing: Currently we do not have good tests for verifying the behavior
of
input events, but WebDriver tests should cover this.
Fixes: This is part of #37932.
Fixes: #26608.
Fixes: #25282.
Fixes: #38090.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: kongbai1996 <1782765876@qq.com>
2025-08-07 08:38:43 +00:00
sagudev
c0cc8484f8 canvas: pop many clips on restore (#38496)
When restoring context/state we need to pop all clips from current
state, before we just poped one (even if there was none).

Testing: Added new WPT tests

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-07 08:23:09 +00:00
Jonathan Schwender
c055e8b456 ci: Move runner-timeout to composite action (#38503)
We are hitting the limit of 20 workflow references in servo. To help
mitigate this somewhat, we migrate the timeout job to a composite
action, which should be able to support our needs just as well.

Testing: [try run of this
PR](https://github.com/servo/servo/actions/runs/16783916103/job/47529580725).
[try run with unconditional
cancel](https://github.com/servo/servo/actions/runs/16784074213/job/47530099654).

This reduces our workflow count by one, slightly helping to address
#36143

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-07 05:13:36 +00:00
Jonathan Schwender
edf80576d5 ci: Don't give lint job secrets (#38504)
The lint workflow doesn't seem to use any secrets, so we shouldn't pass
them.

Testing: [mach
try](https://github.com/servo/servo/actions/runs/16784528217/job/47531648483)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-08-07 03:45:14 +00:00
Gregory Terzian
a99ad240a0 constellation: join on script-threads (#38424)
We currently send exit signals to script-threads, and we also join on
the BHM worker. This ensure the constellation shuts-down only after
script has dropped it's sender to the BHM worker. By joining on the
script-threads, we have a guarantee that they have exited(which is
stronger than having dropped their senders) by the time the
constellation exits.

Testing: Manually opened many tabs and closed the window, both in
single- and multi-process modes.
Fixes: Part of - https://github.com/servo/servo/issues/30849

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-06 18:05:18 +00:00
dependabot[bot]
b23adab8a5 build(deps): bump anstream from 0.6.19 to 0.6.20 (#38502)
Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.19 to
0.6.20.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="550de4dbb5"><code>550de4d</code></a>
chore: Release</li>
<li><a
href="094640c272"><code>094640c</code></a>
docs: Update changelog</li>
<li><a
href="6aec7d7101"><code>6aec7d7</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-cli/anstyle/issues/265">#265</a>
from aschey/main</li>
<li><a
href="96dbb44f26"><code>96dbb44</code></a>
fix(crossterm): fix color conversion</li>
<li><a
href="e4e2347393"><code>e4e2347</code></a>
chore: Release</li>
<li><a
href="03f5d6a0b1"><code>03f5d6a</code></a>
Merge pull request <a
href="https://redirect.github.com/rust-cli/anstyle/issues/264">#264</a>
from epage/alloc</li>
<li><a
href="3f497444fe"><code>3f49744</code></a>
perf(roff): Remove extra allocations</li>
<li><a
href="d5351f4bf4"><code>d5351f4</code></a>
chore: Release</li>
<li><a
href="44e1d1d745"><code>44e1d1d</code></a>
fix(roff): Minimize size of output (<a
href="https://redirect.github.com/rust-cli/anstyle/issues/263">#263</a>)</li>
<li><a
href="9c2b802041"><code>9c2b802</code></a>
fix(roff): Minimize size of output</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-cli/anstyle/compare/anstream-v0.6.19...anstream-v0.6.20">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=anstream&package-manager=cargo&previous-version=0.6.19&new-version=0.6.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 18:02:34 +00:00
Euclid Ye
56033d844a script: Rename some parent to child (#38498)
I believe there was some code migration but name's not been changed.

Testing: No behaviour change.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-06 17:39:38 +00:00
Jonathan Schwender
0233ad5e40 bencher: Clone thresholds from main (#38220)
When running bencher on pull requests or try, we want to clone any
thresholds we setup for main to detect regressions.
Note: The thresholds we are actually interested in are defined on the
production profile, while in PRs we currently test release. Probably we
should reconsider that, but perhaps that could be a different PR with
seperate discussion.

Testing: ./mach try
([ohos](https://github.com/servo/servo/actions/runs/16779272577))

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-06 17:34:40 +00:00
Nico Burns
e31dcc95f9 Use cached layout in grid layout (#38493)
This makes grid layout make use of Servo's layout caching system.

Testing: No behavior change. Existing WPT coverage

---------

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-08-06 14:15:12 +00:00
Martin Robinson
44a11a7c6c script/layout: Ensure a StackingContextTree before IntersectionObserver geometry queries (#38473)
IntersectionObserver needs to be able to query node geometry without
forcing a layout. A previous layout could have run without needing a
`StackingContextTree`. In that case the layout-less query should finish
building the `StackingContextTree` before doing the query.  Add a new
type of layout API which requests that layout finishes building the
StackingContextTree.

This change also slightly simplifies and corrects the naming of
`Element` APIs around client box queries.

Testing: This should fix intermittent failures in WPT tests.
Fixes: #38380.
Fixes: #38390.
Closes: #38400.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-06 13:46:43 +00:00
lumiscosity
757dbc0eda servoshell: make the color picker and select picker closeable (#38373)
Addresses the first half of #38347 by making it possible to close the
select picker and the color picker by pressing ESC or clicking off the
modal. Marking this as draft since I need some help with squashing a bug
- the "clicking off" part only triggers after moving the mouse or
pressing any key, and I'm not sure why.

Testing: Manual.
Fixes: First half of #38347.

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
2025-08-06 11:49:47 +00:00
Euclid Ye
98522db8be webdriver: Search ancestors instead of preceding nodes when computing container for option&optgroup (#38491)
Fix the bug from 6 years ago https://github.com/servo/servo/pull/24090.

According to [spec](https://w3c.github.io/webdriver/#dfn-container), we
should search ancestor instead of preceding nodes (which is reverse
order of pre-order DFS and can end up weird places) when computing
container for `option`&`optgroup`.


Testing: Update WPT.
Fixes: https://github.com/servo/servo/pull/36467#discussion_r2255834497.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-06 08:54:45 +00:00
Abdelrahman Hossam
17a269a8ad script: Implement scrollIntoView (#38230)
This is an implementation for `scrollIntoView`. For now, it is called
when a certain element gains focus.

Testing: Existing WPT tests
Fixes: #24059

Signed-off-by: abdelrahman1234567 <abdelrahman.hossameldin.awadalla@huawei.com>
2025-08-06 08:08:25 +00:00
Shubham Gupta
dcb90bb85e Include the scrollable overflow of a child box if either its parent or child has overflow: visible (#38443)
Include the scrollable overflow of a child box if either its parent or
child has `overflow: visible`

**Issue**: For the blocks having property `overflow:hidden`, their
scroll overflow is not added to parent's scroll overflow.
Causing unable to scroll the parent block aka `Root` block in our Issue
#38248 .

**Testing**: css/cssom-view/scrolling-quirks-vs-nonquirks.html
**Fixes**: #38248

Signed-off-by: Shubham Gupta <shubham13297@gmail.com>
2025-08-06 07:46:54 +00:00
Jerens Lensun
5b148cf5de script_bindings: Add type definitions for configuration.py (#38450)
*Describe the changes that this pull request makes here. This will be
the commit message.*

Testing: *Describe how this pull request is tested or why it doesn't
require tests*
Fixes: *Link to an issue this pull requests fixes or remove this line if
there is no issue*

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-06 05:44:05 +00:00
Euclid Ye
62b181dc85 webdriver: Simplify get_element_pointer_interactable_paint_tree (#38469)
Add step 1 and simplify step 2 - 5 to remove duplicate `GetClientRects`
call.

There is some issue with spec to be updated later.

Testing: No behaviour change.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-06 05:36:34 +00:00
dependabot[bot]
4b7d659cf6 build(deps): bump tokio-util from 0.7.15 to 0.7.16 (#38488)
Bumps [tokio-util](https://github.com/tokio-rs/tokio) from 0.7.15 to
0.7.16.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="cf6b50a3fd"><code>cf6b50a</code></a>
chore: prepare tokio-util v0.7.16 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7507">#7507</a>)</li>
<li><a
href="416e36b0df"><code>416e36b</code></a>
task: stabilise <code>JoinMap</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7075">#7075</a>)</li>
<li><a
href="9741c90f9f"><code>9741c90</code></a>
sync: document cancel safety on <code>SetOnce::wait</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7506">#7506</a>)</li>
<li><a
href="4e3f17bce3"><code>4e3f17b</code></a>
codec: also apply capacity to read buffer in
<code>Framed::with_capacity</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7500">#7500</a>)</li>
<li><a
href="86cbf81e15"><code>86cbf81</code></a>
Merge 'tokio-1.47.1' into 'master'</li>
<li><a
href="be8ee45b3f"><code>be8ee45</code></a>
chore: prepare Tokio v1.47.1 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7504">#7504</a>)</li>
<li><a
href="d9b19166cd"><code>d9b1916</code></a>
Merge 'tokio-1.43.2' into 'tokio-1.47.x' (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7503">#7503</a>)</li>
<li><a
href="db8edc620f"><code>db8edc6</code></a>
chore: prepare Tokio v1.43.2 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7502">#7502</a>)</li>
<li><a
href="e47565b086"><code>e47565b</code></a>
blocking: clarify that spawn_blocking is aborted if not yet started (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7501">#7501</a>)</li>
<li><a
href="4730984d66"><code>4730984</code></a>
readme: add 1.47 as LTS release (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7497">#7497</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-util-0.7.15...tokio-util-0.7.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio-util&package-manager=cargo&previous-version=0.7.15&new-version=0.7.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 20:28:35 +00:00
dependabot[bot]
90dd034070 build(deps): bump async-lock from 3.4.0 to 3.4.1 (#38486)
Bumps [async-lock](https://github.com/smol-rs/async-lock) from 3.4.0 to
3.4.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/smol-rs/async-lock/releases">async-lock's
releases</a>.</em></p>
<blockquote>
<h2>v3.4.1</h2>
<ul>
<li>Fix typos in docs. (<a
href="https://redirect.github.com/smol-rs/async-lock/issues/89">#89</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/smol-rs/async-lock/blob/master/CHANGELOG.md">async-lock's
changelog</a>.</em></p>
<blockquote>
<h1>Version 3.4.1</h1>
<ul>
<li>Fix typos in docs. (<a
href="https://redirect.github.com/smol-rs/async-lock/issues/89">#89</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="82283a2e1b"><code>82283a2</code></a>
v3.4.1</li>
<li><a
href="cd0937d70a"><code>cd0937d</code></a>
Fix clippy::empty_line_after_doc_comments and
clippy::empty_line_after_outer_...</li>
<li><a
href="7819a20973"><code>7819a20</code></a>
Fix clippy::needless_lifetimes warning</li>
<li><a
href="efcc3d8b32"><code>efcc3d8</code></a>
ci: Use reusable workflows for fmt and security_audit</li>
<li><a
href="6bece6e513"><code>6bece6e</code></a>
ci: Use &quot;v2.0.0&quot; branch for security check</li>
<li><a
href="9963923bad"><code>9963923</code></a>
chore: Fix typos (<a
href="https://redirect.github.com/smol-rs/async-lock/issues/89">#89</a>)</li>
<li><a
href="8717b76b68"><code>8717b76</code></a>
chore: Fix lints (<a
href="https://redirect.github.com/smol-rs/async-lock/issues/92">#92</a>)</li>
<li>See full diff in <a
href="https://github.com/smol-rs/async-lock/compare/v3.4.0...v3.4.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=async-lock&package-manager=cargo&previous-version=3.4.0&new-version=3.4.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 19:38:52 +00:00
dependabot[bot]
fe2c901498 build(deps): bump futures-lite from 2.6.0 to 2.6.1 (#38485)
Bumps [futures-lite](https://github.com/smol-rs/futures-lite) from 2.6.0
to 2.6.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/smol-rs/futures-lite/releases">futures-lite's
releases</a>.</em></p>
<blockquote>
<h2>v2.6.1</h2>
<ul>
<li>Fix docs for <code>once_future</code> and
<code>stop_after_future</code>. (<a
href="https://redirect.github.com/smol-rs/futures-lite/issues/131">#131</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md">futures-lite's
changelog</a>.</em></p>
<blockquote>
<h1>Version 2.6.1</h1>
<ul>
<li>Fix docs for <code>once_future</code> and
<code>stop_after_future</code>. (<a
href="https://redirect.github.com/smol-rs/futures-lite/issues/131">#131</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="226ce18976"><code>226ce18</code></a>
v2.6.1</li>
<li><a
href="3444c7a2f8"><code>3444c7a</code></a>
docs: Fix <code>stop_after_future</code> link and
<code>once_future</code> description (<a
href="https://redirect.github.com/smol-rs/futures-lite/issues/131">#131</a>)</li>
<li><a
href="d608d08310"><code>d608d08</code></a>
Some alloc-related cleanup (<a
href="https://redirect.github.com/smol-rs/futures-lite/issues/130">#130</a>)</li>
<li><a
href="077e1c1a5e"><code>077e1c1</code></a>
Switch to unconditional no_std attribute (<a
href="https://redirect.github.com/smol-rs/futures-lite/issues/128">#128</a>)</li>
<li><a
href="5c196b9516"><code>5c196b9</code></a>
docs: Enable doc_auto_cfg on docs.rs (<a
href="https://redirect.github.com/smol-rs/futures-lite/issues/125">#125</a>)</li>
<li><a
href="64dbe9e4ec"><code>64dbe9e</code></a>
ci: Use reusable workflows for fmt and security_audit</li>
<li>See full diff in <a
href="https://github.com/smol-rs/futures-lite/compare/v2.6.0...v2.6.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=futures-lite&package-manager=cargo&previous-version=2.6.0&new-version=2.6.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 19:36:19 +00:00
dependabot[bot]
961a83c905 build(deps): bump event-listener from 5.4.0 to 5.4.1 (#38483)
Bumps [event-listener](https://github.com/smol-rs/event-listener) from
5.4.0 to 5.4.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/smol-rs/event-listener/releases">event-listener's
releases</a>.</em></p>
<blockquote>
<h2>v5.4.1</h2>
<ul>
<li>Fix a copy-paste error in <code>wait_timeout</code> docs (<a
href="https://redirect.github.com/smol-rs/event-listener/issues/152">#152</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/smol-rs/event-listener/blob/master/CHANGELOG.md">event-listener's
changelog</a>.</em></p>
<blockquote>
<h1>Version 5.4.1</h1>
<ul>
<li>Fix a copy-paste error in <code>wait_timeout</code> docs (<a
href="https://redirect.github.com/smol-rs/event-listener/issues/152">#152</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0c18ca2308"><code>0c18ca2</code></a>
v5.4.1</li>
<li><a
href="56d87b45d6"><code>56d87b4</code></a>
Update criterion requirement from 0.6 to 0.7 (<a
href="https://redirect.github.com/smol-rs/event-listener/issues/156">#156</a>)</li>
<li><a
href="810a6db63c"><code>810a6db</code></a>
Update criterion requirement from 0.5 to 0.6 (<a
href="https://redirect.github.com/smol-rs/event-listener/issues/153">#153</a>)</li>
<li><a
href="91a936e66e"><code>91a936e</code></a>
Fix doctest on WASM</li>
<li><a
href="323da40de2"><code>323da40</code></a>
Fix clippy::mem_replace_option_with_some warning</li>
<li><a
href="29e15c0fcb"><code>29e15c0</code></a>
ci: Re-enable WASM test</li>
<li><a
href="a8aefba5e9"><code>a8aefba</code></a>
Merge pull request <a
href="https://redirect.github.com/smol-rs/event-listener/issues/152">#152</a>
from JamesHallowell/fix-wait-timeout-docs</li>
<li><a
href="9870df3f63"><code>9870df3</code></a>
Minor amendment to <code>wait_timeout</code> docs</li>
<li><a
href="51ae1404dc"><code>51ae140</code></a>
ci: Use reusable workflows for fmt and security_audit</li>
<li>See full diff in <a
href="https://github.com/smol-rs/event-listener/compare/v5.4.0...v5.4.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=event-listener&package-manager=cargo&previous-version=5.4.0&new-version=5.4.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 19:15:27 +00:00
dependabot[bot]
46e71fc30b build(deps): bump signal-hook-registry from 1.4.5 to 1.4.6 (#38481)
Bumps [signal-hook-registry](https://github.com/vorner/signal-hook) from
1.4.5 to 1.4.6.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md">signal-hook-registry's
changelog</a>.</em></p>
<blockquote>
<h1>signal-hook-registry-1.4.6</h1>
<ul>
<li>Reword/improve the safety requirements docs for register (<a
href="https://redirect.github.com/vorner/signal-hook/issues/178">#178</a>).</li>
</ul>
<h1>signal-hook-1.3.18</h1>
<ul>
<li>Release the special-case removal of AIX for top-level signal-hook
too (<a
href="https://redirect.github.com/vorner/signal-hook/issues/169">#169</a>,
<a
href="https://redirect.github.com/vorner/signal-hook/issues/176">#176</a>).</li>
</ul>
<h1>signal-hook-async-std-0.3.0</h1>
<ul>
<li>Bump async-std to 0.2 (<a
href="https://redirect.github.com/vorner/signal-hook/issues/172">#172</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4c5d0a0d3b"><code>4c5d0a0</code></a>
Release registry v1.4.6</li>
<li><a
href="b299a05459"><code>b299a05</code></a>
Merge pull request <a
href="https://redirect.github.com/vorner/signal-hook/issues/179">#179</a>
from vorner/safety-docs</li>
<li><a
href="164d58b924"><code>164d58b</code></a>
Clarify safety requirements for register.</li>
<li><a
href="1ff235c739"><code>1ff235c</code></a>
Merge pull request <a
href="https://redirect.github.com/vorner/signal-hook/issues/177">#177</a>
from tomstokes/master</li>
<li><a
href="9ffea0fd11"><code>9ffea0f</code></a>
docs: Fix MIO feature name</li>
<li><a
href="e343491fa5"><code>e343491</code></a>
Version 0.3.18</li>
<li><a
href="dfdec81944"><code>dfdec81</code></a>
signal-hook-async-std 0.3.0</li>
<li><a
href="dcc5da2f30"><code>dcc5da2</code></a>
Merge pull request <a
href="https://redirect.github.com/vorner/signal-hook/issues/175">#175</a>
from gaykitty/update-async-std</li>
<li><a
href="171da9cf95"><code>171da9c</code></a>
Update async-io and futures-lite to v2</li>
<li>See full diff in <a
href="https://github.com/vorner/signal-hook/compare/registry-v1.4.5...registry-v1.4.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=signal-hook-registry&package-manager=cargo&previous-version=1.4.5&new-version=1.4.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 19:05:30 +00:00
sagudev
f3644422c4 layout: Separate LayoutDamage bit patterns nibbles with _ (#38476)
Using `_` on such long bit patterns makes them easier to read.

Testing: Just formatting.
Fixes: #38474

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-05 12:43:16 +00:00
shuppy
3eddfeaee2 script: Tell SpiderMonkey whether scripts are inline (#38363)
to use the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/) as the single
source of truth about scripts and their sources for devtools purposes
(servo/servo#38334), the debugger script needs to be able to distinguish
inline scripts from other scripts, because inline scripts are a special
case where the source contents need to come from the Servo parser.

the mechanism for this is
[Debugger.Script.prototype.**introductionType**](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#introductiontype),
which is `inlineScript` for inline scripts or a variety of other values
for other kinds of scripts, but only the embedder can provide this
information.

this patch bumps mozjs to servo/mozjs#603, which expands on
CompileOptionsWrapper, making it a safe wrapper around CompileOptions.
to construct one from safe code, use Runtime::new_compile_options().
then you can call `set_introduction_type(&'static CStr)` on the new
instance. we also make Runtime::evaluate_script() take a
CompileOptionsWrapper from the caller, instead of constructing one
internally.

in this patch, we set the introductionType to `c"inlineScript"` when
calling run_a_classic_script() and compile_module_script() for inline
scripts, and leave it unset all other cases.

Testing: will undergo automated tests in #38334
Fixes: part of #36027, part of servo/servo#38378

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-05 12:41:14 +00:00
Martin Robinson
0bf8676345 layout: Cache projected point in spatial node when hit testing (#38464)
Implement a simple one element cache for projected points in spatial
nodes. This should reduce the amount matrix math done during hit
testing.

Testing: This should not change test results, but should improve
performance a bit. Thus, tests are not necessary beyond existing
performance tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-05 11:07:42 +00:00
shuppy
ec99e7a8b0 script: Remove logging in debugger script (#38475)
#38333 adds new code to the debugger script containing some
console.log() calls, which unlike native rust log messages can’t be
turned off. this patch removes them for now, until we find a better
approach.

Testing: no testable changes in this patch
Fixes: noisy logging as of #38333

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-05 11:01:35 +00:00
Martin Robinson
11844ca5af layout: Add a layout hit test and use it for document.elementsFromPoint (#38463)
In #18933, hit testing was moved from layout to WebRender. This presents
some issues. For instance, the DOM can change at the same time that hit
test is happening. This can mean that hit test returns references to
defunct DOM nodes, introducing memory safety issues. Currently, Servo
will try to ensure that the epochs used for testing and those recorded
in the DOM match, but this is not very reliable and has led to code that
retries failed hit tests.

This change reintroduces (8 years later) a layout hit tester and turns
it on for `document.elementFromPoint` and `document.elementsFromPoint`.
The idea is that this hit tester will gradually replace the majority of
the WebRender hit testing happening in the renderer.

Testing: This shouldn't really change the behavior hit testing, but it
seems to improve one WPT test.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: kongbai1996 <1782765876@qq.com>
2025-08-05 09:48:21 +00:00
Martin Robinson
3e856cbf11 layout: Introduce ReflowPhasesRun (#38467)
There were various booleans on `ReflowResults` that represented various
actions that might have been taken during a reflow request. Replace
those with a bitflags that better represents what reflow phases have
actually been run. Update variable names to reflect what they mean.

In addition, run some post-layout tasks unconditionally. They are
already contingent on the results returned from layout.

This simplifies and clarifies the code a good deal.

Testing: This should not change observable behavior and thus is covered
by existing WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-05 08:39:42 +00:00
shuppy
92a9d24a13 script: Add new Window globals as debuggees (#38333)
to debug the scripts in a page with the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), we need to
pass the page’s global object to
[Debugger.prototype.**addDebuggee()**](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#adddebuggee-global).
we could pick up the global via the [onNewGlobalObject()
hook](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewglobalobject-global),
but if our script system passes the global to the debugger script
instead, we can later add details like the PipelineId that will help
servo identify debuggees that the API is notifying us about (#38334).

this patch plumbs new Window globals from script into addDebuggee() via
the debugger script. to call into the debugger script with structured
input, we create a new DOM event type, DebuggerEvent, that the debugger
script listens for as the “addDebuggee” event.

Testing: no testable effects yet, but will be used in #37667
Fixes: part of #36027

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-08-05 05:48:53 +00:00
Gregory Terzian
77ff351cde net: clean shutdown of the async runtime (#38425)
The previous use of a static variable for the runtime prevented it from
shutting down cleanly, because shutdown requires dropping or taking
ownership of it. This PR switches the static variable to a handle only,
and introduces a new trait to pass a handle to the async runtime to the
constellation, where it can be shut-down along with other components and
help reduce our count of still running threads after shutdown.

Testing: manual testing, and covered by unit-test in net, and wpt tests.
Fixes: part of - https://github.com/servo/servo/issues/30849

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-04 21:42:47 +00:00
Gregory Terzian
7ad32f944f script: allow for undefined chunks in stream piping (#38470)
Current code uses `undefined` as chunk value to identify the closing of
a stream, but this breaks once you start streaming a chunk that is
actually `undefined`, as shown in
https://github.com/servo/servo/pull/38466. This PR re-implement the
logic in a way that allows for chunks to be `undefined`.

Testing: Should maintain `streams/piping` WPT pass rates. Also makes the
`undefined` case of
[`/encoding/streams/encode-bad-chunks.any.js`](c59ee57b5d/tests/wpt/tests/encoding/streams/encode-bad-chunks.any.js (L29)),
but that is only noticeable in https://github.com/servo/servo/pull/38466
Fixes: None, but noted in https://github.com/servo/servo/pull/38466

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-04 21:42:25 +00:00
TIN TUN AUNG
778dc70181 script: fix set muted on html video element creation (#38462)
Set muted on html video element creation. On `video` element creation,
the `set_mute` function will be called before the media player is
created, hence the player will still act as not being muted. This PR fix
this behaviour by passing `muted` info after player is created as part
of `setup_media_player` process.

Testing: Locally test on Windows11
Fixes: https://github.com/servo/servo/issues/38448

---------

Signed-off-by: rayguo17 <tin.tun.aung1@huawei.com>
Signed-off-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Co-authored-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
2025-08-04 18:57:20 +00:00
Oriol Brufau
391bf5d605 build(deps): bump Stylo to servo/stylo#221 (#38449)
Bumps Stylo to "Drop obsolete `layout.css.transition-behavior.enabled`
flag".

Testing: Not needed (no behavior change).

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-04 17:37:45 +00:00
Oriol Brufau
b01bb462b6 layout: Recreate lazy block size when re-doing layout to avoid floats (#38366)
Block-level boxes that establish an independent formatting context need
to avoid overlapping floats. If their inline size stretches, then we may
need to lay out multiple times.

The problem was that when trying with a different inline size, the
intrinsic block size can change, but we were using the cached final
block size from the previous attempt.

Testing: Adding new test
Fixes: #38365

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-04 17:07:10 +00:00
Taym Haddadi
04ec710e60 Add AsHandleValue trait to Heap<Value> and make Heap values rooted (#38024)
Encapsulates the unsafe conversion from Heap<Value> to HandleValue<'a>,
and reducing repetitive unsafe code at call.

fix #37258
2025-08-04 16:42:53 +00:00
Martin Robinson
9416251cab script: Unify script-based "update the rendering" and throttle it to 60 FPS (#38431)
Instead of running "update the rendering" at every IPC message, only run
it when a timeout has occured in script. In addition, avoid updating the
rendering if a rendering update isn't necessary. This should greatly
reduce the amount of processing that has to happen in script.

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

In addition, pull the following things out of reflow:

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

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

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

Testing: In general, this should not change testable behavior so much,
though it
does seem to fix one test.  The main improvement here should be that
the ScriptThread does less work.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-08-04 16:27:00 +00:00
Leo Ring
bcc8314dd5 Servoshell: Update Window::inner_size on WindowEvent::Resized (fix resize bug) (#38461)
Just adds back a line that was omitted when a different commit was
reverted, see the issue for details, I've tested on Gnome Wayland with
Fedora 42 and the bug is fixed

Fixes: https://github.com/servo/servo/issues/38441
cc @yezhizhen

Signed-off-by: Leo Ring <leoring03@gmail.com>
2025-08-04 13:56:54 +00:00
Leo Ring
26d2d0f7d8 Add can_gc to HTMLDocument NamedGetter (#38455)
Just fixing something missed on my last PR
https://github.com/servo/servo/pull/38433#discussion_r2250744925

Signed-off-by: Leo Ring <leoring03@gmail.com>
2025-08-04 11:41:53 +00:00
Euclid Ye
c59ee57b5d webdriver: Improve "element click" by using container + Upgrade outdated test (#38436)
- According to
[spec](https://w3c.github.io/webdriver/#ref-for-dfn-in-view-3), we
should use container instead of element itself to determine "in-view".
- Updated `test_element_intercepted_no_pointer_events` in
`element_click/interactability.py` to expect "element not interactable".
This was outdated with spec as original test was written 7 years ago
https://github.com/web-platform-tests/wpt/pull/11453.


Testing: new passing cases for `<option>`, `<select>`.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-04 08:12:50 +00:00
Leo Ring
79a45c7da3 Implement HTMLDocument API (#38433)
This updates the pull request from here
https://github.com/servo/servo/pull/32553 that looks to be dormant. The
main change is that I've switched out `reflector` with `document` based
off this suggestion
https://github.com/servo/servo/pull/32553#issuecomment-2179568743, and
the `GetLocation` and `SupportedPropertyNames` methods pass through the
values from `Document`.

The implementation details are otherwise the same as the original PR

Testing: I don't see any WPT tests for this feature, I could make a
custom test if desired
Fixes: https://github.com/servo/servo/issues/32536

---------

Signed-off-by: Leo Ring <leoring03@gmail.com>
2025-08-04 03:42:54 +00:00
Euclid Ye
f0eb6c2b97 servoshell: Sync window toolbar height with minibrowser (#38328)
Toolbar size can be changed if resized, such as entering fullscreen.
Hit-test had wrong offsets after fullscreen/resize as
`WindowEvent::CursorMoved` set wrong coordinates for
`webview_relative_mouse_point` due to outdated toolbar height.

Testing: #38297 now works properly.
Fixes: #38297

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-03 19:16:15 +00:00
sagudev
874645ae86 canvas: Do not use vello layers for opacity or default composition (#38440)
In this PR we moved global alpha handling using temporary layers to
mutation of paint in vello_cpu (we were already doing this in vello
classic). This + not using temporary layer for SrcOver (default and most
common composition operation) allows us to remove most temporary layers
from `with_composition`.

This slightly improves performance of vello backend, but drastically
improves performance of vello_cpu. We are now able to render bunnymark
(100 bunnies) with 60 FPS.

In the future we could cache current layer and change it when
compositing operation changes, although that would complicate clips, so
improvement is questionable.

Testing: Existing WPT tests for functionality, but we do not have any
performance tests.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-03 16:00:46 +00:00
sagudev
fe9341dd46 canvas: Use OptimizeSpeed in vello_cpu (#38437)
According to my tests `OptimizeSpeed` slight improves performance and it
does NOT affect WPT results in negative way.

Testing: Tested by existing WPT tests.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-03 12:12:08 +00:00
Servo WPT Sync
fed1953198 Sync WPT with upstream (03-08-2025) (#38434)
Automated downstream sync of changes from upstream as of 03-08-2025
[no-wpt-sync]

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
2025-08-03 01:48:15 +00:00
Oriol Brufau
76e5758ab9 Upgrade Stylo to 2025-08-01 (#38429)
This continues #37822

Changelog:
- Upstream:
fe9eadf47b...ec21cec41c
- Servo fixups:
29052e550f...7f8df16fdd

Stylo tracking issue: https://github.com/servo/stylo/issues/220

---------

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-08-02 21:48:33 +00:00
Martin Robinson
a7b232ef4c layout: Limit visibility of some types and removed unused enum variants / data (#38428)
Testing: This change mainly removes dead code, so no tests necessary.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-02 16:52:00 +00:00
Alex Touchet
e78d8818a0 deps: Update rand to 0.9.2 (#38430)
Update rand.

Testing: No tests for dependency update.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-08-02 16:25:50 +00:00
Tim van der Lippe
dbb886fad2 Implement initial version of navigator.sendBeacon (#38301)
Gated behind the feature flag `dom_navigator_sendbeacon_enabled` as the
`keep-alive` fetch parameter is crucial for real-life use cases such as
analytics requests.

Part of #4577
Part of #38302

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-08-02 15:14:07 +00:00
Martin Robinson
181f97879d wpt: Ensure that faulty JSON testharness output does not crash the Python test harness (#38420)
When a testharness test also prints debugging output, sometimes the
output can be mixed with the JSON output printed via an alert. This
causes a JSON decoding error in the output. Instead of crashing the
harness and printing many lines of Python stack trace output, print a
nice error. This makes the test output easier to read.

Testing: This is a change to the test harness itself, so no tests
necessary.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-08-02 13:04:17 +00:00
Euclid Ye
58425f6ae2 Replace all sort with unstable sort (#38427)
["When applicable, unstable sorting is preferred because it is generally
faster than stable sorting and it doesn’t allocate auxiliary
memory."](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort)

Binary also reduced by 1KB in Release.

Testing: No behaviour change as semantically all current usage does not
have any pair with `std::cmp::Ordering::Equal`.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-02 12:41:03 +00:00
Alex Touchet
7c70d811a6 Cargo.toml cleanup (#38426)
Cargo.toml cleanup.

Testing: No tests for Cargo.toml edit.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-08-02 10:16:09 +00:00
Euclid Ye
ee1bfa61ce script: Lazy init and reuse const BOOLEAN_ATTRIBUTES (#38423)
Follow up of  #38401.
- The constant String array was recreated for every invocation. Chromium
store this as a global const.
https://source.chromium.org/chromium/chromium/src/+/main:chrome/test/chromedriver/element_commands.cc;l=48-94?q=chrome%2Ftest%2Fchromedriver%2Felement_commands.cc
We now use static `LazyLock` to lazy init and avoid recreation.
- Clean up some comments

Testing: Just refactor.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-02 02:45:52 +00:00
Usman Yahaya Baba
a27715a5a8 Calculate and send the missing transferred_size and content_size to dev tools (#38216)
The current behaviour in dev tools network monitor is missing data for
the `Transferred` size and `Content` size.
We currently have a fn `response_content` that constructs the
`ResponseContentMsg` struct where the two missing data fields are
defined and set to zero values.
These current changes calculates the data in the `response_content` fn
and sends a `NetworkEvent::HttpResponse` to the client when the final
body is done.
Currently, we have data appearing in the `Transferred` column of the
network panel
fixes: https://github.com/servo/servo/issues/38126

---------

Signed-off-by: uthmaniv <uthmanyahayababa@gmail.com>
2025-08-02 02:41:53 +00:00
Usman Yahaya Baba
52b04c9fd3 net: Make devtools test resistant to event reordering (#38412)
This PR makes the `test_redirected_request_to_devtools` collect all the
network events from the channel into a vector, and then asserting that
the expected requests/responses are present in the vector, this aims to
remove the ordering issues that cause intermittent failures.

Testing: ran `./mach test-unit -p net` with the changes in this PR
applied to
https://github.com/servo/servo/actions/runs/16644510716/job/47101866693?pr=38216
getting all tests passing
Fixes: (https://github.com/servo/servo/issues/38411)

---------

Signed-off-by: uthmaniv <uthmanyahayababa@gmail.com>
2025-08-01 22:58:01 +00:00
lumiscosity
5e89f79abe layout: Fix negative outline offset (#38418)
Properly caps the minimum offset on each side as recommended by the
standards: https://drafts.csswg.org/css-ui-3/#outline-offset

Testing: Covered by WPT tests. (3 new passing!)
Fixes: #19508

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-08-01 20:43:04 +00:00
dependabot[bot]
f70d30cf1c build(deps): bump serde_json from 1.0.141 to 1.0.142 (#38417)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.141 to
1.0.142.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/json/releases">serde_json's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.142</h2>
<ul>
<li>impl Default for &amp;Value (<a
href="https://redirect.github.com/serde-rs/json/issues/1265">#1265</a>,
thanks <a
href="https://github.com/aatifsyed"><code>@​aatifsyed</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1731167cd5"><code>1731167</code></a>
Release 1.0.142</li>
<li><a
href="e51c81450a"><code>e51c814</code></a>
Touch up PR 1265</li>
<li><a
href="84abbdb613"><code>84abbdb</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1265">#1265</a>
from aatifsyed/master</li>
<li><a
href="9206cc0150"><code>9206cc0</code></a>
feat: impl Default for &amp;Value</li>
<li>See full diff in <a
href="https://github.com/serde-rs/json/compare/v1.0.141...v1.0.142">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.141&new-version=1.0.142)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 17:50:05 +00:00
dependabot[bot]
fdf180d814 build(deps): bump wayland-protocols-wlr from 0.3.8 to 0.3.9 (#38416)
Bumps [wayland-protocols-wlr](https://github.com/smithay/wayland-rs)
from 0.3.8 to 0.3.9.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-protocols-wlr&package-manager=cargo&previous-version=0.3.8&new-version=0.3.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 17:43:52 +00:00
dependabot[bot]
1e01acd4d5 build(deps): bump wayland-protocols-plasma from 0.3.8 to 0.3.9 (#38415)
Bumps [wayland-protocols-plasma](https://github.com/smithay/wayland-rs)
from 0.3.8 to 0.3.9.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-protocols-plasma&package-manager=cargo&previous-version=0.3.8&new-version=0.3.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 17:35:18 +00:00
dependabot[bot]
4e682cff90 build(deps): bump cc from 1.2.30 to 1.2.31 (#38414)
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.2.30 to 1.2.31.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md">cc's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.31">1.2.31</a>
- 2025-08-01</h2>
<h3>Other</h3>
<ul>
<li>Add doc for using sccache/ccache etc (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1502">#1502</a>)</li>
<li>ability to statically link against C++ stdlib (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1497">#1497</a>)</li>
<li>Add instructions on using sccache (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1503">#1503</a>)</li>
<li>Add support for recognizing some architectures supported by GCC, but
not LLVM. (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1500">#1500</a>)</li>
</ul>
<h1>Changelog</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<p>The format is based on <a
href="https://keepachangelog.com/en/1.0.0/">Keep a Changelog</a>,
and this project adheres to <a
href="https://semver.org/spec/v2.0.0.html">Semantic Versioning</a>.</p>
<h2>[Unreleased]</h2>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bb6cd84d14"><code>bb6cd84</code></a>
chore: release v1.2.31 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1504">#1504</a>)</li>
<li><a
href="02c0952d06"><code>02c0952</code></a>
Add doc for using sccache/ccache etc (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1502">#1502</a>)</li>
<li><a
href="20c06cfd75"><code>20c06cf</code></a>
Proposal: ability to statically link against C++ stdlib (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1497">#1497</a>)</li>
<li><a
href="e435ac9640"><code>e435ac9</code></a>
doc: Add instructions on using sccache (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1503">#1503</a>)</li>
<li><a
href="fcc7fc47d5"><code>fcc7fc4</code></a>
Add support for recognizing some architectures supported by GCC, but not
LLVM...</li>
<li>See full diff in <a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cc&package-manager=cargo&previous-version=1.2.30&new-version=1.2.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 17:26:42 +00:00
sagudev
413fd15264 canvas: prune vello scene on each render and make rendering cacheable (#38406)
As noted in #38345, vello scenes only grow. While we can reset them when
clearing viewport (#38356) that is not enough. We need to reset scene on
each render (~each frame) and providing old frame as backdrop to new
scene. Be do this lazily so multiple rendering without any changes
should be cheaper, we still do GPUBuffer mapping, because that would
require more complex work.

Testing: Code functionality is covered by existing WPT tests, but we do
not have any performance test.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-01 13:48:44 +00:00
Gregory Terzian
5ac9f40625 script: in stream piping, ensure the heap is set only after it has been moved (#38385)
Setting a value on a `Heap` requires the heap to not be moved after the
call to `set`, the current code sets the value first, then moves it into
the `shutdown_error` refcell. It should be the other way around.

Testing: the core logic is covered by WPT tests, which should remain
unchanged.
 
Fixes: [38299](https://github.com/servo/servo/issues/38299)

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-08-01 10:55:17 +00:00
Euclid Ye
625d636b04 compositor: Add more doc for size of different RenderingContext implementation (#38399)
For more details, see
https://github.com/servo/servo/issues/38369#issuecomment-3138378527.

Fixes:
https://github.com/servo/servo/issues/38369#issuecomment-3138458772
Testing: Just adding docs.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-01 09:07:32 +00:00
Kingsley Yung
5e8754bb1d webdriver: consider boolean attribute when get element attribute (#38401)
The function handle_get_attribute should act differently when the
attribute is a boolean attribute.

The full list of attributes can be found in [1]. All attributes marked
as "Boolean attribute" in the "Value" column are boolean attributes.
Note that "hidden" is effectively treated as a boolean attribute,
according to WPT test "test_global_boolean_attributes" in
webdriver/tests/classic/get_element_attribute/get.py

[1] https://html.spec.whatwg.org/multipage/#attributes-3

Testing: Updated WPT test expectation
Fixes: #38353

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2025-08-01 08:44:26 +00:00
Kenzie Raditya Tirtarahardja
05ad9026f5 cargo: Upgrade keyboard-types to 0.8.0 and xcomponent-sys to 0.3.4 (#38375)
With some adjustment for `NamedKey`. The two crates need to be bumped
together to avoid duplicate of `keyboard-types` action.

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-01 08:14:38 +00:00
Jo Steven Novaryo
a063b5e78a script: Fire scroll event whenever JS scrolled (#38321)
Implement JS scroll event firing compliant to
https://drafts.csswg.org/cssom-view/#scrolling-events. Basically
whenever, the an element or the viewport is scrolled, we will fire a
scroll event. The changes push a scroll event whenever an API causes a
scroll position to change.

Testing: New WPT tests for basic APIs.
Part of: https://github.com/servo/servo/issues/31665

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-08-01 07:30:22 +00:00
Euclid Ye
372e5eae59 webdriver: Fix "element in view" by correctly computing resolved PointerEvents style (#38383)
Testing: `element_click/interactability.py`. For some other tests in
headed window, even tho the target is in view it falsely claim not in
view previously.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-08-01 07:08:28 +00:00
batu_hoang
929fd0aa3c webdriver: improve session commands (#38397)
Add timeout and strictFileInteractability capabilities to response of
new session command.
Allow delete session command to run without a session.

Testing: Clear some unexpected results of session tests in webdriver CI
and
`tests/wpt/meta/webdriver/tests/classic/delete_session/delete.py.ini`

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
2025-08-01 06:39:03 +00:00
Kenzie Raditya Tirtarahardja
c836ed374c Suppress warning in webview unit test (#38398)
`webview.focus` return `Result<(),()>`, hence it gives warning if the
result is not handled.

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-08-01 05:42:16 +00:00
Jo Steven Novaryo
a8886c1222 layout: Add BoxFragment rare data (#38325)
Introduce `BoxFragmentRareData`, rare data for `BoxFragment`, which
would store the specific data that is relevant to several fragments.
This would reduce the `BoxFragment` size to 256 from 264 and add 8 bytes
for fragment that have rare data (due to the additional pointer to the
rare data).

Testing: Existing WPT coverage

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-08-01 05:17:39 +00:00
sagudev
09f0e20e29 canvas: Clear vello scene if possible (#38356)
Vello scene only ever grows, so we need to clear it as soon as it's
possible (in clear rect). This PR also adds ignore_clips to
vello_backend (already exists in vello_cpu_backend).

Testing: Behavior is verified by existing tests, as this is mainly a
change for performance. There are currently no performance tests. This
makes bunnymark actually playable (from 3 FPS to 20 FPS on vello_cpu).

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-08-01 05:05:15 +00:00
Jerens Lensun
b05d265de5 script_binding: Add type check on servo script bindings (#38161)
Introduce type checking with Pyrefly in `components/script_bindings`

This commit adds Pyrefly-based type checking to the
`components/script_bindings` directory. The primary goal is to catch
type inconsistencies early and reduce the likelihood of unexpected
runtime errors.

This change affects the `webidl` component, as these script bindings are
responsible for connecting WebIDL specifications to the Rust codebase.

Testing: `./mach test-wpt webidl`
Fixes: *Link to an issue this pull requests fixes or remove this line if
there is no issue*

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-08-01 04:34:24 +00:00
dependabot[bot]
4ce5b17605 build(deps): bump libredox from 0.1.8 to 0.1.9 (#38396)
Bumps libredox from 0.1.8 to 0.1.9.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=libredox&package-manager=cargo&previous-version=0.1.8&new-version=0.1.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 18:04:06 +00:00
dependabot[bot]
b5aaf49d66 build(deps): bump wayland-protocols from 0.32.8 to 0.32.9 (#38394)
Bumps [wayland-protocols](https://github.com/smithay/wayland-rs) from
0.32.8 to 0.32.9.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-protocols&package-manager=cargo&previous-version=0.32.8&new-version=0.32.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 17:49:56 +00:00
dependabot[bot]
ea9d56b92d build(deps): bump wayland-cursor from 0.31.10 to 0.31.11 (#38392)
Bumps [wayland-cursor](https://github.com/smithay/wayland-rs) from
0.31.10 to 0.31.11.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-cursor&package-manager=cargo&previous-version=0.31.10&new-version=0.31.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 17:41:15 +00:00
dependabot[bot]
fd3fc30609 build(deps): bump redox_users from 0.5.0 to 0.5.2 (#38393)
Bumps redox_users from 0.5.0 to 0.5.2.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=redox_users&package-manager=cargo&previous-version=0.5.0&new-version=0.5.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 17:40:31 +00:00
Martin Robinson
5da55adfd3 layout: Account for sticky nodes in ScrollTree transforms and cache transforms (#38391)
When calculating the node to world transform for use in bounding box
queries, cache the values of the transform. In addition, when scroll
offsets change, ensure that the cached values are invalided properly.
This change necessitated the storage of children for each node in the
tree, so that we can walk both up and down the tree. The purpose of this
part of the change is to increase performance when doing multiple
queries and prepare the tree for hit testing.

In addition, this change also tries to take into account sticky offsets,
using the algorithm from WebRender to calculate sticky offsets. This is
also going to be important for hit testing.

Testing: Newly passing tests:
 - /css/css-position/position-sticky-dynamic-ancestor-001.html
 - /css/css-tables/tentative/position-sticky-container.html

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-07-31 13:36:32 +00:00
Jerens Lensun
fca94336d3 mach: Add comment to ignore type on geteuid as it only available on unix (#38388)
Currently pyrefly have an issues with top level assertion on platform
gating, so we decide to ignore it for now.

https://github.com/facebook/pyrefly/issues/795

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-07-31 10:25:02 +00:00
Euclid Ye
d38781d71d servoshell: Revert #38307 to fix resize bug (#38381)
Also added some comments to make things clear.
For details, see
https://github.com/servo/servo/issues/38369#issuecomment-3138378527

Testing: Tested on X11 Ubuntu, Wayland Ubuntu, macOS, Windows.
Fixes: #38369

Co-authored-by: minghuaw <michael.wu1107@gmail.com>

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
Co-authored-by: minghuaw <michael.wu1107@gmail.com>
2025-07-31 08:55:00 +00:00
Jerens Lensun
36f1a373e3 ci: Automatically cancel bootstrap step after 1 hour in wpt linux/mach (#38358)
Introduces a timeout for each WPT run chunk job, set to 1 hour, after
which it will be automatically canceled.

Testing: 
Fixes: #38348

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-07-31 07:24:23 +00:00
shuppy
c09e117bfe script: Create a debugger script for the SpiderMonkey Debugger API (#38331)
to use the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), we need to
call it from an internal debugger script that we will supply. this
script must run in the same runtime as the debuggee(s), but in a
separate
[compartment](https://udn.realityripple.com/docs/Mozilla/Projects/SpiderMonkey/Compartments)
([more
details](https://hacks.mozilla.org/2020/03/future-proofing-firefoxs-javascript-debugger-implementation/)).

this patch defines a new DebuggerGlobalScope type and a new debugger
script resource. when creating each script thread, we create a debugger
global, load the debugger script from resources/debugger.js, and run
that script in the global to initialise the Debugger API.

subsequent patches will use the debugger script as an RPC mechanism for
the Debugger API.

Testing: no testable effects yet, but will be used in #37667
Fixes: part of #36027

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-07-31 06:17:23 +00:00
batu_hoang
8e27fd48cc Implement webdriver element in view (#38329)
Implement step 6 of `ElementClick`:
https://w3c.github.io/webdriver/#dfn-element-click

Tests:
`tests/wpt/tests/webdriver/tests/classic/element_click/interactability.py`

cc: @xiaochengh

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
2025-07-31 04:27:02 +00:00
Sebastian C
8008d5aa85 net: Add expiry limit to cookies and prevent panics from max-age (#38376)
Based on RFC6256 expiration must be limited to 400 days. This also
solves a bug I found where large max-age values could cause an overflow
panic when adding.

Testing: New unit test added. There doesn't appear to be a WPT test that
relies on this under cookies/ but cookiestore/ does.

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
2025-07-31 02:52:50 +00:00
shuppy
8194aa7c1e script: Implement jsglue trap for runJobs() (#38265)
in the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), hooks like
[onNewGlobalObject()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewglobalobject-global)
use an AutoDebuggerJobQueueInterruption to [switch to a new microtask
queue](b14aebff23/mozjs-sys/mozjs/js/src/debugger/Debugger.cpp (L2834-L2841))
and avoid clobbering the debuggee’s microtask queue. this in turn relies
on JobQueue::runJobs(), which is [not yet implemented in
RustJobQueue](b14aebff23/mozjs-sys/src/jsglue.cpp (L76-L78)).

this patch bumps mozjs to servo/mozjs#597, which implements
[runJobs()](b14aebff23/mozjs-sys/mozjs/js/public/Promise.h (L61-L76))
for RustJobQueue by calling into Servo’s MicrotaskQueue::checkpoint()
via a new function in JobQueueTraps.

SpiderMonkey [does not own external job
queues](b14aebff23/mozjs-sys/mozjs/js/public/Promise.h (L117-L123)),
so the lifetime of these queues is managed in Servo, where they are
stored in a Vec-based stack. stack-like behaviour is adequate for
SpiderMonkey’s save and restore patterns, as far as we can tell, but
we’ve added an assertion just in case.

Testing: manually tested working in devtools debugger patch (#37667),
where it will undergo automated tests

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-07-31 02:46:37 +00:00
dependabot[bot]
b40d73de38 build(deps): bump rustls from 0.23.29 to 0.23.31 (#38372)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.29 to
0.23.31.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="647ece1346"><code>647ece1</code></a>
Prepare 0.23.31</li>
<li><a
href="b2831e7490"><code>b2831e7</code></a>
rustls-bench: short circuit single threaded tests</li>
<li><a
href="668231f573"><code>668231f</code></a>
Improve testing of non-blocking <code>complete_io()</code></li>
<li><a
href="5e5d629611"><code>5e5d629</code></a>
<code>complete_io</code>: make non-blocking error return reachable</li>
<li><a
href="2a852c0244"><code>2a852c0</code></a>
Exit <code>complete_io</code> loop as soon as no progress is made</li>
<li><a
href="2f487cfc5a"><code>2f487cf</code></a>
rustls-test: withdraw <code>FailsReads</code> helper</li>
<li><a
href="41bfb22d5b"><code>41bfb22</code></a>
rustls-test: add helper for non-blocking IO tests</li>
<li><a
href="0ccbc63df3"><code>0ccbc63</code></a>
Cargo: rustls version 0.23.29 -&gt; 0.23.30</li>
<li><a
href="4c16f03443"><code>4c16f03</code></a>
Fix: Do not try to call deframer on junk data</li>
<li><a
href="6b9df65bb9"><code>6b9df65</code></a>
sign: make public_key_to_spki() public</li>
<li>Additional commits viewable in <a
href="https://github.com/rustls/rustls/compare/v/0.23.29...v/0.23.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rustls&package-manager=cargo&previous-version=0.23.29&new-version=0.23.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 18:13:29 +00:00
dependabot[bot]
c6eb18d02e build(deps): bump zune-jpeg from 0.4.19 to 0.4.20 (#38371)
Bumps [zune-jpeg](https://github.com/etemesi254/zune-image) from 0.4.19
to 0.4.20.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/etemesi254/zune-image/releases">zune-jpeg's
releases</a>.</em></p>
<blockquote>
<h2>v0.1.0</h2>
<p>No release notes provided.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/etemesi254/zune-image/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zune-jpeg&package-manager=cargo&previous-version=0.4.19&new-version=0.4.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 18:12:16 +00:00
dependabot[bot]
32f1497095 build(deps): bump clap from 4.5.41 to 4.5.42 (#38370)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.41 to 4.5.42.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.42</h2>
<h2>[4.5.42] - 2025-07-30</h2>
<h3>Fixes</h3>
<ul>
<li>Include subcommand visible long aliases in <code>--help</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.42] - 2025-07-30</h2>
<h3>Fixes</h3>
<ul>
<li>Include subcommand visible long aliases in <code>--help</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="27cc4b7f59"><code>27cc4b7</code></a>
chore: Release</li>
<li><a
href="16a4fc7190"><code>16a4fc7</code></a>
docs: Update changelog</li>
<li><a
href="07f9f15eda"><code>07f9f15</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5874">#5874</a>
from tetzng/fix-fish-completions</li>
<li><a
href="721deab943"><code>721deab</code></a>
chore: Release</li>
<li><a
href="a4be55bf64"><code>a4be55b</code></a>
docs: Update changelog</li>
<li><a
href="fd5e6915f0"><code>fd5e691</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5877">#5877</a>
from therealprof/features/use-btreemap-instead-of-so...</li>
<li><a
href="6604e79ee7"><code>6604e79</code></a>
Use BTreeMap instead of a sorted Vec</li>
<li><a
href="28e163a8e7"><code>28e163a</code></a>
fix(complete): Remove {} and replace commas with newlines</li>
<li><a
href="b5a47c46ac"><code>b5a47c4</code></a>
chore: Release</li>
<li><a
href="b154a7a114"><code>b154a7a</code></a>
docs: Update changelog</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.41...clap_complete-v4.5.42">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.5.41&new-version=4.5.42)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 18:11:24 +00:00
dependabot[bot]
e3268dc742 build(deps): bump wayland-client from 0.31.10 to 0.31.11 (#38368)
Bumps [wayland-client](https://github.com/smithay/wayland-rs) from
0.31.10 to 0.31.11.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-client&package-manager=cargo&previous-version=0.31.10&new-version=0.31.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 18:08:16 +00:00
sagudev
5c42a113bd chore: Update vello to fix radials in vello_cpu (#38338)
This update includes a fix for
https://github.com/linebender/vello/issues/1124.

Testing: Existing WPT tests
try run: https://github.com/sagudev/servo/actions/runs/16599834725

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-30 17:49:41 +00:00
Gregory Terzian
815ed10b5f background_hang_monitor: ensure workers run until monitored components do (#38322)
Shut-down of the background hang monitor(bhm) is currently  problematic:

- it does not always run until the monitored script-thread does(see
"BackgroundHangMonitor has gone away" mentioned in
https://github.com/servo/servo/issues/34158).
- it shuts-down before the constellation(good, so actually
https://github.com/servo/servo/issues/24850 was "fixed" but in a way
that introduced a new problem), but using a mechanism that allows it to
shutdown before script(the problem above).
- there are various mechanism(see the doc comments removed by this PR)
in place which are meant to ensure a clean shutdown despite the above
problems; those are complicated, and become unnecessary once those
problems are fixed.

All of the above is fixed by the changes in this PR, which ensure the
bhm does not shut-down before script, and also maintains the invariant
that it must shut-down before the constellation(in single-process mode)
or before the main thread(in multi-process mode), but using a mechanism
which allows it to keep running until script shuts-down.

An unnecessary option around the exit signal is also removed.

As a positive side-effect, it also ensures that any script-thread is
shut-down before the constellation(because for the bhm worker to exit,
the monitored script must have exited first), so this should also fix a
host of other problems noted in
https://github.com/servo/servo/issues/30849, but each should be
confirmed independently(and various other improvements seem possible in
their specific contexts, such as joining on script threads, and removing
the `ScriptThreadMessage::ExitScriptThread`).

Fixes: https://github.com/servo/servo/issues/24850 and part of
https://github.com/servo/servo/issues/34158

Testing: Unit tests in `component/background_hang_monitor/tests`. Also
manually tested loading "about-blank" in single- and multi-process mode.

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
2025-07-30 13:03:28 +00:00
lumiscosity
e5334a64c4 layout: respect image-rendering on border images (#38346)
Properly passes `image-rendering` down to border images.

Testing: Covered by WPT tests.

---------

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-07-30 12:57:10 +00:00
Oriol Brufau
c7c33f5f47 layout: Make IndependentFormattingContext::contents private (again) (#38350)
This was done in #24871, but after some refactorings it became public.
This makes it private again. As said in
b2b3ea992c:

> Privacy forces the rest of the code to go through methods
> rather than matching on the enum,
> reducing accidental layout-mode-specific behavior.

It also avoids the risk of accidentally calling `layout()` on the inner
layout-mode-specific struct, bypassing caching.

Testing: Not needed (no behavior change)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-07-30 12:55:32 +00:00
Oriol Brufau
8c737a6a79 layout: Make a couple tracing reflect what they actually trace (#38349)
`IndependentFormattingContext::layout` was being traced with name
`IndependentFormattingContext::layout_with_caching`. Better use
`IndependentFormattingContext::layout` instead.

There was also a debug trace with name `NonReplaced cache miss`, but now
this code also applies to replaced boxes, so I'm renaming it to
`IndependentFormattingContext::layout cache miss`.

Testing: Not needed (no behavior change)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-07-30 12:55:25 +00:00
shuppy
a671c50bc9 devtools: Fix source contents tests and fix a race (#38359)
test_sources_list() relied on <https://servo.org/js/load-table.js> to
test scripts loaded from multiple origins, but that file was deleted a
couple of weeks ago. this patch adds a second internal web server, then
replaces that load with scripts loaded from two distinct origins:
<http://127.0.0.1:10000> and <http://127.0.0.1:10001>.

fixing that test revealed an impl bug where inline module scripts
containing `import` statements may never get their source contents
populated. this is because the logic for populating source contents for
inline scripts only applied to source actors created *before* we
finished parsing the page. we assumed that inline scripts always block
the parser, but this is not the case. this patch ensures that inline
source contents can be populated for source actors created after parse.

Testing: adds a new test,
test_source_content_with_inline_module_import_external()
Fixes: part of #36325

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-07-30 11:29:24 +00:00
Jo Steven Novaryo
900dd8d191 layout: Consider transform for bounding box queries (#37871)
The recent changes that cached the Scroll Tree present an opportunity to
calculate the queries that consider transform and scroll (dubbed as post
composite queries) accurately.

This PR propose a solution for this calculation by noting the lowest
scroll tree nodes that would affect a fragment. To do this, each
fragment would store a new attribute `spatial_tree_node` -- scroll tree
node id that we could use for the query. This referencing is considered
because the scroll tree node construction is managed by the fragment
itself. Therefore it would ease the managing the possibly stale
reference and future query cache invalidation considering the
development of incremental layout.

The bounding box query then could transform the bounding content rect of
a fragment using the computed current transformation matrix.

Fixes: https://github.com/servo/servo/issues/35768
Testing: Existing and new WPT

---------

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-07-30 08:13:54 +00:00
batu_hoang
37ac4ffeb4 Rework on webdriver wait for navigation complete (#38234)
For current implementation, when a command may trigger a navigation,
webdriver only waits for document readiness state.
However, not all navigations make change in document.
This PR handles more cases for waiting for a navigation, and apply to
`element_click`.

- Before sending a command which may trigger a navigation, `webdriver`
sets `load status send` to `embedder`, `constelltation` and `script
thread` to listen to `navigation events`.
- Webdriver check if there is a navigation with `script thread`.
- If the navigation is loading a new url, webdriver checks if the
request is approved with `constellation`, then waits for document
readiness state.
- If the navigation is a hashchange, webdriver waits untill all new
generated dom events have been processed.

Testing: 
`tests/wpt/tests/webdriver/tests/classic/element_click/navigate.py`
`tests/wpt/tests/webdriver/tests/classic/element_click/user_prompts.py`
https://github.com/longvatrong111/servo/actions/runs/16488690749

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
2025-07-30 07:24:07 +00:00
Euclid Ye
8b3e7b1c6a webdriver: Keep constellation alive and Open new top-level browsing context with new session request when none is open (#37410)
Keep Constellation alive even when all browsing context closed in
WebDriver mode. In this case, when creating a new session, we would open
a new top-level browsing context.

Fixes: #37408
Testing: `./mach test-wpt -r
.\tests\wpt\tests\webdriver\tests\classic\close_window\close.py
--product servodriver`

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-30 06:41:58 +00:00
Oriol Brufau
0e18057863 build(deps): bump Stylo to servo/stylo#215 (#38351)
Bumps Stylo to "Vendor markupsafe; Add macOS and Windows CI jobs".

Testing: Not needed (no behavior change).

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-07-30 03:23:03 +00:00
dependabot[bot]
b0f57bffdc build(deps): bump wayland-backend from 0.3.10 to 0.3.11 (#38340)
Bumps [wayland-backend](https://github.com/smithay/wayland-rs) from
0.3.10 to 0.3.11.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-backend&package-manager=cargo&previous-version=0.3.10&new-version=0.3.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 22:32:40 +00:00
sagudev
82f5524901 canvas: Use create_similar_draw_target for recreate (#38336)
`create_similar_draw_target` is more performant then creating completely
new target (this creates new wgpu device in vello backend).

Testing: This change does not modify test results, but should increase
performance.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-29 19:45:49 +00:00
dependabot[bot]
f16e7d6daa build(deps): bump libredox from 0.1.6 to 0.1.8 (#38344)
Bumps libredox from 0.1.6 to 0.1.8.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=libredox&package-manager=cargo&previous-version=0.1.6&new-version=0.1.8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 17:32:13 +00:00
dependabot[bot]
cafbb7a4bb build(deps): bump wayland-scanner from 0.31.6 to 0.31.7 (#38341)
Bumps [wayland-scanner](https://github.com/smithay/wayland-rs) from
0.31.6 to 0.31.7.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/smithay/wayland-rs/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wayland-scanner&package-manager=cargo&previous-version=0.31.6&new-version=0.31.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 17:29:22 +00:00
dependabot[bot]
899ce08eab build(deps): bump rustc-demangle from 0.1.25 to 0.1.26 (#38343)
Bumps [rustc-demangle](https://github.com/rust-lang/rustc-demangle) from
0.1.25 to 0.1.26.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/rustc-demangle/releases">rustc-demangle's
releases</a>.</em></p>
<blockquote>
<h2>rustc-demangle-v0.1.26</h2>
<h3>Other</h3>
<ul>
<li>Add new v0 demangling tags for pattern types</li>
<li>Do not publish the <code>native-c</code> crate</li>
<li>Use release-plz for releases</li>
<li>Add a CI workflow to publish new releases after a tag is pushed</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/rustc-demangle/blob/main/CHANGELOG.md">rustc-demangle's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/rustc-demangle/compare/rustc-demangle-v0.1.25...rustc-demangle-v0.1.26">0.1.26</a>
- 2025-07-16</h2>
<h3>Other</h3>
<ul>
<li>Add new v0 demangling tags for pattern types</li>
<li>Do not publish the <code>native-c</code> crate</li>
<li>Use release-plz for releases</li>
<li>Add a CI workflow to publish new releases after a tag is pushed</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/rust-lang/rustc-demangle/commits/rustc-demangle-v0.1.26">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rustc-demangle&package-manager=cargo&previous-version=0.1.25&new-version=0.1.26)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 17:23:51 +00:00
Jerens Lensun
c738bbc41c mach: Make test-tidy line length check Unicode-aware (#38335)
Currently, our implementation for each line-checking function reads the
file as bytes, so we need to properly decode each line to UTF-8 before
evaluating it. This ensures it is counted as a string and not as bytes

Testing: I tested by changing the comment like the issue above and it
not give an error
Fixes: #38237

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-07-29 14:46:32 +00:00
lumiscosity
eda83564f3 Losslessly optimize Android resources (#38332)
Losslessly optimizes the Android resources. These are the only non-test
PNGs remaining in the project, to my knowledge.

Testing: Unnecessary, since this only optimizes assets.

Signed-off-by: maple! <averyrudelphe@gmail.com>
2025-07-29 12:15:50 +00:00
atbrakhi
4ef15ec42f devtools: Make contentType optional in source actor source responses (#38330)
This is allowed by the [protocol
docs](https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#loading-script-sources),
and eliminates an unwrap().

Testing: does not get exercised yet, but will be used in #37667 
Fixes: part of #36027

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: Delan Azabani <dazabani@igalia.com>
2025-07-29 11:16:59 +00:00
sagudev
7761030250 canvas: Fix the build of the vello backend (#38326)
Multiple Into hoops cannot be resolved by compiler

Testing: Just refactor.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-29 10:14:56 +00:00
Martin Robinson
8d5faa9bf9 compositor: Request reflow when doing a page zooming (#38166)
Request a reflow when doing page zoom and only modify the scaling of the
WebView scene after the first root pipeline display list with the new
zoom is ready. In addition:

  - store zoom limits in `Scale` types
  - send `ViewportDetails` along with the display list so that we can
    detect when the root pipeline scale is ready.

Testing: This is quite hard to test as it requires verification that
contents are zoomed appropriately at the right time.
Fixes: #38091.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-07-29 10:04:37 +00:00
Oriol Brufau
3d2f0d1be5 layout: Fix depends_on_block_constraints logic (#38318)
The logic was wrong, sometimes we weren't setting it to true on flex
containers that needed it, and then as a workaround we were setting it
to to true on flex items that didn't need it.

For example, this testcase had 5 cache misses when stretching the items,
now we will avoid laying them out again:
```html
<div style="display: flex">
  <div></div>  <div></div>  <div></div>  <div></div>  <div></div>
</div>
```

Also, the workaround wasn't always working, e.g. it failed to stretch
the green element here:
```html
<div style="display: flex; min-height: 200px">
  <div>
    <div style="display: flex; height: 100%; background-color: red">
      <div style="width: 200px; background-color: green;"></div>
    </div>
  </div>
</div>
```

Testing: Adding new test
Fixes: #38023

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-07-29 09:59:54 +00:00
Euclid Ye
a5d4c49ec6 cargo: Use workspace dependency for winit and upgrade to 0.30.12 (#38261)
Use workspace dependency to sync the `winit` version between examples &
libservo.
Upgrade winit to 0.30.12.

Fixes: The hope is lost as it doesn't fix anything..

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-29 09:45:22 +00:00
Oriol Brufau
7ce5de4e2d layout: Include spanned gutters when laying out contents of table cell (#38290)
If a cell would e.g. span 2 columns, each 50px wide, separated by a 10px
gutter, then we used to lay out the contents of the cell with a 100px
wide containing block. Now we will include the size of the gutter.

Testing: Adding new test
Fixes: #38277

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-07-29 08:57:22 +00:00
Euclid Ye
61273a4321 servoshell: Remove redundant WindowEvent::Resized handler (#38307)
They are redundant as marked in #38174 as rendering related rect already
updated in `minibrowser::update`. As a result, we are able to remove
`window_rendering_context` and `inner_size` fields from `struct Window`.

Testing: No regression/behaviour change in all tests running with headed
window.
Fixes:
https://github.com/servo/servo/issues/38255#issuecomment-3117642340

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-29 08:44:42 +00:00
Jerens Lensun
239bdd7c1a mach: Fail on invalid argument in try_parser (#38324)
This will block the command, print an error message about an invalid
argument being passed to the ./mach try command, and return the exit
code.

Testing: `./mach try test-wpt`
Fixes: #38193

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-07-29 07:00:07 +00:00
minghuaw
554b2da1ad Script: Implement TextDecoderStream (#38112)
This PR implements the `TextDecoderStream`. Other than introducing the
necessary mod and webidl files corresponding to `TextDecoderStream`,
this PR also involves some changes in `TextDecoder` and
`TrasnformStream`:

- The common part that can be shared between `TextDecoder` and
`TextDecoderStream` are extracted into a separate type
`script::dom::textdecodercommon::TextDecoderCommon`. This type could
probably use a different name because there is an interface called
`TextDecoderCommon` in the spec
(https://encoding.spec.whatwg.org/#textdecodercommon) which just gets
included in `TextDecoder` and `TextDecoderStream`.
- The three algorithms in `TransformStream` (`cancel`, `flush`, and
`transform`) all have become `enum` that has a `Js` variant for a JS
function object and a `Native` variant for a rust trait object. Whether
the cancel algorithm needs this enum type is debatable as I did not find
any interface in the spec that explicitly sets the cancel algorithm.

Testing: Existing WPT tests `tests/wpt/tests/encoding/stream` should be
sufficient
Fixes: #37723

---------

Signed-off-by: minghuaw <michael.wu1107@gmail.com>
Signed-off-by: minghuaw <wuminghua7@huawei.com>
Signed-off-by: Minghua Wu <michael.wu1107@gmail.com>
2025-07-29 04:18:15 +00:00
Usman Yahaya Baba
25822920cf Make request parameter immutable in send_response_to_devtools and send_early_httprequest_to_devtools (#38319)
- Changes request parameters to immutable borrow
Testing: 
Fixes: https://github.com/servo/servo/issues/38300

Signed-off-by: uthmaniv <uthmanyahayababa@gmail.com>
2025-07-29 00:42:20 +00:00
sagudev
d410236c87 canvas: Send CanvasClose to canvas thread from canvas state (#38315)
Currently we only closed `CanvasRenderingContext2D` (and
`OffscreenCanvasRenderingContext2D` because it wraps
`CanvasRenderingContext2D`), but we didn't close last consumer of
`CanvasState` which is `PaintRenderingContext`. To prevent any future
leaks, let's just send `CanvasClose` in `CanvasState` drop.

Testing: Existing WPT tests

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-28 20:09:47 +00:00
Euclid Ye
213b532712 cargo: Remove last traces of webdriver from libservo (#38314)
The feature which enables optional dependency & function is never used
and can be safely removed.

Testing: It can still builds.

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
2025-07-28 14:47:56 +00:00
Josh Matthews
02297791c8 chore: Add test for synthesized image documents with CSP headers. (#38191)
Adds a regression test for https://github.com/servo/servo/pull/38186,
verifying that the CSP header on the image is ignored when it's loaded
as a document.

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-07-28 13:45:54 +00:00
sagudev
a67998b520 canvas: Gate raqote backend behind feature (enabled for now) (#38312)
We want to eventually remove raqote backend, but for now we can gate it
behind a feature (still enabled by default in servoshell) like the rest
of backends. `dom_canvas_backend=auto` will select first available
backend. Builds on top of https://github.com/servo/servo/pull/38310 to
support cases where no backend is available.

Testing: It compiles with or without feature

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-28 11:54:40 +00:00
shuppy
9da4c74a60 script: Implement jsglue traps for saveJobQueue() (#38232)
in the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), hooks like
[onNewGlobalObject()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewglobalobject-global)
use an AutoDebuggerJobQueueInterruption to [switch to a new microtask
queue](b14aebff23/mozjs-sys/mozjs/js/src/debugger/Debugger.cpp (L2834-L2841))
and avoid clobbering the debuggee’s microtask queue. this in turn relies
on JobQueue::saveJobQueue(), which is [not yet implemented in
RustJobQueue](b14aebff23/mozjs-sys/src/jsglue.cpp (L83-L86)).

this patch bumps mozjs to servo/mozjs#595, which implements
[saveJobQueue() and
SavedJobQueue](b14aebff23/mozjs-sys/mozjs/js/public/Promise.h (L92-L114))
for RustJobQueue by calling into Servo via two new JobQueueTraps that
create and destroy extra “interrupt” queues for use by the debugger.

SpiderMonkey [does not own external job
queues](b14aebff23/mozjs-sys/mozjs/js/public/Promise.h (L117-L123)),
so the lifetime of these queues is managed in Servo, where they are
stored in a Vec-based stack. stack-like behaviour is adequate for
SpiderMonkey’s save and restore patterns, as far as we can tell, but
we’ve added an assertion just in case.

Testing: manually tested working in devtools debugger patch (#37667),
where it will undergo automated tests
Fixes: #38311

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-07-28 11:39:35 +00:00
batu_hoang
f5ee72f89a Rework webdriver session (#38225)
Reimplement webdriver session for better match to spec:

- Add `create_session`:
[spec](https://www.w3.org/TR/webdriver2/#dfn-create-a-session)
- Refactor `handle_new_session`
- Replace `PageLoadStrategy` string by enum

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
2025-07-28 10:14:14 +00:00
sagudev
ae69646371 canvas: Make 2D context state creation failable and use dom_canvas_backend pref for backend selection (#38310)
Before script just crashed in those cases because IPCSender was dropped,
now we send `None` to tell script about the failure and fail getContext
or registerPainter accordingly.
This PR also unifies `dom_canvas_{backends}_enabled` prefs into
`dom_canvas_backend` which is more flexible in multi-backends scenarios.

Reviewable per commit.

Testing: Added servo specific WPT test.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-28 09:13:07 +00:00
Jerens Lensun
93d234d270 mach: Add type check on python/servo directory (#38085)
Introduce `python/servo` folder in pyrefly type checker

Testing: Manual testing via `./mach test-tidy` command

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-07-28 04:16:08 +00:00
sagudev
056b1538c0 canvas: Add vello_cpu backend (#38282)
vello_cpu does not have any tests timeouts, because we do not need
download stuff from GPU as all work happens on CPU. So performance wise
it's better then classic vello at least for our usecase. There are some
vello bugs, but I think we will be able to sort them out within
upstream, eventually. Interestingly enough there are no new PASS like
they were with classic vello.

Difference with raqote can be observed here:
https://github.com/sagudev/servo/actions/runs/16549241085/attempts/1#summary-46802486798

## Known vello problems:

- https://github.com/linebender/vello/issues/1119
- https://github.com/linebender/vello/issues/1056
-
`/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html`
- `kurbo::Cap::Butt` is defect (only visible with big lineWidth)
https://github.com/linebender/vello/issues/1063
  - `/html/canvas/element/line-styles/2d.line.cross.html`
  - `/html/canvas/element/line-styles/2d.line.miter.acute.html`
- other lack of strong correct problems
(https://github.com/linebender/vello/issues/1063#issuecomment-2998084736):
  - `/html/canvas/element/path-objects/2d.path.rect.selfintersect.html`
- `putImageData(getImageData(...), ...)` is lossy (precision problems,
might be due to ImageData being unmultiplied)
-
`/html/canvas/element/pixel-manipulation/2d.imageData.put.unchanged.html`

Testing: Tested using vello_cpu_canvas subsuite

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-27 19:56:38 +00:00
Nico Burns
15be75f955 Disable serde feature of taffy dependency (#38303)
Disables the serde of taffy. This feature doesn't seem to be actually
being used.

Testing: This would cause build errors if it was required. So if it
builds it should be fine.

Signed-off-by: Nico Burns <nico@nicoburns.com>
2025-07-27 14:54:27 +00:00
Kingsley Yung
ab6e151c89 script_binding: wrapper of JSAutoStructuredCloneBuffer (#38284)
Instead of using raw pointer of JSAutoStructuredCloneBuffer, use its
wrapper JSAutoStructuredCloneBufferWrapper, which implements the Drop
trait that can prevent leakage when structured cloning fails.

Testing: Refactoring. Existing tests should be enough.
Fixes: #37966

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2025-07-27 12:41:02 +00:00
Servo WPT Sync
633d746e2f Sync WPT with upstream (27-07-2025) (#38296)
Automated downstream sync of changes from upstream as of 27-07-2025
[no-wpt-sync]

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
2025-07-27 01:45:05 +00:00
Oriol Brufau
9614fb997a layout: Inline {default,natural}_{inline,block}_size methods (#38276)
They only had one caller.

Testing: Unnecessary (no behavior change)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-07-26 17:40:01 +00:00
sagudev
bc71fb8c0d canvas: Respect FillRule (#38294)
We just need to pass user provided FillRule via IPC to canvas paint
thread, then pass it all down to backend, which will handle it.

Testing: Added WPT tests.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-26 17:20:04 +00:00
Ashwin Naren
4188852963 script: Don't panic on IndexedDB put operation structured clone (#38280)
Fixes panic by rethrowing exceptions.

Testing: Covered by WPT
Fixes: #38075

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-26 16:35:10 +00:00
Ashwin Naren
e1bda86861 script: Implement converting values to indexeddb key ranges (#38278)
Implements
https://www.w3.org/TR/IndexedDB-2/#convert-a-value-to-a-key-range. Part
of #38187.

Testing: Currently unused, doesn't change anything
Fixes: None

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-26 13:20:52 +00:00
sagudev
f8d6c5646c canvas: Use bytemuck to remove unsafe from raqote backend (#38283)
This PR changes surface type from `Vec<u8>` to `Vec<u32>` as this is
native type of raqote's backend. We used unsafe for `Vec<u8>` <->
`Vec<u32>` casting that is replaced with bytemuck. Bytemuck is already
in cargo lock.

Testing: Existing WPT tests.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-26 13:16:16 +00:00
sagudev
daebb5a033 canvas: Move peniko/kurbo conversions in separate file (#38279)
This will allow reusing those conversions in vello_cpu backend.

Testing: Just refactor

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-26 13:14:25 +00:00
Kingsley Yung
4d899ecba5 webdriver: configure Expires and SameSite in handle_add_cookie (#38285)
Handler::handle_add_cookie did not configure the attributes "Expires"
and "SameSite". This patch adds them.

Testing: Passing WPT tests that were expected to fail.
Fixes: https://github.com/servo/servo/pull/37715#issuecomment-3069734014

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2025-07-26 12:23:24 +00:00
Euclid Ye
a3de3ffa75 webdriver: Implement maximize window for both headless&headed window (#38271)
- Implement [Maximize
Window](https://w3c.github.io/webdriver/#maximize-window)
- Previously, headless window screen size is same as inner size if not
specified in preference. We make it double as required by the test to
not have max window initially.
- Some other random cleanup.

Testing: webdriver Stress test for maximize window (headed + headless).

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-26 12:21:17 +00:00
JoeDow
9ef4d0c9d7 layout: Correctly marking box damage when text-related style changed (#38059)
This change aims to supplement the missing incremental box tree
construction when text-related styles change. Since certain text style
adjustments can alter visible text content and typography. Therefore,
the parent nodes of such text are marked as needing to re-collect their
box tree children to ensure the correct display of text.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-07-26 11:50:13 +00:00
sagudev
d678901122 canvas: Add vello backend (#36821)
Add vello backend by implementing Backend traits in canvas crate (so
this lives in canvas_paint_thread - embedded process). Current
implementation uses normal wgpu, so we block on GPU work. Vello backend
is gated behind `vello` feature and `dom_canvas_vello_enabled` pref.

Feature-wise this backend is on on par with raqote (sometimes better
sometimes worse), but performance wise it's worse.

## Known vello problems:

- image roundtrip does not work (fixed in
https://github.com/linebender/vello/pull/974)
- https://github.com/linebender/vello/issues/1066 (fixed)
- clip layers are not working properly:
https://github.com/linebender/vello/issues/1061
  - `/html/canvas/element/pixel-manipulation/2d.imageData.put.*`
  - `/html/canvas/element/path-objects/2d.path.clip.intersect.html`
- https://github.com/linebender/vello/issues/1056
-
`/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html`
- `kurbo::Cap::Butt` is defect (only visible with big lineWidth)
https://github.com/linebender/vello/issues/1063
  - `/html/canvas/element/line-styles/2d.line.cross.html`
  - `/html/canvas/element/line-styles/2d.line.miter.acute.html`
- other lack of strong correct problems
(https://github.com/linebender/vello/issues/1063#issuecomment-2998084736):
  - `/html/canvas/element/path-objects/2d.path.rect.selfintersect.html`
- There is currently no way to do put image properly in vello as we
would need to ignore all clips and other stuff (we try to work around
this on best effort basis)
https://github.com/linebender/vello/issues/1088
  - `/html/canvas/element/pixel-manipulation/2d.imageData.put.*`
- precision problems
  - `/html/canvas/element/path-objects/2d.path.stroke.scale2.html`
  - `/html/canvas/element/path-objects/2d.path.arc.scale.1.html`

## Known servo problems

- bad performance due to blocking on GPU work
  - some get/put intensive tests `TIMEOUT`
- proper shadow support (non-blocker as we already are living without it
now)
- support for rect shadow is there but unimplemented currently as that's
the state in raqote

Testing: `mach try vello` will run normal WPT (with raqote) +
vello_canvas subsuite that runs only on `/html/canvas/element`. All
subsuite expectations are stored separately.
Fixes: #36823
Fixes: #35230

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-26 04:53:10 +00:00
Ashwin Naren
c2ed599eb1 script: implement IDBKeyRange (#38268)
#37684 provided the backend for this change. The key range interface
just wraps a `IndexedDBKeyRange` and exposes the methods from it as per
the spec.

Spec: https://www.w3.org/TR/IndexedDB-2/#keyrange
Testing: WPT tests (some regressions have been exposed, but that's fine)

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
2025-07-26 02:59:15 +00:00
sagudev
8b2a5fca54 canvas: Make GetImageData simple and only IPC message to obtain pixels (#38274)
Currently we had `GetImageData` and `SendPixels` to obtain pixels from
script thread. This PR unifies those into single `GetImageData` that
does not need canvas size and has optional rect (for obtaining sub
image).

Testing: Existing WPT tests

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-26 02:28:52 +00:00
dependabot[bot]
77f85f390e build(deps): bump litrs from 0.4.1 to 0.4.2 (#38275)
Bumps [litrs](https://github.com/LukasKalbertodt/litrs) from 0.4.1 to
0.4.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/LukasKalbertodt/litrs/releases">litrs's
releases</a>.</em></p>
<blockquote>
<h2>v0.4.2</h2>
<ul>
<li>Fix incorrect byte string value with non-ASCII <code>\xHH</code>
byte string escapes
<ul>

<li><code>ByteStringLit::parse(r#&quot;b&quot;\xff&quot;&quot;#).unwrap().value()</code>
would return <code>[0xc3, 0xbf]</code> instead of
<code>[0xff]</code>.</li>
</ul>
</li>
<li>Remove CR LF normalization to align with spec
<ul>
<li>This is technically a breaking change, but released with a minor
version bump as it just aligns with the specification. It is extremely
unlikely to affect any real world use case.</li>
</ul>
</li>
<li>Fix incorrect error span for out-of-range Unicode escapes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/LukasKalbertodt/litrs/blob/main/CHANGELOG.md">litrs's
changelog</a>.</em></p>
<blockquote>
<h2>[0.4.2] - 2025-07-25</h2>
<ul>
<li>Fix incorrect byte string value with non-ASCII <code>\xHH</code>
byte string escapes
<ul>

<li><code>ByteStringLit::parse(r#&quot;b&quot;\xff&quot;&quot;#).unwrap().value()</code>
would return <code>[0xc3, 0xbf]</code> instead of
<code>[0xff]</code>.</li>
</ul>
</li>
<li>Remove CR LF normalization to align with spec
<ul>
<li>This is technically a breaking change, but released with a minor
version bump as it just aligns with the specification. It is extremely
unlikely to affect any real world use case.</li>
</ul>
</li>
<li>Fix incorrect error span for out-of-range Unicode escapes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1dc8a3e95c"><code>1dc8a3e</code></a>
Bump version to 0.4.2</li>
<li><a
href="8c55d089c6"><code>8c55d08</code></a>
Fix incorrect error span for out-of-range Unicode escapes</li>
<li><a
href="8fe855226f"><code>8fe8552</code></a>
Fix bug with non-ASCII \xHH byte string escapes</li>
<li><a
href="ed84018240"><code>ed84018</code></a>
Refactor escape code to make it more flexible for upcoming
c-strings</li>
<li><a
href="15edbcd138"><code>15edbcd</code></a>
Remove CR LF normalization to align with spec</li>
<li><a
href="d3cf292593"><code>d3cf292</code></a>
Update CI workflow</li>
<li>See full diff in <a
href="https://github.com/LukasKalbertodt/litrs/compare/v0.4.1...v0.4.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=litrs&package-manager=cargo&previous-version=0.4.1&new-version=0.4.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-25 17:38:17 +00:00
sagudev
fc0038743d canvas: Make pixel obtaining methods take &mut GenericDrawTarget (#38264)
This will be needed for vello_cpu. While we could wrap it in RefCell for
inner mut, but that would be less ergonomic and performant.

Testing: Just refactoring, but the code is covered by WPT tests.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-25 13:22:41 +00:00
JoeDow
9d29017c0d layout: optimize the propagation and cleanup of RestyleDamage (#38199)
This change optimizes `RestyleDamage` propagation and cleanup to reduce
unnecessary box reconstruction and relayouts, while preventing damage
from the current reflow affecting subsequent ones. Improvements include:
- For box damage caused by `NodeDamage`, `RestyleDamage::RELAYOUT` is no
longer marked immediately—avoiding erroneous propagation of
`LayoutDamage::RECOLLECT_BOX_TREE_CHILDREN` to descendants during
`RestyleDamage` propagation.
- Clearing damage for nodes whose boxes will be preserved, preventing it
from carrying over to the next reflow and increasing its workload.

Testing: This should not change observable behavior and is thus covered
by existing WPT tests. Although Servo lacks performance test cases,
manual testing shows that this modification reduces reflow time by
nearly 250ms, representing a decrease of approximately 25%.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-07-25 12:10:16 +00:00
Leo Ring
87bbe0b374 servoshell: Properly set RenderingContext size after resizing (#38257)
The tab bar would disappear on resizes, this just reverts the change
which introduced the bug.

Testing: servoshell doesn't currently have tests, so this change does
not come with a test.
Fixes: https://github.com/servo/servo/issues/38255

Signed-off-by: Leo Ring <leoring03@gmail.com>
2025-07-25 12:05:50 +00:00
sagudev
420a5a64a7 canvas: Remove Backend trait (#38262)
After #38214 `Backend` trait is only used as DrawTarget builder and as
type holder. By moving types and DrawTarget creation into
`GenericDrawTarget` trait, we can completely remove `Backend` trait.

Testing: Just refactor, but code is covered by WPT tests.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-25 12:05:03 +00:00
JoeDow
5cd57f9dba layout: Add incremental box tree construction for inline boxes (#38084)
This changes extend the incremental box tree construction for inline
boxes. Since an `InlineItem` can be split into multiple `InlineItem`s by
a block element, the reason such an inline item is marked as damaged may
simply be the removal of the block element or the need to reconstruct
its box tree. Therefore, under the current LayoutDamage design,
theoretically, even damaged inline items might still have some of their
splits reusable. However, based on the principle of simplicity and
effectiveness, this PR only considers reusing undamaged inline boxes.

Testing: This should not change observable behavior and is thus covered
by existing WPT tests.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-07-25 11:49:46 +00:00
Ashwin Naren
3f1e170410 script: Update indexeddb serialization (#38269)
- Dates are now serialized as f64s (like how firefox does it)
- Array buffers are serialized with structured cloning (this is not the
most correct/efficent way to serialize them, however it is currently
better than not supporting array buffers at all)
- Array types are still unimplemented.

Testing: WPT
Fixes: Some panicking

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-25 11:11:22 +00:00
Kenzie Raditya Tirtarahardja
165ede59cd script(webdriver): Send keys set caret at the end of content of text input (#38250)
Remote end step of [element send
keys](https://w3c.github.io/webdriver/#element-send-keys)

> Step 8.2. Set the text insertion caret using [set selection
range](https://w3c.github.io/webdriver/#dfn-set-selection-range) using
current text length for both the start and end parameters.

Also check if the element is already an active element before focusing
(Step 7.7).

Testing:
`./tests/wpt/tests/webdriver/tests/classic/element_send_keys/form_controls.py`

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
Signed-off-by: Kenzie Raditya Tirtarahardja <kenzieradityatirtarahardja18@gmail.com>
Co-authored-by: Euclid Ye <yezhizhenjiakang@gmail.com>
2025-07-25 07:24:48 +00:00
Ashwin Naren
ab78a76da6 script: Implement IDBFactory.cmp (#38260)
Implements `IDBFactory.cmp`, most of the work was done by #38123.

Testing: Covered by WPT (lots of flaky results: the appropriate tests
are in tests/wpt/meta/IndexedDB/idlharness.any.js.ini)
Fixes: None

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-25 05:29:10 +00:00
Jo Steven Novaryo
6cd8578f8b dom: Textual Input UA Shadow Dom (#37527)
Depend on: 
- https://github.com/servo/servo/pull/37427
- https://github.com/servo/servo/pull/37483

Utilize input `type=text` for the display of all textual input. In
which, consist of
https://html.spec.whatwg.org/#the-input-element-as-a-text-entry-widget
and
https://html.spec.whatwg.org/#the-input-element-as-domain-specific-widgets
inputs.

For `password`, `url`, `tel`, and, `email` input, the appearance of
input container is exactly the same as the `text` input. Other types of
textual input simply extends `text` input by adding extra components
inside the container.

Testing: Servo textual input appearance WPT.

---------

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-07-25 04:38:14 +00:00
Jo Steven Novaryo
1d896699a4 script: Implement CSSStyleSheet.replace (#38244)
Implement `CSSStyleSheet.replace`
[#dom-cssstylesheet-replace](https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace)
with disallow modification flag
[#concept-css-style-sheet-disallow-modification-flag](https://drafts.csswg.org/cssom/#concept-css-style-sheet-disallow-modification-flag).

Basically it would behave like `replaceSync`, but we are running it
asynchronously.

Testing: Existing WPT coverage.
Part of: https://github.com/servo/servo/issues/36162

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-07-25 03:05:22 +00:00
Euclid Ye
928934d4b0 servoshell: Respond resize with authentic result and Adjust minimum window size (#38258)
- We no longer pretend that resize is always successful and simplify the
result computation.
- Adjust minimum window size to match other browsers and the test
expectation.
- Restrict some unnecessary access specifier.

Testing: ` ./mach test-wpt -r
"tests\wpt\tests\webdriver\tests\classic\set_window_rect\set.py"
--log-raw "D:\servo test log\set.txt" --product servodriver
{--headless}`
Fixes: #37804 as Task 8 is last task.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-25 02:31:18 +00:00
sagudev
d39e701b46 canvas: Fully stateless backend (#38214)
I think this simplifies canvas backends greatly. Before there were many
(needless) hoops from abstract to concrete (backend) and back, now we
can do most stuff on abstract types.

Testing: Existing WPT tests
Fixes: #38022 

try run: https://github.com/sagudev/servo/actions/runs/16450978211

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-24 18:11:29 +00:00
dependabot[bot]
86d8317460 build(deps): bump prettyplease from 0.2.35 to 0.2.36 (#38254)
Bumps [prettyplease](https://github.com/dtolnay/prettyplease) from
0.2.35 to 0.2.36.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/prettyplease/releases">prettyplease's
releases</a>.</em></p>
<blockquote>
<h2>0.2.36</h2>
<ul>
<li>Preserve braces in <code>use path::to::{self};</code> (<a
href="https://redirect.github.com/dtolnay/prettyplease/issues/121">#121</a>,
thanks <a
href="https://github.com/charlyisidore"><code>@​charlyisidore</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9ae9017fbc"><code>9ae9017</code></a>
Release 0.2.36</li>
<li><a
href="6e28f51132"><code>6e28f51</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/prettyplease/issues/121">#121</a>
from charlyisidore/use-self</li>
<li><a
href="27e359b2d3"><code>27e359b</code></a>
Preserve braces in <code>use std::fmt::{self};</code> <a
href="https://redirect.github.com/dtolnay/prettyplease/issues/120">dtolnay/prettyplease#120</a></li>
<li>See full diff in <a
href="https://github.com/dtolnay/prettyplease/compare/0.2.35...0.2.36">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=prettyplease&package-manager=cargo&previous-version=0.2.35&new-version=0.2.36)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 18:07:14 +00:00
Euclid Ye
3bc104c8e4 servoshell: Add window position for headless window (#38249)
Add virtual `window_position` to headless window so that `moveTo` and
WebDriver window command can work properly.

Testing: `./mach test-wpt -r
"tests\wpt\tests\webdriver\tests\classic\set_window_rect\set.py"
--product servodriver --headless`
Fixes: Task 7 of #37804.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-24 17:57:58 +00:00
dependabot[bot]
4c7797a7ab build(deps): bump owned_ttf_parser from 0.25.0 to 0.25.1 (#38252)
Bumps
[owned_ttf_parser](https://github.com/alexheretic/owned-ttf-parser) from
0.25.0 to 0.25.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/alexheretic/owned-ttf-parser/releases">owned_ttf_parser's
releases</a>.</em></p>
<blockquote>
<h2>0.25.1</h2>
<ul>
<li>Reduce size of published crate archive (148 KiB -&gt; 9 KiB).</li>
<li>Update min <em>ttf-parser</em> to <code>0.25.1</code>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/alexheretic/owned-ttf-parser/blob/main/CHANGELOG.md">owned_ttf_parser's
changelog</a>.</em></p>
<blockquote>
<h1>0.25.1</h1>
<ul>
<li>Reduce size of published crate archive (148 KiB -&gt; 9 KiB).</li>
<li>Update min <em>ttf-parser</em> to <code>0.25.1</code>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3abee95150"><code>3abee95</code></a>
Release 0.25.1</li>
<li><a
href="4759b5593f"><code>4759b55</code></a>
Exclude test data and tools from published packages (<a
href="https://redirect.github.com/alexheretic/owned-ttf-parser/issues/18">#18</a>)</li>
<li>See full diff in <a
href="https://github.com/alexheretic/owned-ttf-parser/compare/0.25.0...0.25.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=owned_ttf_parser&package-manager=cargo&previous-version=0.25.0&new-version=0.25.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 17:54:52 +00:00
dependabot[bot]
d9dee9011e build(deps): bump ipc-channel from 0.20.0 to 0.20.1 (#38253)
Bumps [ipc-channel](https://github.com/servo/ipc-channel) from 0.20.0 to
0.20.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/servo/ipc-channel/releases">ipc-channel's
releases</a>.</em></p>
<blockquote>
<h2>v0.20.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Merge thread_local declarations by <a
href="https://github.com/jschwe"><code>@​jschwe</code></a> in <a
href="https://redirect.github.com/servo/ipc-channel/pull/397">servo/ipc-channel#397</a></li>
<li>Remove receivers from Router IpcReceiverSet on channel close. by <a
href="https://github.com/jdm"><code>@​jdm</code></a> in <a
href="https://redirect.github.com/servo/ipc-channel/pull/400">servo/ipc-channel#400</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/servo/ipc-channel/compare/v0.20.0...v0.20.1">https://github.com/servo/ipc-channel/compare/v0.20.0...v0.20.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="69d1d4bf8c"><code>69d1d4b</code></a>
Publish 0.20.1. (<a
href="https://redirect.github.com/servo/ipc-channel/issues/401">#401</a>)</li>
<li><a
href="4c2cbf8cae"><code>4c2cbf8</code></a>
bug: Drop macOS receivers from OsIpcReceiverSet on channel close. (<a
href="https://redirect.github.com/servo/ipc-channel/issues/400">#400</a>)</li>
<li><a
href="59d6d8099c"><code>59d6d80</code></a>
Merge thread_local declarations (<a
href="https://redirect.github.com/servo/ipc-channel/issues/397">#397</a>)</li>
<li>See full diff in <a
href="https://github.com/servo/ipc-channel/compare/v0.20.0...v0.20.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ipc-channel&package-manager=cargo&previous-version=0.20.0&new-version=0.20.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 17:50:43 +00:00
Kenzie Raditya Tirtarahardja
4b12ae73fe webdriver: Implement element clear (#38208)
Initial Implementation of [Element
Clear](https://w3c.github.io/webdriver/#element-clear).

Testing: `tests/wpt/tests/webdriver/tests/classic/element_clear/`

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
Signed-off-by: Kenzie Raditya Tirtarahardja <kenzieradityatirtarahardja18@gmail.com>
Co-authored-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
2025-07-24 17:49:31 +00:00
Jo Steven Novaryo
1fb782bc38 script: Batch scroll event firing (#38222)
The user interaction triggered `scroll` event was initially implemented
in https://github.com/servo/servo/pull/36687.

In triggering the `scroll` event, the event should be batched and
triggered within update the rendering. This serves as a preparatory PR
for the JS API triggered `scroll` event.

Part of: #31665

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-07-24 17:48:12 +00:00
dependabot[bot]
eeac336518 build(deps): bump the servo-media-related group with 12 updates (#38202)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 17:29:48 +00:00
leo030303
5592061474 Add dimension source attribute to HTMLImageElement (#38152)
HTMLImageElement was missing the dimension source attribute, as step 9
from
[here](https://html.spec.whatwg.org/multipage/images.html#updating-the-source-set)
wasn't implemented. This meant that for an element like this:
```html
<picture>
  <source media="(min-width: 1000px)"
    srcset=""
    width="84" height="29">
  <img
    src=""
    width="25" height="25" alt="Wikimedia Foundation" lang="en" loading="lazy">
</picture>
```

The `width` and `height` attributes of the source tag were being
ignored. This broke stuff like these icons on the main page of
Wikipedia.

**Original:**
<img width="2252" height="229" alt="image"
src="https://github.com/user-attachments/assets/fa3fdd9d-5f91-43d4-bc9d-784b0a836d44"
/>


**Fixed:**
<img width="2252" height="229" alt="image"
src="https://github.com/user-attachments/assets/6f4ca4ae-e764-4394-ac31-9a92bbb456ab"
/>


Testing: If there isn't an existing test that covers this I'll make one,
waiting for the github runner to tell me whether this is covered and
then I'll amend the PR

---------

Signed-off-by: Leo Ring <leoring03@gmail.com>
Signed-off-by: Xiaocheng Hu <hu.xiaocheng@huawei.com>
Signed-off-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
Co-authored-by: Xiaocheng Hu <hu.xiaocheng@huawei.com>
Co-authored-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
2025-07-24 13:40:44 +00:00
Josh Matthews
be38bd4636 servo: Track async webview focus operations (#38243)
https://github.com/servo/servo/pull/38160 added a webdriver-specific API
to support waiting on focus operations to complete. These changes
replace that with a generalized pattern, where a unique ID is created
for each focus operation and the embedder can receive notifications
about each focus operation when it is complete, regardless of whether
the focus was actually changed.

Testing: Existing test coverage from
https://github.com/servo/servo/pull/38160/ is unchanged.

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-07-24 06:01:11 +00:00
Euclid Ye
0b8986c8da servoshell: Consider window decorations when handling resize requests from web content (#38174)
Also fix some docs. This is used by JS `resizeTo`, `resizeBy` and
webdriver [set window
rect](https://w3c.github.io/webdriver/#set-window-rect).

Testing: Can now pass more tests for headed window.
Fixes: Well.. Originally the attempt is to address
https://github.com/servo/servo/issues/38093#issuecomment-3092284104. But
it turns out as a more general problem to be fixed in another PR.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-24 05:42:04 +00:00
Shubham Gupta
f62aa8edc2 Rename viewport_zoom to pinch_zoom (#38194)
This change is in accordance with highlighting the usage of various zoom
options.

`pinch_zoom`: `Mobile-style` zoom using pinch gesture
 `page_zoom`: `Desktop-style` zoom (`Ctrl` + `+`/`-`)

It just renames the variable `viewport_zoom` to `pinch_zoom` for better
clarity

Testing: Testing not required, as it's just renaming of variable .

---------

Signed-off-by: Shubham Gupta <shubham13297@gmail.com>
Signed-off-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
Co-authored-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
2025-07-24 05:29:18 +00:00
Kenzie Raditya Tirtarahardja
d95dd69e3c script(webdriver): Check if element is keyboard interactable in element send keys (#38235)
Step 7.6 of remote end steps of [Element Send
Keys](https://w3c.github.io/webdriver/#element-send-keys)
> If element is not
[keyboard-interactable](https://w3c.github.io/webdriver/#dfn-keyboard-interactable),
return [error](https://w3c.github.io/webdriver/#dfn-error) with [error
code](https://w3c.github.io/webdriver/#dfn-error-code) [element not
interactable](https://w3c.github.io/webdriver/#dfn-element-not-interactable).

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-24 04:32:46 +00:00
Euclid Ye
f726737f24 Reduce redundancy in servoshell webdriver message forwarding and add log for script::handle_send_keys_non_typeable (#38238)
There was a new commit for after
https://github.com/servo/servo/pull/38189#discussion_r2224805172, but
that does not seem to update/stop MQ.
Also removed `forward_webdriver_command` in servoshell according to
https://github.com/servo/servo/pull/38212#discussion_r2221854327.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-24 04:22:04 +00:00
Narfinger
6ace7ad577 OHOS: Clean up some compile warnings (#38240)
This cleans up some compile warnings about unused functions.

Testing: Does not change functionality, OHOS and Linux still compile
(hopefully the other builds too).

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Co-authored-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-07-24 03:29:49 +00:00
lumiscosity
bb5cb57099 Remove unused resources (#38245)
As previously discussed in #38229. To recap: `badcert.jpg` was made
unused in 8422dac811 and `tumbeast.png`
and its associated file of `not-found.html` were superseded at some
point by a message in `neterror.html` (if it were still used, it'd
surely be
[embedded](https://github.com/servo/servo/blob/main/components/shared/embedder/resources.rs).)

This PR also removes two unnecessary spaces inside of the new tab's CSS
file, ensuring a consistent 2 space indentation in the file and saving 2
bytes.

Testing: Unnecessary, as this commit only removes unused assets (and
fixes one style nit).

Signed-off-by: maple! <averyrudelphe@gmail.com>
2025-07-24 00:34:15 +00:00
lumiscosity
067631037c Optimize PNG images in resources folder (#38229)
Losslessly optimizes the PNG images in the `resources` folder with:
```
oxipng -o max -a -s
oxipng -o max --zopfli -a -s
```

Tests are unaffected by this commit in order to avoid potential
breakages.

As an aside: shouldn't `tumbeast.png` and `badcert.jpg` be removed? For
`tumbeast.png`, the related page `resources/not-found.html` doesn't seem
to be reachable by any means (the usual dry error message is used
instead, as far as my testing goes, neither `about:not-found` nor
`servo:not-found` work), and the only use of `badcert.jpg` (in
`badcert.html`) got removed in 8422dac811.

Testing: Unnecessary. Since the optimized images haven't changed their
extension, so long as Servo's PNG parsing is correct, there should be no
change aside from reduced binary sizes.

Signed-off-by: maple! <averyrudelphe@gmail.com>
2025-07-23 18:43:09 +00:00
dependabot[bot]
1f23382d18 build(deps): bump hyper-util from 0.1.15 to 0.1.16 (#38241)
Bumps [hyper-util](https://github.com/hyperium/hyper-util) from 0.1.15
to 0.1.16.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/hyperium/hyper-util/blob/master/CHANGELOG.md">hyper-util's
changelog</a>.</em></p>
<blockquote>
<h1>0.1.16 (2025-07-22)</h1>
<ul>
<li>Add <code>impl Clone</code> for <code>proxy::Tunnel</code>
service.</li>
<li>Fix <code>proxy::Matcher</code> to detect SOCKS4 schemes.</li>
<li>Fix <code>legacy::Client</code> pool idle checker to trigger less
aggresively, saving CPU.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5cefcd804e"><code>5cefcd8</code></a>
v0.1.16</li>
<li><a
href="0d4ca3f50d"><code>0d4ca3f</code></a>
chore(cargo): move tokio/net feature to client (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/218">#218</a>)</li>
<li><a
href="89b2cffaf0"><code>89b2cff</code></a>
fix(proxy): Fix SOCKS4 proxy protocol parsing and matching (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/219">#219</a>)</li>
<li><a
href="39fd779bb2"><code>39fd779</code></a>
tests(client): fix flaky pool test (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/220">#220</a>)</li>
<li><a
href="24714a39ca"><code>24714a3</code></a>
fix(client): cap pool idle interval to a minimum (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/217">#217</a>)</li>
<li><a
href="f0dcda37f8"><code>f0dcda3</code></a>
deps: allow socket2 0.6 (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/213">#213</a>)</li>
<li><a
href="dd63305975"><code>dd63305</code></a>
refactor(client): simplify pool idle task with async/await (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/216">#216</a>)</li>
<li><a
href="afd758b265"><code>afd758b</code></a>
fix(client): don't spawn pool idle interval if timeout is 0 (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/215">#215</a>)</li>
<li><a
href="9f7a5e01e3"><code>9f7a5e0</code></a>
feat(client): implement Clone for Tunnel (<a
href="https://redirect.github.com/hyperium/hyper-util/issues/212">#212</a>)</li>
<li>See full diff in <a
href="https://github.com/hyperium/hyper-util/compare/v0.1.15...v0.1.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hyper-util&package-manager=cargo&previous-version=0.1.15&new-version=0.1.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 17:29:00 +00:00
dependabot[bot]
13544ae0d2 build(deps): bump aws-lc-rs from 1.13.2 to 1.13.3 (#38242)
Bumps [aws-lc-rs](https://github.com/aws/aws-lc-rs) from 1.13.2 to
1.13.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/aws/aws-lc-rs/releases">aws-lc-rs's
releases</a>.</em></p>
<blockquote>
<h2>aws-lc-rs v1.13.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Rename ML-DSA by <a
href="https://github.com/justsmth"><code>@​justsmth</code></a> in <a
href="https://redirect.github.com/aws/aws-lc-rs/pull/844">aws/aws-lc-rs#844</a></li>
</ul>
<h2>Other Merged PRs</h2>
<ul>
<li>Skip aws-lc-rs &quot;publish dryrun&quot; job when -sys crates not
published by <a
href="https://github.com/justsmth"><code>@​justsmth</code></a> in <a
href="https://redirect.github.com/aws/aws-lc-rs/pull/840">aws/aws-lc-rs#840</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/aws/aws-lc-rs/compare/v1.13.2...v1.13.3">https://github.com/aws/aws-lc-rs/compare/v1.13.2...v1.13.3</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="78258d2b58"><code>78258d2</code></a>
Fix publish dryrun when -sys crates update (<a
href="https://redirect.github.com/aws/aws-lc-rs/issues/840">#840</a>)</li>
<li><a
href="3efb4fa9d2"><code>3efb4fa</code></a>
Rename ML-DSA (<a
href="https://redirect.github.com/aws/aws-lc-rs/issues/844">#844</a>)</li>
<li>See full diff in <a
href="https://github.com/aws/aws-lc-rs/compare/v1.13.2...v1.13.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=aws-lc-rs&package-manager=cargo&previous-version=1.13.2&new-version=1.13.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 17:27:15 +00:00
Euclid Ye
0e4b2bfef0 cargo: Remove unused dependency (#38239)
Remove unused `Cargo.toml` dependency to reduce binary size.

Testing: Can still compile in different platforms.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-23 13:32:50 +00:00
shuppy
52f53f61e1 script: Add support for creating globals in isolated compartments (#38236)
to use the [SpiderMonkey Debugger
API](https://firefox-source-docs.mozilla.org/js/Debugger/), we need to
call it from an internal debugger script that we will supply. this
script must run in the same runtime as the debuggee(s), but in a
separate
[compartment](https://udn.realityripple.com/docs/Mozilla/Projects/SpiderMonkey/Compartments)
([more
details](https://hacks.mozilla.org/2020/03/future-proofing-firefoxs-javascript-debugger-implementation/)).

when creating two globals in the same runtime, Servo will generally try
to reuse an existing compartment for the second global, and there are
other places in script where we rely on this reuse. but for the debugger
script, we can’t do this.

this patch adds a `use_system_compartment` flag to global object
descriptors, which isolates the global in its own compartment in both
possible directions:
- it forces the global to create a new compartment, preventing the reuse
of any debuggee’s compartment
- it fails the check in select_compartment(), preventing future
debuggees from reusing the global’s compartment

Testing: manually tested working in devtools debugger patch (#37667),
where it will undergo automated tests

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
2025-07-23 11:34:37 +00:00
Narfinger
d9f8fc505f ohos CI: Add the build profile to speedometer results (#38231)
OHOS CI now has the build profile before the test cases to distringuish
dev, release, production and similar.

Testing: Tested on CI with a run here:
https://github.com/Narfinger/servo/actions/runs/16465984131/job/46543984398
Fixes: Same name in speedometer CI will have very different speeds
because of different profiles. This will fix this behaviour.

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2025-07-23 10:09:46 +00:00
Kenzie Raditya Tirtarahardja
0970a99b7c webdriver: Implement element send keys command for non-typeable form control (#38189)
Implement Step 8 of remote end steps of [Element Send
keys](https://w3c.github.io/webdriver/#element-send-keys), specifically
for non-typeable form control.

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-23 09:11:44 +00:00
Jo Steven Novaryo
3cb16eb864 Reland Input type=text Shadow DOM With Performance Improvement (#37483)
Depends on #37427.

In addition to the changes introduced by
https://github.com/servo/servo/pull/37065, there are several performance
improvements and nits as follows:
- Use the internal pseudo element for style matching, this will reduce
the performance regression by ~66%.
- Manual construction of the `Text` node inside a text container. This
is followed by the modification of the inner `Text` node instead of
using `SetTextContent` which is more expensive.
- Use `implemented_pseudo_element` instead of
`text_control_inner_editor` `NodeFlag` to handle the special cases that
these elements should follow, specifically the:
  - focus delegation workaround;
  - selections; and
  - line height resolving.
- More documentation.

Servo's side of: https://github.com/servo/stylo/pull/217

Testing: No new unexpected WPT failure, except for the one introduced by
https://github.com/servo/servo/pull/37065/.
Fixes: #36307 #37205

---------

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
2025-07-23 09:08:24 +00:00
Jo Steven Novaryo
f523445fc3 script: Implement DocumentOrShadowDOM.adoptedStylesheet with FrozenArray (#38163)
Spec:
https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets

Implement `DocumentOrShadowDOM.adoptedStylesheet`. Due to
`ObservableArray` being a massive issue on its own, it will be as it was
a `FrozenArray` at first. This approach is similar to how Gecko
implement adopted stylesheet. See
https://phabricator.services.mozilla.com/D144547#change-IXyOzxxFn8sU.

All of the changes will be gated behind a preference
`dom_adoptedstylesheet_enabled`.

Adopted stylesheet is implemented by adding the setter and getter of it.
While the getter works like a normal attribute getter, the setter need
to consider the inner working of document and shadow root StylesheetSet,
specifically the ordering and the invalidations. Particularly for
setter, we will clear all of the adopted stylesheet within the
StylesheetSet and readd them. Possible optimization exist, but the focus
should be directed to implementing `ObservableArray`.

More context about the implementations
https://hackmd.io/vtJAn4UyS_O0Idvk5dCO_w.

Testing: Existing WPT Coverage
Fixes: https://github.com/servo/servo/issues/37561

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-07-23 08:16:01 +00:00
Euclid Ye
d2e5137201 constellation: Eliminate long-standing inactive code path for WebDriverData (#38221)
Cleaning up some dead code which has been here for 9 years. They are
never detected by Lint because we still initialize them as `None` but
never change afterwards.

Testing: No regression.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-07-23 06:50:20 +00:00
TIN TUN AUNG
7f6d4825cf Ohos: include build id in ohos built binary (#38215)
add link-arg for ohos to include build id in its ELF binary. This could
enable us to do debugging and size checking more easily.

Testing: No test needed.
Fixes: N/A

cc @jschwe

Signed-off-by: rayguo17 <tin.tun.aung1@huawei.com>
2025-07-23 04:54:21 +00:00
Jonathan Schwender
6417d3077b ohos CI: Simplify getting the model name (#38219)
Same output, but much easier and faster than sifting through the whole
output of hdc bugreport.

Testing: CI change: `./mach try`
[run](https://github.com/servo/servo/actions/runs/16460283107)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-07-23 04:25:53 +00:00
JoeDow
b8a1df2bc2 script: Use NodeDamage::ContentOrHeritage for slot changes (#38198)
This change aims to reduce the scope of incremental box tree
construction. Previously, when children of slot node changed, the slot
would always be marked as requiring box tree reconstruction; now, it is
adjusted to only mark the slot node as needing to recollect its box tree
children.

Testing: This should not change observable behavior and is thus covered
by existing WPT tests.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-07-23 03:36:32 +00:00
Euclid Ye
4ff6b1d4a7 Implement setting position through webdriver for headed window (#38209)
Previously, we pretend we are able to set position in response. Now we
can really do it.

Testing: Able to set position accurately when tested locally.
Fixes: Task 5 of #37804.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-22 18:26:37 +00:00
dependabot[bot]
cff48d4910 build(deps): bump io-uring from 0.7.8 to 0.7.9 (#38217)
Bumps [io-uring](https://github.com/tokio-rs/io-uring) from 0.7.8 to
0.7.9.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/tokio-rs/io-uring/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=io-uring&package-manager=cargo&previous-version=0.7.8&new-version=0.7.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 18:16:40 +00:00
Kenzie Raditya Tirtarahardja
9e29ca2047 WebDriver: Will Send Keys add documentation and modularize input file (#38165)
Add comment for better documentation of `will_send_keys` and extract
file input handling to a function.

Testing: No behaviour change

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-22 17:30:22 +00:00
sagudev
cd340fa8b9 canvas: Make script/canvas thread boundry mostly stateless (#38164)
This PR removes all `Set*` commands as we will send needed options on
each request. This will allow us to remove most of state handling from
canvas paint thread (currently it's still stateful).

Testing: Existing WPT tests
work towards #38022

try run: https://github.com/sagudev/servo/actions/runs/16413823963

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-22 12:30:15 +00:00
JoeDow
8a1cc69717 Layout: minor optimizations and bugfix for non-functional details (#38197)
This change adds some minor optimizations and bugfix for non-functional
details with seperated commits:
- fix the omission that stop use `Rayon` in single-thread mode
- add trace for incremental box tree construction
- fix the bug that failed to skip reflow entirely when there is no need
for `restyle` and a fragment tree has already been built.
- add trace for stylist preparation during reflow. In certain scenarios,
this phase might take up a significant amount of time, such as when
there are a large number of shadow trees.

Testing: This should not change observable behavior and is thus covered
by existing WPT tests.

---------

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
2025-07-22 10:56:52 +00:00
Kenzie Raditya Tirtarahardja
19a121e829 WebDriver: Clean Up and Re-Update WPT Expectation (#38192)
Previously, we have failing many subtests in `*/user_prompt.py` with
prefix `capabilities0-*`. The name of the subtest was probably changed
before, hence the failing expectation is not removed when we do `./mach
update-wpt`. Here we reupdate the expectation.

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-22 10:28:38 +00:00
Euclid Ye
57fc569818 Remove Webdriver Server's access to Constellation (#38212)
`WebDriverCommandMsg::TakeScreenshot` was the last thing blocking us
from removing constellation access in webdriver server. Now we can
happily remove it.

Surprisingly, this reduces binary size by 185KB for release profile in
Windows. #37737 removes a net total of 300 lines, but only reduced 98KB.

Testing: No regression after testing.

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-22 10:17:19 +00:00
sagudev
61df7ab127 chore: Update kurbo to 0.11.3 (#38210)
Some of the code we wrote is now also in upstream, mainly
`BezPath.current_position` and euclid types conversions.

Testing: Code is covered by existing WPT tests.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Co-authored-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
2025-07-22 08:23:13 +00:00
Jonathan Schwender
97f544aa20 dom: Optimize IFrameCollection::validate (#38196)
The `IFrameCollection` was previously rebuilt too often. This PR tries
to address the todo, to only rebuild the IFrameCollection when
necessary.

Testing: `CSS Selector Invalidation: :has() invalidation should not be
O(n^2)` wpt-test changed status from timeout to failed.
Fixes: #38131 (IFrameCollection::validate accounts for 30% of script
time in DOM heavy scenarios)


Co-authored-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
2025-07-22 03:54:16 +00:00
dependabot[bot]
03ab419793 build(deps): bump bytemuck_derive from 1.9.3 to 1.10.0 (#38205)
Bumps [bytemuck_derive](https://github.com/Lokathor/bytemuck) from 1.9.3
to 1.10.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Lokathor/bytemuck/blob/main/changelog.md">bytemuck_derive's
changelog</a>.</em></p>
<blockquote>
<h1><code>bytemuck</code> changelog</h1>
<h2>1.23.1</h2>
<ul>
<li>Added a windows-only <code>ZeroableInOption</code> impl for
&quot;stdcall&quot; functions.</li>
</ul>
<h2>1.23</h2>
<ul>
<li><code>impl_core_error</code> crate feature adds
<code>core::error::Error</code> impl.</li>
<li>More <code>ZeroableInOption</code> impls.</li>
</ul>
<h2>1.22</h2>
<ul>
<li>Add the <code>pod_saturating</code> feature, which adds
<code>Pod</code> impls for <code>Saturating&lt;T&gt;</code>
when <code>T</code> is already <code>Pod</code>.</li>
<li>A bump in the minimum <code>bytemuck_derive</code> dependency from
1.4.0 to 1.4.1 to
avoid a bug if you have a truly ancient <code>cargo.lock</code> file
sitting around.</li>
<li>Adds <code>Send</code> and <code>Sync</code> impls to
<code>BoxBytes</code>.</li>
</ul>
<h2>1.21</h2>
<ul>
<li>Implement <code>Pod</code> and <code>Zeroable</code> for
<code>core::arch::{x86, x86_64}::__m512</code>, <code>__m512d</code> and
<code>__m512i</code> without nightly.
Requires Rust 1.72, and is gated through the <code>avx512_simd</code>
cargo feature.</li>
<li>Allow the use of <code>must_cast_mut</code> and
<code>must_cast_slice_mut</code> in const contexts.
Requires Rust 1.83, and is gated through the
<code>must_cast_extra</code> cargo feature.</li>
<li>internal: introduced the <code>maybe_const_fn</code> macro that
allows defining some function
to be const depending upon some <code>cfg</code> predicate.</li>
</ul>
<h2>1.20</h2>
<ul>
<li>New functions to allocate zeroed <code>Arc</code> and
<code>Rc</code>. Requires Rust 1.82</li>
<li><code>TransparentWrapper</code> impls for
<code>core::cmp::Reverse</code> and
<code>core::num::Saturating</code>.</li>
<li>internal: Simplified the library's <code>fill_zeroes</code> calls to
<code>write_bytes</code></li>
</ul>
<h2>1.19</h2>
<ul>
<li>Adds the <code>#[track_caller]</code> attribute to functions which
may panic.</li>
</ul>
<h2>1.18</h2>
<ul>
<li>Adds the <code>latest_stable_rust</code> cargo feature, which is a
blanket feature that turns all other features on that are both sound and
compatible with Stable rust.</li>
</ul>
<h2>1.17.1</h2>
<ul>
<li>Adds <code>#[repr(C)]</code> to the <code>union Transmute&lt;A,
B&gt;</code> type that's used internally
for most of the transmutations.</li>
</ul>
<h2>1.17.0</h2>
<ul>
<li>Makes the <code>must_cast</code> versions of the by-value and by-ref
casts be <code>const</code>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/Lokathor/bytemuck/commits/bytemuck_derive-v1.10.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bytemuck_derive&package-manager=cargo&previous-version=1.9.3&new-version=1.10.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 20:31:12 +00:00
dependabot[bot]
0f55aa292e build(deps): bump serde_json from 1.0.140 to 1.0.141 (#38204)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.140 to
1.0.141.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/json/releases">serde_json's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.141</h2>
<ul>
<li>Optimize string escaping during serialization (<a
href="https://redirect.github.com/serde-rs/json/issues/1273">#1273</a>,
thanks <a
href="https://github.com/conradludgate"><code>@​conradludgate</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6843c3660e"><code>6843c36</code></a>
Release 1.0.141</li>
<li><a
href="6e2c21063a"><code>6e2c210</code></a>
Touch up PR 1273</li>
<li><a
href="623d9b47cf"><code>623d9b4</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1273">#1273</a>
from conradludgate/optimise-string-escaping</li>
<li><a
href="de70b7db1f"><code>de70b7d</code></a>
use unreachable_unchecked for escape table. use a second match to
roundtrip E...</li>
<li><a
href="f2d940dd54"><code>f2d940d</code></a>
replace start index with bytes slice reference</li>
<li><a
href="cd55b5a0ff"><code>cd55b5a</code></a>
Ignore mismatched_lifetime_syntaxes lint</li>
<li><a
href="c1826ebccc"><code>c1826eb</code></a>
Pin nightly toolchain used for miri job</li>
<li><a
href="8a56cfa6d0"><code>8a56cfa</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1248">#1248</a>
from jimmycathy/master</li>
<li><a
href="af3d80de56"><code>af3d80d</code></a>
chore: fix typo</li>
<li>See full diff in <a
href="https://github.com/serde-rs/json/compare/v1.0.140...v1.0.141">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.140&new-version=1.0.141)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 20:25:16 +00:00
dependabot[bot]
2d34a2b13c build(deps): bump async-process from 2.3.1 to 2.4.0 (#38203)
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=async-process&package-manager=cargo&previous-version=2.3.1&new-version=2.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 20:24:19 +00:00
dependabot[bot]
50603e5074 build(deps): bump libredox from 0.1.4 to 0.1.6 (#38201)
Bumps libredox from 0.1.4 to 0.1.6.


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=libredox&package-manager=cargo&previous-version=0.1.4&new-version=0.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 20:06:42 +00:00
Euclid Ye
b0a29393a9 WebDriver: Wait focus to complete when switching window (#38160)
Previously the webdriver do not wait for focus to complete, which can
cause some instability.

No matter interact as human or webdriver, the focus chain always goes
as: Embedder forwards -> Constellation (do some updates) -> Embedder (do
some updates).

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-21 04:18:21 +00:00
webbeef
7c96084298 Adjust CSP for top-level image documents (#38186)
Allows loading of document images even when they have a sandbox CSP rule
to align with other browsers.

Testing: No wpt failures:
https://github.com/webbeef/servo/actions/runs/16403138806
Fixes: #38180

Signed-off-by: webbeef <me@webbeef.org>
2025-07-20 21:07:47 +00:00
Tim van der Lippe
e24acc5fe5 Add warning for reporting CSP violations (#38181)
This should help debug cases like #38180

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-07-20 16:06:04 +00:00
Josh Matthews
b8a6600e9a compositing: Remove unused dependency. (#38179)
There are no uses of `net` within the `compositing` crate.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-07-20 16:05:31 +00:00
Tim van der Lippe
20386d9854 Forward CSP violations from load_whole_resource to parent event loop (#38048)
Any CSP violations happening when loading a worker should be reported
on the global of the document that creates the worker. Since workers
run in different threads, we can't pass in this parent global into
the worker global scope. Instead, we need to send a message to the
parent event loop to report it on the correct global.

Part of https://github.com/servo/servo/issues/4577
Fixes https://github.com/servo/servo/issues/37027

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-07-20 16:00:28 +00:00
Servo WPT Sync
772c84633e Sync WPT with upstream (20-07-2025) (#38178)
Automated downstream sync of changes from upstream as of 20-07-2025
[no-wpt-sync]

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
2025-07-20 15:11:10 +00:00
Alex Touchet
7ab0d91109 Update rustc-hash and rustix (#38173)
Update rustc-hash and rustix.

Testing: No tests for dependency updates.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-07-18 22:43:20 +00:00
Kingsley Yung
0537c29064 devtools: Use request destination as cause_type in NetworkEventActor (#38162)
The cause_type in NetworkEventActor was derived from a hard-coded
pattern match on the request URL file extension.

This patch replaces the hard-coded pattern matching with the Destination
field of Request, to provide a better alignment with the Fetch
specification.

Testing: Updated unit tests.
Fixes: #38151

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
2025-07-18 22:20:26 +00:00
dependabot[bot]
a52f9fd9a9 build(deps): bump webpki-roots from 1.0.1 to 1.0.2 (#38172)
Bumps [webpki-roots](https://github.com/rustls/webpki-roots) from 1.0.1
to 1.0.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rustls/webpki-roots/releases">webpki-roots's
releases</a>.</em></p>
<blockquote>
<h2>1.0.2</h2>
<ul>
<li>Add &quot;TrustAsia TLS ECC Root CA&quot; and &quot;TrustAsia TLS
RSA Root CA&quot; <a
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1972384">https://bugzilla.mozilla.org/show_bug.cgi?id=1972384</a></li>
<li>Add &quot;SwissSign RSA TLS Root CA 2022 - 1&quot; <a
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1845047">https://bugzilla.mozilla.org/show_bug.cgi?id=1845047</a></li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Update dependencies by <a
href="https://github.com/djc"><code>@​djc</code></a> in <a
href="https://redirect.github.com/rustls/webpki-roots/pull/102">rustls/webpki-roots#102</a></li>
<li>1.0.2: track July 2025 CCADB changes by <a
href="https://github.com/ctz"><code>@​ctz</code></a> in <a
href="https://redirect.github.com/rustls/webpki-roots/pull/103">rustls/webpki-roots#103</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/rustls/webpki-roots/compare/v/1.0.1...v/1.0.2">https://github.com/rustls/webpki-roots/compare/v/1.0.1...v/1.0.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="87222f0639"><code>87222f0</code></a>
1.0.2: track July 2025 CCADB changes</li>
<li><a
href="974587e439"><code>974587e</code></a>
Check MSRV in CI</li>
<li><a
href="3bd9fa51c5"><code>3bd9fa5</code></a>
Version Cargo.lock</li>
<li><a
href="769e56b014"><code>769e56b</code></a>
Replace rustls-pemfile with PemObject API</li>
<li><a
href="a6398002a2"><code>a639800</code></a>
Scope certificate generation input</li>
<li><a
href="3e19303eb0"><code>3e19303</code></a>
Upgrade to rcgen 0.14</li>
<li><a
href="f3ea6fec79"><code>f3ea6fe</code></a>
Move all dependencies into workspace</li>
<li><a
href="944d375fb3"><code>944d375</code></a>
Upgrade to webpki 0.103</li>
<li>See full diff in <a
href="https://github.com/rustls/webpki-roots/compare/v/1.0.1...v/1.0.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webpki-roots&package-manager=cargo&previous-version=1.0.1&new-version=1.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 18:21:56 +00:00
dependabot[bot]
501bd98121 build(deps): bump clipboard-win from 5.4.0 to 5.4.1 (#38171)
Bumps [clipboard-win](https://github.com/DoumanAsh/clipboard-win) from
5.4.0 to 5.4.1.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DoumanAsh/clipboard-win/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clipboard-win&package-manager=cargo&previous-version=5.4.0&new-version=5.4.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 18:01:55 +00:00
dependabot[bot]
8d9d2ee2c2 build(deps): bump sysinfo from 0.36.0 to 0.36.1 (#38170)
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.36.0
to 0.36.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md">sysinfo's
changelog</a>.</em></p>
<blockquote>
<h1>0.36.1</h1>
<ul>
<li>Linux: Improve processor CPU usage computation.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fc31b411ee"><code>fc31b41</code></a>
Merge pull request <a
href="https://redirect.github.com/GuillaumeGomez/sysinfo/issues/1559">#1559</a>
from GuillaumeGomez/update</li>
<li><a
href="20d3d295f9"><code>20d3d29</code></a>
Update crate version to <code>0.36.1</code></li>
<li><a
href="400387ee1c"><code>400387e</code></a>
Update CHANGELOG for <code>0.36.1</code> version</li>
<li><a
href="d46ed5d18c"><code>d46ed5d</code></a>
Merge pull request <a
href="https://redirect.github.com/GuillaumeGomez/sysinfo/issues/1558">#1558</a>
from GuillaumeGomez/improve-processor-cpu-usage-comp...</li>
<li><a
href="44e64c54fb"><code>44e64c5</code></a>
Improve processors CPU usage computation on linux</li>
<li>See full diff in <a
href="https://github.com/GuillaumeGomez/sysinfo/compare/v0.36.0...v0.36.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sysinfo&package-manager=cargo&previous-version=0.36.0&new-version=0.36.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 17:57:06 +00:00
dependabot[bot]
58fee976d9 build(deps): bump cc from 1.2.29 to 1.2.30 (#38169)
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.2.29 to 1.2.30.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cc-rs/releases">cc's
releases</a>.</em></p>
<blockquote>
<h2>cc-v1.2.30</h2>
<h3>Other</h3>
<ul>
<li>define _REENTRANT by default (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1496">#1496</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md">cc's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.29...cc-v1.2.30">1.2.30</a>
- 2025-07-18</h2>
<h3>Other</h3>
<ul>
<li>define _REENTRANT by default (<a
href="https://redirect.github.com/rust-lang/cc-rs/pull/1496">#1496</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9df3cccb53"><code>9df3ccc</code></a>
chore: release v1.2.30 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1498">#1498</a>)</li>
<li><a
href="59854df166"><code>59854df</code></a>
Solarish-ish: define _REENTRANT by default (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1496">#1496</a>)</li>
<li><a
href="c3190115ac"><code>c319011</code></a>
Update rusqlite requirement from 0.36.0 to 0.37.0 (<a
href="https://redirect.github.com/rust-lang/cc-rs/issues/1494">#1494</a>)</li>
<li>See full diff in <a
href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.29...cc-v1.2.30">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cc&package-manager=cargo&previous-version=1.2.29&new-version=1.2.30)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 17:56:21 +00:00
Simon Wülker
329e8cfbb3 Implement allow_declarative_shadow_roots for async html parser (#38150)
This method was previously unimplemented, so attaching declarative
shadow roots was always allowed.

Testing: CI does not run with the async html parser. Try run:
https://github.com/simonwuelker/servo/actions/runs/16350173930/job/46196332473
Part of https://github.com/servo/servo/issues/37418

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-07-18 12:23:01 +00:00
Kenzie Raditya Tirtarahardja
b4b9e2c515 Webdriver: Forward ErrorStatus for take element screenshot (#38134)
Based on
[spec](tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini),
we should not expect the error to only be
`ErrorStatus::StaleElementReference`.

Testing:
`tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini`

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-18 06:42:51 +00:00
Kenzie Raditya Tirtarahardja
39144bb013 Webdriver: Allow error when selecting file at ElementSendKeys (#38158)
Based on [spec](https://w3c.github.io/webdriver/#element-send-keys),

> 5. Verify that each file given by the user exists. If any do not,
return [error](https://w3c.github.io/webdriver/#dfn-error) with [error
code](https://w3c.github.io/webdriver/#dfn-error-code) [invalid
argument](https://w3c.github.io/webdriver/#dfn-invalid-argument).

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-18 06:40:40 +00:00
Ashwin Naren
a91625a332 Proper compare implementation for IndexedDBKeyType (#38123)
This is needed by the IndexedDBKeyRange implementation.

Doesn't fix anything since it isn't used at the moment.

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-18 06:26:47 +00:00
sagudev
a070f24450 canvas: Move current_default_path to script CanvasState (#38114)
This PR moves current path handling from canvas paint thread to script's
`CanvasState`. Because we used manual impl for arcto and some prechecks
for early fail before sending IPC we also needed to fix some bugs in
Path impl.

Testing: Existing WPT tests
work towards #38022
try run: https://github.com/sagudev/servo/actions/runs/16316090028

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-18 05:45:53 +00:00
Jo Steven Novaryo
bbed6cddcd css: Refactor StyleSheetInDocument owner (#38136)
Refactor `documentotshadowroot::StyleSheetInDocument`, renaming it into
`ServoStylesheetInDocument` to avoid confusion with Stylo's
`StylesheetInDocument` trait.

To support constructed stylesheet. The `ServoStylesheetInDocument.owner`
would contains enum of:
- `Dom<Element>` - for stylesheet parsed from an element.
- `Dom<CSSStylesheet>` - for constructed stylesheet.

Testing: No WPT regression.
Fixes: #38133

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-07-18 05:39:09 +00:00
Sebastian C
d671f58078 Add CookieStore pref and add baseline test expectations (#38154)
This adds a preference to control the Cookie Store feature. Also enables
the Cookie Store WPT tests with the preference and adds the expectations
as a baseline. These tests are expected to be failing because we have
not implemented the Cookie Store yet.

Testing: Enables new WPT tests.

---------

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
2025-07-18 02:21:34 +00:00
Alex Touchet
adb804772b Update tempfile (#38153)
Update tempfile.

Testing: No tests for dependency update.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-07-17 21:38:39 +00:00
batu_hoang
f0e10e63e2 webdriver: Implement send alert text (#38140)
Implement webdriver `SendAlertText` command

Tests:
https://github.com/longvatrong111/servo/actions/runs/16342669929
https://github.com/longvatrong111/servo/actions/runs/16342671477

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
2025-07-17 21:24:50 +00:00
Jo Steven Novaryo
3ce95b2ba5 script: Impl safe from_jsval wrapper (#38149)
Implement `SafeFromJSValConvertible`, a safe wrapper for
`ToJSValConvertible`. And, replace unsafe `ToJSValConvertible` with
`SafeFromJSValConvertible` in `script/dom` to reduce the amount of
unsafe code in `script`.

This would support the implementation of `AdoptedStylesheet` where we
will need to have a setter/getter of sequence, that was implemented by
`any` types.

Part of https://github.com/servo/servo/issues/37951

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
2025-07-17 19:32:36 +00:00
dependabot[bot]
dcae2dd9fd build(deps): bump ab_glyph from 0.2.30 to 0.2.31 (#38148)
Bumps [ab_glyph](https://github.com/alexheretic/ab-glyph) from 0.2.30 to
0.2.31.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/alexheretic/ab-glyph/releases">ab_glyph's
releases</a>.</em></p>
<blockquote>
<h2>ab-glyph-0.2.31</h2>
<ul>
<li>Add &quot;gvar-alloc&quot; feature enabled by default (activates
<em>ttf-parser</em> &quot;gvar-alloc&quot; feature).
Provides full gvar table support.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f963bcaf08"><code>f963bca</code></a>
Release ab_glyph 0.2.31</li>
<li><a
href="a216ede621"><code>a216ede</code></a>
Add &quot;gvar-alloc&quot; feature enabled by default (<a
href="https://redirect.github.com/alexheretic/ab-glyph/issues/118">#118</a>)</li>
<li>See full diff in <a
href="https://github.com/alexheretic/ab-glyph/compare/ab-glyph-0.2.30...ab-glyph-0.2.31">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ab_glyph&package-manager=cargo&previous-version=0.2.30&new-version=0.2.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 18:01:02 +00:00
dependabot[bot]
c50282e390 build(deps): bump zbus_macros from 5.8.0 to 5.9.0 (#38147)
Bumps [zbus_macros](https://github.com/dbus2/zbus) from 5.8.0 to 5.9.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dbus2/zbus/releases">zbus_macros's
releases</a>.</em></p>
<blockquote>
<h2>🔖 zbus_macros 3.14.0.</h2>
<p> Allow unicast signals through the <code>dbus_interface</code>.
Fixes <a
href="https://redirect.github.com/dbus2/zbus/issues/374">#374</a>.
⬆️ Bump our MSRV. More and more dependencies are requiring Rust 1.64.0,
so let's bump our MSRV
to match.
🔥 Drop manual <code>Default</code> impl of
<code>PropertyEmitsChangedSignal</code>. After Rust 1.64, we can use the
derive for this.
️ Revert locking of <code>winnow</code> version. We've bumped our MSRV
so there is no need for this
workaround anymore.
🎨 Code comments should also adhere to 100 character limit.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="30487b8fdb"><code>30487b8</code></a>
Merge pull request <a
href="https://redirect.github.com/dbus2/zbus/issues/1434">#1434</a> from
zeenix/zb-release</li>
<li><a
href="4b7928d2f8"><code>4b7928d</code></a>
🔖 zb,zm: Release 5.9.0</li>
<li><a
href="d570c947ea"><code>d570c94</code></a>
📝 CONTRIBUTING: Link to gimoji's web interface</li>
<li><a
href="0bf6e14b54"><code>0bf6e14</code></a>
Merge pull request <a
href="https://redirect.github.com/dbus2/zbus/issues/1431">#1431</a> from
zeenix/name-request-defaults</li>
<li><a
href="ba2a40752d"><code>ba2a407</code></a>
🧵 zb: Remove deadlocks in Connection name request tasks</li>
<li><a
href="3d35496021"><code>3d35496</code></a>
🐛 zb: Allow name replacement by default</li>
<li><a
href="0ad37f317a"><code>0ad37f3</code></a>
📝 zb: Remove a bunch of unnecessary links</li>
<li><a
href="493a9943d6"><code>493a994</code></a>
Merge pull request <a
href="https://redirect.github.com/dbus2/zbus/issues/1429">#1429</a> from
valpackett/val/knrmmkqzrvyp</li>
<li><a
href="f2fb16fd18"><code>f2fb16f</code></a>
🧑‍💻 zb: add fdo::dbus::StartServiceReply type</li>
<li><a
href="f93584de1f"><code>f93584d</code></a>
⬆️ micro: Update winnow to v0.7.12 (<a
href="https://redirect.github.com/dbus2/zbus/issues/1428">#1428</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/dbus2/zbus/compare/zbus-5.8.0...zbus-5.9.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zbus_macros&package-manager=cargo&previous-version=5.8.0&new-version=5.9.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 17:54:24 +00:00
Euclid Ye
5911cd891f servoshell: Fix scroll speed for Desktop (#37982)
According to discussion
https://github.com/servo/servo/pull/34063#discussion_r2187147615, this
PR
1. adds `PIXEL_DELTA_FACTOR` to increase scroll speed for
`MouseScrollDelta::PixelDelta`, which is used by macOS and
high-precision mouse.
2. adds `LINE_WIDTH` to increase x-axis scroll speed for
`MouseScrollDelta::LineDelta`, which is used by Linux and Windows.
3. Increase mouse scroll speed in general to match other browsers
4. Reduce keyboard scroll offset to scroll exactly "one line", to match
other browsers.

Testing: Example in #35037 can now scroll in x-axis as fast as y-axis,
similar to other browsers.
Fixes: part of #38072.

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-17 17:43:30 +00:00
dependabot[bot]
d5907a8d66 build(deps): bump zbus from 5.8.0 to 5.9.0 (#38144)
Bumps [zbus](https://github.com/dbus2/zbus) from 5.8.0 to 5.9.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dbus2/zbus/releases">zbus's
releases</a>.</em></p>
<blockquote>
<h2>🔖 zbus 5.9.0</h2>
<ul>
<li>🧵 Remove deadlocks in Connection name request tasks, resulting in
leaks under certain
circumstances.</li>
<li>🐛 When registering names, allow name replacement by default.</li>
<li> Allow setting request name flags in
<code>connection::Builder</code>.</li>
<li> Proper Default impl for <code>RequestNameFlags</code>. This change
is theoretically an API break for
users who assumed the default value to be empty.</li>
<li>🧑💻 Add <code>fdo::StartServiceReply</code> type. In 6.0 this will be
the return type of
<code>fdo::DBusProxy::start_service_by_name</code>. For now, just
provide a <code>TryFrom&lt;u32&gt;</code>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="30487b8fdb"><code>30487b8</code></a>
Merge pull request <a
href="https://redirect.github.com/dbus2/zbus/issues/1434">#1434</a> from
zeenix/zb-release</li>
<li><a
href="4b7928d2f8"><code>4b7928d</code></a>
🔖 zb,zm: Release 5.9.0</li>
<li><a
href="d570c947ea"><code>d570c94</code></a>
📝 CONTRIBUTING: Link to gimoji's web interface</li>
<li><a
href="0bf6e14b54"><code>0bf6e14</code></a>
Merge pull request <a
href="https://redirect.github.com/dbus2/zbus/issues/1431">#1431</a> from
zeenix/name-request-defaults</li>
<li><a
href="ba2a40752d"><code>ba2a407</code></a>
🧵 zb: Remove deadlocks in Connection name request tasks</li>
<li><a
href="3d35496021"><code>3d35496</code></a>
🐛 zb: Allow name replacement by default</li>
<li><a
href="0ad37f317a"><code>0ad37f3</code></a>
📝 zb: Remove a bunch of unnecessary links</li>
<li><a
href="493a9943d6"><code>493a994</code></a>
Merge pull request <a
href="https://redirect.github.com/dbus2/zbus/issues/1429">#1429</a> from
valpackett/val/knrmmkqzrvyp</li>
<li><a
href="f2fb16fd18"><code>f2fb16f</code></a>
🧑‍💻 zb: add fdo::dbus::StartServiceReply type</li>
<li><a
href="f93584de1f"><code>f93584d</code></a>
⬆️ micro: Update winnow to v0.7.12 (<a
href="https://redirect.github.com/dbus2/zbus/issues/1428">#1428</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/dbus2/zbus/compare/zbus-5.8.0...zbus-5.9.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zbus&package-manager=cargo&previous-version=5.8.0&new-version=5.9.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 17:30:45 +00:00
Euclid Ye
f81ae1f57f webdriver chore: check browsing context existence before handling user prompt when required (#38142)
Thanks to Trong with #38035, we can finally handle user prompt. This PR
is mainly dirty work:
1. Add browsing context check before handling user prompt when required,
according to spec.
2. Reduce IPC by removing redundant context existence check.
3. Add many missing docs.

Testing: New passing cases and turn some ERROR into FAIL.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-17 17:07:05 +00:00
Simon Wülker
9a5a33190d Remove Document::set_allow_declarative_shadow_roots (#38143)
This method is unused. `rustc` didn't complain because it was marked as
`pub` (which it shouldn't have been). A few of the surrounding methods
were also `pub`, which this change fixes.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-07-17 16:43:56 +00:00
Euclid Ye
fe2c13c777 doc: Add doc for compositor/webview/embedder related to window/rect/inner_size/rendering_context (#38110)
Add docs before actually fixing #38089, #38090, #37978, #38093.

Testing: Just adding docs.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-17 13:40:28 +00:00
batu_hoang
345733a5c5 webdriver: Add handle any user prompts step for all commands (#38035)
- Add `handler any user prompt` step for all commands.
- Enable webdriver tests which were blocked by `handle any user prompt`
step.

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
2025-07-17 09:47:47 +00:00
Tim van der Lippe
18d1a62add Inherit CSP for blob workers (#38033)
Workers created from Blobs inherit their CSP. Now we inherit the CSP and
set the correct base API url. The base API url should be used when
determining the
report-uri endpoint. Otherwise, the blob URL would be used as a base,
which is invalid and the report wouldn't be sent.

Also create a helper method to concatenate two optionals of CSPList,
which was used in several places.

Part of #4577

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2025-07-17 08:14:20 +00:00
Kenzie Raditya Tirtarahardja
439cb00e31 WebDriver: Set current browsing context for NavigateTo and Refresh command (#38107)
Previously we didn't change the current browsing context id in our
WebDriver Session after the commands `Navigate To` and `Refresh`

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-17 07:57:31 +00:00
Jerens Lensun
1c4797809a Mach: add type check on python tidy folder (#38043)
Introduce `python/tidy` folder in pyrefly type checker

Testing: Manual testing via `./mach test-tidy` command

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
2025-07-17 07:35:11 +00:00
Jerens Lensun
2ad250de26 remove test-wpt-android from mach command (#38135)
This command is no longer work as we arleady remove
`in_android_emulator` in this PR
[37958](https://github.com/servo/servo/pull/37958) cause of
`setup_configuration_for_android_target` is no longer exist in
`python/servo` codebase

These commands are being removed in order to implement Python type
checking the Servo repository.
Testing: This just removes some mach command so shouldn't need tests.

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
2025-07-17 07:31:39 +00:00
Kenzie Raditya Tirtarahardja
5e8b3cc5eb CI: Run webdriver tests using one process and 2 chunks (#38052)
- Make the test more stable with setting one process only for each
chunk, while still running it concurrently with different machine for
each chunk.
- Previously, there is an issue where the log files is too big to be
uploaded to intermittent tracker. Now the log file should be smaller
even if we have many errors, since we have multiple chunk.

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-17 06:01:48 +00:00
Abdelrahman Hossam
c76c44d0fb script: Ensure that keyboard modifiers, screen point, and client point are set in WheelEvents (#37947)
- Updating the WheelEvent initialization to correctly handle keyboard
modifiers when the wheel event is triggered. The changes ensure that the
modifiers (Ctrl, Alt, Shift, Meta) are properly set based on the current
state of the keyboard when the wheel event is created. This is
particularly important for scenarios where the wheel event is influenced
by key presses, such as scrolling with the Ctrl key pressed to zoom in
or out.

- Updating the `screen_point` and `client_point` as it was always 0,0
before. Now, it shows the correct position of the mouse pointer while
triggering the wheel event.

Test: Manual Test case and existing WPT tests
(classic/perform_actions/wheel.py[test_scroll_with_key_pressed])
Fixes: #37827

Signed-off-by: abdelrahman1234567 <abdelrahman.hossameldin.awadalla@huawei.com>
2025-07-17 05:32:03 +00:00
Ashwin Naren
f70a4eb4ff Move common indexeddb methods out of dom implementations (#38101)
A lot of shared functions were scattered around the dom files; I moved
them into `indexed_db.rs` for clarity.

Fixes: Nothing to my knowledge, just a cleanup

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-17 04:19:03 +00:00
Ashwin Naren
e10466b4c4 net: Do not print SVG tree in Debug implementation of VectorImageData (#37846)
Manually implement Debug LoadResult so that VectorImageData doesn't get
logged.

Testing: N/A
Fixes: #37771

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-17 04:15:23 +00:00
sagudev
86ea2641f8 servoshell: Update egui to 0.32 (#38119)
We also need to bump egui-file-dialog and accesskit_winit. Changes are
done based on migration paths provided by egui.

Testing: No tests for servoshell
Fixes: #38117

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-16 19:42:16 +00:00
sagudev
9b63854cd0 CI: remove i686-linux-android (#38118)
As discussed in [#general > Help solving android bindgen failure @
💬](https://servo.zulipchat.com/#narrow/channel/263398-general/topic/Help.20solving.20android.20bindgen.20failure/near/529091066)

Testing: Not needed, becuase we just remove from CI
Will help with #37077.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-16 17:07:24 +00:00
sagudev
b799f27817 canvas: Use stored transform instead of querying canvas paint thread (#38097)
We already store transform in context state, so let's just use this when
querying instead of using IPC to ask canvas paint thread.

Testing: Existing WPT tests
work towards #38022

try run: https://github.com/sagudev/servo/actions/runs/16299182583

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-16 14:59:10 +00:00
Gregory Terzian
b821377771 script: further use of safe to jsval (#38099)
Remove size bound from safe to jsval trait, apply to script/dom, with
the exception of windowproxy.

Second part of https://github.com/servo/servo/issues/37951

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>

*Describe the changes that this pull request makes here. This will be
the commit message.*

Testing: *Describe how this pull request is tested or why it doesn't
require tests*
Fixes: *Link to an issue this pull requests fixes or remove this line if
there is no issue*
2025-07-16 14:46:10 +00:00
Euclid Ye
72a9f36c43 webdriver: Reduce IPC for viewport boundary check (#38113)
Testing: No behaviour change.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-16 10:06:49 +00:00
Kenzie Raditya Tirtarahardja
7dae7f9983 WebDriver: Implement some missing steps of wait for navigation to complete (#38079)
Based on
https://w3c.github.io/webdriver/#dfn-wait-for-navigation-to-complete. We
still have not implement the timer parameter, but I think it makes sense
to implement Step 8.4 of `Navigation To` first.

---------

Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
2025-07-16 09:19:57 +00:00
Alex Touchet
8f9d484693 Update non-breaking dependencies (#38108)
Update non-breaking dependencies.

Testing: No tests for dependency updates.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-07-16 07:51:37 +00:00
sagudev
429b91da49 canvas: Store current path only in user space (#38098)
Before we stored path in user or device space and do conversions as
needed, but that complicated things.

So we got two options:
1. store path in device space: here we would need to add
`Option<Transform>` to each path command, and before each usage we need
to invert current transform (as current transform is already applied by
stroke/fill and we cannot just pass I, because we need scaling to make
lines thicker)
2. store path in user space and do transform whenever we change the
transform. There is no need to do inverse on uses.

I chose option 2. because it's less complicated and will probably
benefit performance (set transform typically called more rarely than
path building/fill/stroke).

In follow up PR I will move all of this to script, that's why
PathBuilderRef was not removed yet.

Testing: Existing WPT tests
work towards #38022
try run: https://github.com/sagudev/servo/actions/runs/16304221495

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
2025-07-16 07:30:20 +00:00
Abdelrahman Hossam
2ea95c8813 servoshell: Send all button events to the WebView regardless of what button is pressed (#38053)
Sending all mouse button events (Left, Right, Middle, Back, Forward) to
the constellation for processing. Before, only left and right mouse
buttons were handled.

Testing: There is no new test for this. Manually testing was done. We
currently
do not have a good way to test user interaction in servoshell.
Fixes: #37996

Signed-off-by: abdelrahman1234567 <abdelrahman.hossameldin.awadalla@huawei.com>
2025-07-16 06:53:55 +00:00
Alex Touchet
cbdf72b4eb Update wasm-bindgen and associated dependencies (#38103)
Update wasm-bindgen and associated dependencies.

Testing: No tests for dependency updates.

Signed-off-by: Alex Touchet <26315797+atouchet@users.noreply.github.com>
2025-07-16 02:11:36 +00:00
Ashwin Naren
71e7019d45 [IndexedDB] Adhere better to the specification for idb object store related operations (#37682)
Many object store related operations require the transaction to be
checked: to ensure it is still active, and, if the operation is a write,
that the transaction is not read-only. I've added the
`check_transaction` method to perform these checks.

Additionally `Clear` was still half-implemented, so I went ahead and
implemented that.

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-16 02:11:06 +00:00
Euclid Ye
2e3c280f46 webdriver: improve perform pointermove & wheel actions with more accurate coordinates (#38095)
1. Create `get_origin_relative_coordinates` according to
[spec](https://w3c.github.io/webdriver/#dfn-get-coordinates-relative-to-an-origin)
to be reused
2. Add previously missing offset for PointerOrigin::Element
3. Refactor code for perform pointermove/wheel to be closer to spec.
4. Handle some issues with spec:
https://github.com/w3c/webdriver/issues/1758

Testing: Several new passing cases as we are more precise with
coordinates now.
Fixes: Part of #38042.

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
2025-07-16 02:03:34 +00:00
4205 changed files with 457522 additions and 41042 deletions

21
.github/actions/parse_msrv/action.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Parse rust-version from libservo
description: "Parse rust-version from libservo. Requires cargo-metadata."
outputs:
rust_version:
description: "Minimum rust-version defined by libservo"
value: ${{ steps.parse_version.outputs.LIBSERVO_RUST_VERSION }}
runs:
using: "composite"
steps:
- name: Parse libservo version
id: parse_version
shell: bash
run: |
msrv=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "libservo") | .rust_version')
if [[ "${msrv}" == "null" ]]; then
echo "Failed to determine MSRV of libservo"
exit 1
fi
echo "libservo has a minimum supported Rust version of ${msrv}."
echo "LIBSERVO_RUST_VERSION=${msrv}" >> "$GITHUB_OUTPUT"

View File

@@ -0,0 +1,37 @@
# In the unlikely event a self-hosted runner was selected and reserved but it
# goes down before the workload starts, cancel the workflow run.
name: Detect self-hosted runner assigment timeout
description: "Cancel the workflow run if a self-hosted runner was selected, but the job failed to start"
inputs:
unique-id:
required: true
description: "Unique ID of the runner"
github_token:
required: true
description: "Must be able to do github API calls and cancel jobs."
runs:
using: "composite"
steps:
- name: Wait a bit
shell: bash
run: sleep 120
- name: Cancel if workload job is still queued
shell: bash
run: |
run_url=/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}
export GH_TOKEN=${{ inputs.github_token }}
if [ "$(gh api "$run_url/jobs" \
| jq -er --arg id '${{ inputs.unique-id }}' \
'.jobs[] | select(.name | contains("[" + $id + "]")) | .status'
)" = queued ]; then
echo 'Timeout waiting for runner assignment!'
echo 'Hint: does this repo have permission to access the runner group?'
echo 'Hint: https://github.com/organizations/servo/settings/actions/runner-groups'
echo
echo 'Cancelling workflow run'
gh api "$run_url/cancel" --method POST
exit 1
fi

View File

@@ -2,6 +2,9 @@ version: 2
updates:
- package-ecosystem: cargo
directory: "/"
exclude-paths:
- "tests/blink_perf/**"
- "tests/wpt/**"
schedule:
interval: daily
open-pull-requests-limit: 10
@@ -18,6 +21,8 @@ updates:
- "emath"
- "epaint"
- "epaint_default_fonts"
- "accesskit_winit"
- "egui-file-dialog"
gstreamer-related:
patterns:
- "gio*"

View File

@@ -42,7 +42,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
target: ['aarch64-linux-android', 'armv7-linux-androideabi', 'i686-linux-android', 'x86_64-linux-android']
target: ['aarch64-linux-android', 'armv7-linux-androideabi', 'x86_64-linux-android']
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
@@ -81,7 +81,7 @@ jobs:
uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r26c
ndk-version: r28b
- name: Setup Gradle caches
uses: gradle/actions/setup-gradle@v4
- name: Trigger initial download of Gradle with retries
@@ -152,7 +152,7 @@ jobs:
needs: ["build"]
strategy:
matrix:
target: ['aarch64-linux-android', 'armv7-linux-androideabi', 'i686-linux-android', 'x86_64-linux-android']
target: ['aarch64-linux-android', 'armv7-linux-androideabi', 'x86_64-linux-android']
if: ${{ inputs.bencher && inputs.profile != 'debug' && github.event_name != 'workflow_dispatch' && github.event_name != 'merge_group' }}
name: 'Bencher (${{ matrix.target }})'
uses: ./.github/workflows/bencher.yml
@@ -160,8 +160,8 @@ jobs:
target: android-${{ matrix.target }}
profile: ${{ inputs.profile }}
compressed-file-path: ${{ inputs.profile }}-binary-android-${{ matrix.target }}/servoapp.apk
binary-path: lib/${{ matrix.target == 'aarch64-linux-android' && 'arm64-v8a' || matrix.target == 'armv7-linux-androideabi' && 'armeabi-v7a' || matrix.target == 'i686-linux-android' && 'x86' || matrix.target == 'x86_64-linux-android' && 'x86_64'}}/libservoshell.so
binary-path: lib/${{ matrix.target == 'aarch64-linux-android' && 'arm64-v8a' || matrix.target == 'armv7-linux-androideabi' && 'armeabi-v7a' || matrix.target == 'x86_64-linux-android' && 'x86_64'}}/libservoshell.so
file-size: true
speedometer: false
dromaeo: false
secrets: inherit
secrets: inherit

View File

@@ -113,9 +113,10 @@ jobs:
if: github.event_name == 'pull_request_target'
run: |
echo "RUN_BENCHER_OPTIONS=--branch ${{ github.event.number }}/PR \
--branch-start-point ${{ github.base_ref }} \
--branch-start-point-hash ${{ github.event.pull_request.base.sha }} \
--branch-reset \
--start-point ${{ github.base_ref }} \
--start-point-hash ${{ github.event.pull_request.base.sha }} \
--start-point-reset \
--start-point-clone-thresholds \
--github-actions ${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_ENV"
- name: Set bencher opts for main
if: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
@@ -130,8 +131,9 @@ jobs:
echo "RUN_BENCHER_OPTIONS=--branch try \
--github-actions ${{ secrets.GITHUB_TOKEN }} \
--hash $(git rev-parse HEAD~1) \
--branch-start-point main \
--branch-start-point-hash $(git merge-base upstream/main HEAD) \
--start-point main \
--start-point-hash $(git merge-base upstream/main HEAD) \
--start-point-clone-thresholds \
--branch-reset" >> "$GITHUB_ENV"
# we join results and send all data once to have it all in one report
- name: Send results
@@ -140,4 +142,4 @@ jobs:
./etc/ci/bencher.py merge ${{ env.SERVO_FILE_SIZE_RESULT }} ${{ env.SERVO_STRIPPED_FILE_SIZE_RESULT }} ${{ env.SERVO_SPEEDOMETER_RESULT }} ${{ env.SERVO_DROMAEO_RESULT }} --bmf-output b.json
bencher run --adapter json --file b.json \
--project ${{ env.BENCHER_PROJECT }} --token ${{ secrets.BENCHER_API_TOKEN }} --testbed ubuntu-22.04 \
$RUN_BENCHER_OPTIONS
$RUN_BENCHER_OPTIONS

View File

@@ -75,8 +75,8 @@ jobs:
lint:
if: ${{ inputs.workflow == 'lint' }}
name: Lint
# Note: The lint workflow does not need access to any secrets.
uses: ./.github/workflows/lint.yml
secrets: inherit
android:
if: ${{ inputs.workflow == 'android' }}

View File

@@ -37,7 +37,7 @@ jobs:
uses: baptiste0928/cargo-install@v3
with:
crate: cargo-deny
version: 0.18
version: 0.18.3
locked: true
- name: Bootstrap dependencies
run: |

View File

@@ -65,6 +65,7 @@ jobs:
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Bootstrap dependencies
timeout-minutes: 60
run: |
sudo apt update
sudo apt install -qy --no-install-recommends mesa-vulkan-drivers fonts-noto-cjk

View File

@@ -111,11 +111,18 @@ jobs:
runner-timeout:
needs:
- runner-select
uses: ./.github/workflows/self-hosted-runner-timeout.yml
secrets: inherit
with:
unique-id: ${{ needs.runner-select.outputs.unique-id }}
is-self-hosted: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: '.github'
- name: Runner timeout
uses: ./.github/actions/runner-timeout
if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
with:
github_token: '${{ secrets.GITHUB_TOKEN }}'
unique-id: '${{ needs.runner-select.outputs.unique-id }}'
build:
needs:
@@ -183,10 +190,6 @@ jobs:
timeout_minutes: 20
max_attempts: 2 # https://github.com/servo/servo/issues/30683
command: ./mach test-unit --${{ inputs.profile }}
- name: Build libservo with examples
if: ${{ inputs.build-libservo }}
continue-on-error: true
run: cargo build -p libservo --all-targets
- name: Archive build timing
uses: actions/upload-artifact@v4
with:
@@ -244,3 +247,34 @@ jobs:
speedometer: ${{ inputs.profile == 'release' }}
dromaeo: ${{ inputs.profile == 'release' }}
secrets: inherit
build-libservo:
if: ${{ inputs.build-libservo }}
name: Build libservo and MSRV check
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 1
# This is necessary to checkout the pull request if this run was triggered via a
# `pull_request_target` event.
- uses: actions/checkout@v4
if: github.event_name == 'pull_request_target'
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
- name: Set LIBCLANG_PATH env # needed for bindgen in mozangle
run: echo "LIBCLANG_PATH=/usr/lib/llvm-14/lib" >> $GITHUB_ENV
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Determine MSRV
id: msrv
uses: ./.github/actions/parse_msrv
- name: Install MSRV
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.rust_version }}
- name: Compile libservo with MSRV
run: |
cargo +${{ steps.msrv.outputs.rust_version }} build -p libservo --locked --all-targets

View File

@@ -40,6 +40,7 @@ jobs:
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Prep test environment
timeout-minutes: 60
run: |
gtar -xzf target.tar.gz
./mach bootstrap --skip-lints

View File

@@ -94,11 +94,18 @@ jobs:
runner-timeout:
needs:
- runner-select
uses: ./.github/workflows/self-hosted-runner-timeout.yml
secrets: inherit
with:
unique-id: ${{ needs.runner-select.outputs.unique-id }}
is-self-hosted: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: '.github'
- name: Runner timeout
uses: ./.github/actions/runner-timeout
if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
with:
github_token: '${{ secrets.GITHUB_TOKEN }}'
unique-id: '${{ needs.runner-select.outputs.unique-id }}'
build:
needs:
@@ -167,10 +174,6 @@ jobs:
timeout_minutes: 40 # https://github.com/servo/servo/issues/30275
max_attempts: 3 # https://github.com/servo/servo/issues/30683
command: ./mach test-unit --${{ inputs.profile }}
- name: Build libservo with examples
if: ${{ inputs.build-libservo }}
continue-on-error: true
run: cargo build -p libservo --all-targets
- name: Build mach package
run: ./mach package --${{ inputs.profile }}
- name: Run DMG smoketest
@@ -231,3 +234,32 @@ jobs:
speedometer: false
dromaeo: false
secrets: inherit
build-libservo:
if: ${{ inputs.build-libservo }}
name: Build libservo and MSRV check
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 1
# This is necessary to checkout the pull request if this run was triggered via a
# `pull_request_target` event.
- uses: actions/checkout@v4
if: github.event_name == 'pull_request_target'
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Determine MSRV
id: msrv
uses: ./.github/actions/parse_msrv
- name: Install MSRV
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.rust_version }}
- name: Compile libservo with MSRV
run: |
cargo +${{ steps.msrv.outputs.rust_version }} build -p libservo --locked --all-targets

View File

@@ -67,7 +67,7 @@ jobs:
run: sudo apt update && ./mach bootstrap --skip-lints
- name: Setup OpenHarmony SDK
id: setup_sdk
uses: openharmony-rs/setup-ohos-sdk@v0.2.1
uses: openharmony-rs/setup-ohos-sdk@v0.2.3
with:
version: "5.0.2"
fixup-path: true
@@ -269,14 +269,14 @@ jobs:
- uses: actions/checkout@v4
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 2
fetch-depth: 0
# This is necessary to checkout the pull request if this run was triggered via a
# `pull_request_target` event.
- uses: actions/checkout@v4
if: github.event_name == 'pull_request_target'
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
fetch-depth: 0
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Getting hitrace-bench version
@@ -289,26 +289,53 @@ jobs:
continue-on-error: true
- name: Run speedometer
id: BencherRun
run: python3 ./mach test-speedometer-ohos --bmf-output speedometer.json
run: python3 ./mach test-speedometer-ohos --bmf-output speedometer.json --profile ${{ inputs.profile }}
continue-on-error: true
- name: Create empty speedometer.json if failed
run: touch speedometer.json
if: ${{ steps.BencherRun.outcome == 'failure' }}
- name: Getting model name
# awk is used to remove trailing spaces
run: |
echo "MODEL_NAME=$(hdc bugreport | head -n 20 | grep MarketName | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}' -)" >> $GITHUB_ENV
echo "MODEL_NAME=$(hdc shell param get const.product.name | awk '{$1=$1;print}' )" >> $GITHUB_ENV
- name: Combining bencher files
run: jq --compact-output --slurp add speedometer.json bench.json > combined-bencher.json
- uses: bencherdev/bencher@main
# set options
- name: Set bencher opts for PRs (label try run)
if: github.event_name == 'pull_request_target'
run: |
echo "RUN_BENCHER_OPTIONS=--branch ${{ github.event.number }}/PR \
--start-point ${{ github.base_ref }} \
--start-point-hash ${{ github.event.pull_request.base.sha }} \
--start-point-reset \
--start-point-clone-thresholds" >> "$GITHUB_ENV"
- name: Set bencher opts for main
if: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
run: |
echo "RUN_BENCHER_OPTIONS=--branch main" >> "$GITHUB_ENV"
- name: Set bencher opts for try branch
if: ${{ github.event_name == 'push' && github.ref_name == 'try' }}
run: |
git remote add upstream https://github.com/servo/servo
git fetch upstream main
echo "RUN_BENCHER_OPTIONS=--branch try \
--hash $(git rev-parse HEAD~1) \
--start-point main \
--start-point-hash $(git merge-base upstream/main HEAD) \
--start-point-clone-thresholds \
--start-point-reset" >> "$GITHUB_ENV"
- name: Uploading to bencher.dev
# Note: That e.g. `--start-point` is ignored if it is an empty string,
# which should be the case on e.g. normal push workflows on main.
run: |
bencher run --adapter json \
--file combined-bencher.json \
--project '${{ env.BENCHER_PROJECT }}' \
--hash '${{ github.sha }}' \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--github-actions '${{ secrets.GITHUB_TOKEN }}' \
--testbed="$MODEL_NAME"
--testbed="$MODEL_NAME" \
$RUN_BENCHER_OPTIONS
- name: Success
if: ${{ !contains(steps.*.outcome, 'failure') && !contains(steps.*.outcome, 'cancelled') }}
run: exit 0

View File

@@ -1,39 +0,0 @@
name: Detect Self-hosted Runner Timeout
on:
workflow_call:
inputs:
unique-id:
required: true
type: string
is-self-hosted:
required: true
type: boolean
jobs:
# In the unlikely event a self-hosted runner was selected and reserved but it
# goes down before the workload starts, cancel the workflow run.
runner-timeout:
if: ${{ inputs.is-self-hosted }}
name: Detect Runner Timeout
runs-on: ubuntu-latest
steps:
- name: Wait a bit
run: sleep 120
- name: Cancel if workload job is still queued
run: |
run_url=/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}
export GH_TOKEN=${{ secrets.GITHUB_TOKEN }}
if [ "$(gh api "$run_url/jobs" \
| jq -er --arg id '${{ inputs.unique-id }}' \
'.jobs[] | select(.name | contains("[" + $id + "]")) | .status'
)" = queued ]; then
echo 'Timeout waiting for runner assignment!'
echo 'Hint: does this repo have permission to access the runner group?'
echo 'Hint: https://github.com/organizations/servo/settings/actions/runner-groups'
echo
echo 'Cancelling workflow run'
gh api "$run_url/cancel" --method POST
exit 1
fi

View File

@@ -85,11 +85,18 @@ jobs:
runner-timeout:
needs:
- runner-select
uses: ./.github/workflows/self-hosted-runner-timeout.yml
secrets: inherit
with:
unique-id: ${{ needs.runner-select.outputs.unique-id }}
is-self-hosted: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: '.github'
- name: Runner timeout
uses: ./.github/actions/runner-timeout
if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
with:
github_token: '${{ secrets.GITHUB_TOKEN }}'
unique-id: '${{ needs.runner-select.outputs.unique-id }}'
build:
needs:
@@ -180,10 +187,6 @@ jobs:
timeout_minutes: 30
max_attempts: 3 # https://github.com/servo/servo/issues/30683
command: .\mach test-unit --${{ inputs.profile }} -- -- --test-threads=1
- name: Build libservo with examples
if: ${{ inputs.build-libservo }}
continue-on-error: true
run: cargo build -p libservo --all-targets
- name: Archive build timing
uses: actions/upload-artifact@v4
with:
@@ -223,3 +226,35 @@ jobs:
speedometer: false
dromaeo: false
secrets: inherit
build-libservo:
if: ${{ inputs.build-libservo }}
name: Build libservo and MSRV check
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 1
# This is necessary to checkout the pull request if this run was triggered via a
# `pull_request_target` event.
- uses: actions/checkout@v4
if: github.event_name == 'pull_request_target'
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
- name: Setup Python
uses: ./.github/actions/setup-python
- name: Upgrade llvm
if: ${{ runner.environment != 'self-hosted' }}
run: choco upgrade llvm
- name: Determine MSRV
id: msrv
uses: ./.github/actions/parse_msrv
- name: Install MSRV
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.rust_version }}
- name: Compile libservo with MSRV
run: |
cargo +${{ steps.msrv.outputs.rust_version }} build -p libservo --locked --all-targets

1665
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,11 @@ authors = ["The Servo Project Developers"]
license = "MPL-2.0"
edition = "2024"
publish = false
# We have yet to decide on a policy for updating the minimum supported rust version.
# Before increasing this, please open a discussion on zulip explaining the reason
# why we should consider increasing our minimum supported rust version.
# Please also note, that the **default** rust version in rust-toolchain.toml may be
# bumped freely.
rust-version = "1.85.0"
[workspace.dependencies]
@@ -33,15 +38,16 @@ base64 = "0.22.1"
bincode = "1"
bitflags = "2.9"
bluetooth_traits = { path = "components/shared/bluetooth" }
bytemuck = "1"
byteorder = "1.5"
canvas_traits = { path = "components/shared/canvas" }
cbc = "0.1.2"
cfg-if = "1.0.1"
cfg-if = "1.0.3"
chrono = { version = "0.4", features = ["serde"] }
cipher = { version = "0.4.4", features = ["alloc"] }
compositing_traits = { path = "components/shared/compositing" }
constellation_traits = { path = "components/shared/constellation" }
content-security-policy = { git = "https://github.com/servo/rust-content-security-policy/", branch = "servo-csp", features = ["serde"] }
content-security-policy = { git = "https://github.com/servo/rust-content-security-policy", branch = "servo-csp", features = ["serde"] }
cookie = { package = "cookie", version = "0.18" }
crossbeam-channel = "0.5"
cssparser = { version = "0.35", features = ["serde"] }
@@ -69,12 +75,13 @@ gstreamer-gl-sys = "0.23"
gstreamer-sys = "0.23"
gstreamer-video = "0.23"
harfbuzz-sys = "0.6.1"
harfrust = "0.1.1"
headers = "0.4"
hitrace = "0.1.5"
html5ever = "0.35"
http = "1.3"
http-body-util = "0.1"
hyper = "1.6"
hyper = "1.7"
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "logging", "tls12", "webpki-tokio"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http2", "tokio"] }
hyper_serde = { path = "components/hyper_serde" }
@@ -86,8 +93,9 @@ indexmap = { version = "2.10.0", features = ["std"] }
ipc-channel = "0.20"
itertools = "0.14"
js = { package = "mozjs", git = "https://github.com/servo/mozjs" }
keyboard-types = "0.7"
kurbo = "0.11"
keyboard-types = { version = "0.8.1", features = ["serde", "webdriver"] }
kurbo = { version = "0.11.3", features = ["euclid"] }
layout_api = { path = "components/shared/layout" }
libc = "0.2"
log = "0.4"
mach2 = "0.4"
@@ -99,12 +107,13 @@ mime = "0.3.13"
mime_guess = "2.0.5"
mozangle = "0.5.3"
net_traits = { path = "components/shared/net" }
nix = "0.29"
nom = "7.1.3"
nix = "0.30"
nom = "8.0.0"
num-traits = "0.2"
num_cpus = "1.17.0"
openxr = "0.19"
parking_lot = "0.12"
peniko = "0.4"
percent-encoding = "2.3"
proc-macro2 = "1"
profile_traits = { path = "components/shared/profile" }
@@ -120,9 +129,8 @@ resvg = "0.45.0"
rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] }
rustls-pemfile = "2.0"
rustls-pki-types = "1.12"
layout_api = { path = "components/shared/layout" }
script_traits = { path = "components/shared/script" }
selectors = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
selectors = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
serde = "1.0.219"
serde_bytes = "0.11"
serde_json = "1.0"
@@ -130,24 +138,24 @@ servo-media = { git = "https://github.com/servo/media" }
servo-media-dummy = { git = "https://github.com/servo/media" }
servo-media-gstreamer = { git = "https://github.com/servo/media" }
servo-tracing = { path = "components/servo_tracing" }
servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
skrifa = "0.31.3"
smallbitvec = "2.6.0"
smallvec = { version = "1.15", features = ["union", "serde"] }
static_assertions = "1.1"
smallvec = { version = "1.15", features = ["serde", "union"] }
string_cache = "0.8"
string_cache_codegen = "0.5"
strum = "0.26"
strum_macros = "0.26"
stylo = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-07-01" }
stylo = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-08-01" }
surfman = { git = "https://github.com/servo/surfman", rev = "f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1", features = ["chains"] }
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
synstructure = "0.13"
taffy = { version = "0.8.3", default-features = false, features = ["detailed_layout_info", "grid", "serde", "std"] }
taffy = { version = "0.9", default-features = false, features = ["calc", "detailed_layout_info", "grid", "std"] }
tikv-jemalloc-sys = "0.6.0"
tikv-jemallocator = "0.6.0"
time = { package = "time", version = "0.3", features = ["large-dates", "local-offset", "serde"] }
@@ -165,7 +173,9 @@ unicode-script = "0.5"
unicode-segmentation = "1.12.0"
url = "2.5"
urlpattern = "0.3"
uuid = { version = "1.12.1", features = ["v4"] }
uuid = { version = "1.18.0", features = ["v4"] }
vello = { git = "https://github.com/linebender/vello", rev = "b0e2e598ac62c7b3d04d8660e7b1b7659b596970" }
vello_cpu = { git = "https://github.com/linebender/vello", rev = "b0e2e598ac62c7b3d04d8660e7b1b7659b596970" }
webdriver = "0.53.0"
webgpu_traits = { path = "components/shared/webgpu" }
webpki-roots = "1.0"
@@ -176,6 +186,7 @@ wgpu-core = "25"
wgpu-types = "25"
winapi = "0.3"
windows-sys = "0.59"
winit = "0.30.12"
wio = "0.2"
wr_malloc_size_of = { git = "https://github.com/servo/webrender", branch = "0.67" }
xi-unicode = "0.3.0"
@@ -221,8 +232,8 @@ codegen-units = 1
#
# html5ever = { path = "../html5ever/html5ever" }
# markup5ever = { path = "../html5ever/markup5ever" }
# xml5ever = { path = "../html5ever/xml5ever" }
# web_atoms = { path = "../html5ever/web_atoms" }
# xml5ever = { path = "../html5ever/xml5ever" }
#
# Or for Stylo:
#
@@ -249,4 +260,4 @@ codegen-units = 1
# <crate> = { path = "/path/to/local/checkout" }
#
# [patch."https://github.com/servo/rust-content-security-policy"]
# content-security-policy = { path = "../rust-content-security-policy/" }
# content-security-policy = { path = "../rust-content-security-policy" }

View File

@@ -90,4 +90,4 @@ For more detailed build instructions, see the Servo book under [Setting up your
- `OHOS_SDK_NATIVE` (e.g. `${DEVECO_SDK_HOME}/default/openharmony/native` or `${OHOS_BASE_SDK_HOME}/${API_VERSION}/native`)
- `SERVO_OHOS_SIGNING_CONFIG`: Path to json file containing a valid signing configuration for the demo app.
- Review the detailed instructions at [Building for OpenHarmony].
- The target distribution can be modified by passing `--flavor=<default|harmonyos>` to `mach <build|package|install>.
- The target distribution can be modified by passing `--flavor=<default|harmonyos>` to `mach <build|package|install>`.

View File

@@ -11,8 +11,13 @@ rust-version.workspace = true
path = "lib.rs"
[features]
allocation-tracking = ["backtrace", "fnv"]
use-system-allocator = ["libc"]
[dependencies]
backtrace = { workspace = true, optional = true }
fnv = { workspace = true, optional = true }
[target.'cfg(not(any(windows, target_env = "ohos")))'.dependencies]
libc = { workspace = true, optional = true }
tikv-jemalloc-sys = { workspace = true }

View File

@@ -2,13 +2,53 @@
* 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/. */
//! Selecting the default global allocator for Servo
//! Selecting the default global allocator for Servo, and exposing common
//! allocator introspection APIs for memory profiling.
use std::os::raw::c_void;
#[cfg(not(feature = "allocation-tracking"))]
#[global_allocator]
static ALLOC: Allocator = Allocator;
#[cfg(feature = "allocation-tracking")]
#[global_allocator]
static ALLOC: crate::tracking::AccountingAlloc<Allocator> =
crate::tracking::AccountingAlloc::with_allocator(Allocator);
#[cfg(feature = "allocation-tracking")]
mod tracking;
pub fn dump_unmeasured() {
#[cfg(feature = "allocation-tracking")]
ALLOC.dump_unmeasured_allocations();
}
pub use crate::platform::*;
type EnclosingSizeFn = unsafe extern "C" fn(*const c_void) -> usize;
/// # Safety
/// No restrictions. The passed pointer is never dereferenced.
/// This function is only marked unsafe because the MallocSizeOfOps APIs
/// requires an unsafe function pointer.
#[cfg(feature = "allocation-tracking")]
unsafe extern "C" fn enclosing_size_impl(ptr: *const c_void) -> usize {
let (adjusted, size) = crate::ALLOC.enclosing_size(ptr);
if size != 0 {
crate::ALLOC.note_allocation(adjusted, size);
}
size
}
#[allow(non_upper_case_globals)]
#[cfg(feature = "allocation-tracking")]
pub static enclosing_size: Option<EnclosingSizeFn> = Some(crate::enclosing_size_impl);
#[allow(non_upper_case_globals)]
#[cfg(not(feature = "allocation-tracking"))]
pub static enclosing_size: Option<EnclosingSizeFn> = None;
#[cfg(not(any(windows, feature = "use-system-allocator", target_env = "ohos")))]
mod platform {
use std::os::raw::c_void;
@@ -21,7 +61,10 @@ mod platform {
///
/// Passing a non-heap allocated pointer to this function results in undefined behavior.
pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize {
unsafe { tikv_jemallocator::usable_size(ptr) }
let size = unsafe { tikv_jemallocator::usable_size(ptr) };
#[cfg(feature = "allocation-tracking")]
crate::ALLOC.note_allocation(ptr, size);
size
}
/// Memory allocation APIs compatible with libc
@@ -46,12 +89,18 @@ mod platform {
pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize {
#[cfg(target_vendor = "apple")]
unsafe {
return libc::malloc_size(ptr);
let size = libc::malloc_size(ptr);
#[cfg(feature = "allocation-tracking")]
crate::ALLOC.note_allocation(ptr, size);
size
}
#[cfg(not(target_vendor = "apple"))]
unsafe {
return libc::malloc_usable_size(ptr as *mut _);
let size = libc::malloc_usable_size(ptr as *mut _);
#[cfg(feature = "allocation-tracking")]
crate::ALLOC.note_allocation(ptr, size);
size
}
}
@@ -81,7 +130,10 @@ mod platform {
ptr = *(ptr as *const *const c_void).offset(-1)
}
HeapSize(heap, 0, ptr) as usize
let size = HeapSize(heap, 0, ptr) as usize;
#[cfg(feature = "allocation-tracking")]
crate::ALLOC.note_allocation(ptr, size);
size
}
}
}

View File

@@ -0,0 +1,211 @@
/* 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/. */
//! An allocator wrapper that records metadata about each live allocation.
//! This metadata can then be used to identify allocations that are not visible
//! through any existing MallocSizeOf path.
use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::os::raw::c_void;
use std::sync::Mutex;
use fnv::{FnvBuildHasher, FnvHashMap};
/// The maximum number of allocations that we'll keep track of. Once the limit
/// is reached, we'll evict the first allocation that is smaller than the new addition.
const MAX_TRACKED_ALLOCATIONS: usize = usize::MAX;
/// Cap the number of stack frames that we'll store per allocation.
const MAX_FRAMES: usize = 50;
/// A certain number of frames at the top of the allocation stack are just
/// just internal liballoc implementation details or AccountingAlloc functions.
/// We can skip them without losing any meaningful information.
const SKIPPED_FRAMES: usize = 5;
/// The minimum size of allocation that we'll track. Can be used to reduce overhead
/// by skipping bookkeeping for small allocations.
const MIN_SIZE: usize = 0;
thread_local! {
static IN_ALLOCATION: Cell<bool> = Cell::new(false);
}
#[derive(PartialEq, Eq)]
struct AllocSite {
/// The stack at the time this allocation was recorded.
frames: [*mut std::ffi::c_void; MAX_FRAMES],
/// The start of the allocated memory.
ptr: *mut u8,
/// The size of the allocated memory.
size: usize,
/// True if this allocation site's size is ever queried after the initial
/// allocation. If false, it means that the allocation is not visible from
/// any of the MallocSizeOf roots.
noted: bool,
}
impl AllocSite {
fn contains(&self, ptr: *mut u8) -> bool {
ptr >= self.ptr && ptr < unsafe { self.ptr.offset(self.size as isize) }
}
}
unsafe impl Send for AllocSite {}
/// A map of pointers to allocation callsite metadata.
static ALLOCATION_SITES: Mutex<FnvHashMap<usize, AllocSite>> =
const { Mutex::new(FnvHashMap::with_hasher(FnvBuildHasher::new())) };
#[derive(Default)]
pub struct AccountingAlloc<A = System> {
allocator: A,
}
impl<A> AccountingAlloc<A> {
pub const fn with_allocator(allocator: A) -> Self {
Self { allocator }
}
fn remove_allocation(&self, ptr: *const c_void, size: usize) {
if size < MIN_SIZE {
return;
}
let old = IN_ALLOCATION.with(|status| status.replace(true));
if old {
return;
}
let mut sites = ALLOCATION_SITES.lock().unwrap();
if let Entry::Occupied(e) = sites.entry(ptr as usize) {
e.remove();
}
IN_ALLOCATION.with(|status| status.set(old));
}
fn record_allocation(&self, ptr: *mut u8, size: usize) {
if size < MIN_SIZE {
return;
}
let old = IN_ALLOCATION.with(|status| status.replace(true));
if old {
return;
}
let mut num_skipped = 0;
let mut num_frames = 0;
let mut frames = [std::ptr::null_mut(); MAX_FRAMES];
backtrace::trace(|frame| {
if num_skipped < SKIPPED_FRAMES {
num_skipped += 1;
return true;
}
frames[num_frames] = frame.ip();
num_frames += 1;
num_frames < MAX_FRAMES
});
let site = AllocSite {
frames,
size,
ptr,
noted: false,
};
let mut sites = ALLOCATION_SITES.lock().unwrap();
if sites.len() < MAX_TRACKED_ALLOCATIONS {
sites.insert(ptr as usize, site);
} else if let Some(key) = sites
.iter()
.find(|(_, s)| s.size < site.size)
.map(|(k, _)| k.clone())
{
sites.remove(&key);
sites.insert(ptr as usize, site);
}
IN_ALLOCATION.with(|status| status.set(old));
}
pub(crate) fn enclosing_size(&self, ptr: *const c_void) -> (*const c_void, usize) {
ALLOCATION_SITES
.lock()
.unwrap()
.iter()
.find(|(_, site)| site.contains(ptr.cast_mut().cast()))
.map_or((std::ptr::null_mut(), 0), |(_, site)| {
(site.ptr.cast(), site.size)
})
}
pub(crate) fn note_allocation(&self, ptr: *const c_void, size: usize) {
if size < MIN_SIZE {
return;
}
IN_ALLOCATION.with(|status| status.set(true));
if let Some(site) = ALLOCATION_SITES.lock().unwrap().get_mut(&(ptr as usize)) {
site.noted = true;
}
IN_ALLOCATION.with(|status| status.set(false));
}
pub(crate) fn dump_unmeasured_allocations(&self) {
// Ensure that we ignore all allocations triggered while processing
// the existing allocation data.
IN_ALLOCATION.with(|status| status.set(true));
{
let sites = ALLOCATION_SITES.lock().unwrap();
let default = "???";
for site in sites
.values()
.filter(|site| site.size > MIN_SIZE && !site.noted)
{
let mut resolved = vec![];
for ip in site.frames.iter().filter(|ip| !ip.is_null()) {
backtrace::resolve(*ip, |symbol| {
resolved.push((
symbol.filename().map(|f| f.to_owned()),
symbol.lineno(),
symbol.name().map(|n| format!("{}", n)),
));
});
}
println!("---\n{}\n", site.size);
for (filename, line, symbol) in &resolved {
let fname = filename.as_ref().map(|f| f.display().to_string());
println!(
"{}:{} ({})",
fname.as_deref().unwrap_or(default),
line.unwrap_or_default(),
symbol.as_deref().unwrap_or(default),
);
}
}
}
IN_ALLOCATION.with(|status| status.set(false));
}
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for AccountingAlloc<A> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.allocator.alloc(layout) };
self.record_allocation(ptr, layout.size());
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { self.allocator.dealloc(ptr, layout) };
self.remove_allocation(ptr.cast(), layout.size());
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.allocator.alloc_zeroed(layout) };
self.record_allocation(ptr, layout.size());
ptr
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
self.remove_allocation(ptr.cast(), layout.size());
let ptr = unsafe { self.allocator.realloc(ptr, layout, new_size) };
self.record_allocation(ptr, new_size);
ptr
}
}

View File

@@ -2,10 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Weak};
use std::thread;
use std::thread::{self, Builder, JoinHandle};
use std::time::{Duration, Instant};
use background_hang_monitor_api::{
@@ -16,46 +14,33 @@ use background_hang_monitor_api::{
use crossbeam_channel::{Receiver, Sender, after, never, select, unbounded};
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use log::warn;
use crate::sampler::{NativeStack, Sampler};
#[derive(Clone)]
pub struct HangMonitorRegister {
sender: Weak<Sender<(MonitoredComponentId, MonitoredComponentMsg)>>,
tether: Sender<Never>,
sender: MonitoredComponentSender,
monitoring_enabled: bool,
}
impl HangMonitorRegister {
/// Start a new hang monitor worker, and return a handle to register components for monitoring.
/// Start a new hang monitor worker, and return a handle to register components for monitoring,
/// as well as a join handle on the worker thread.
pub fn init(
constellation_chan: IpcSender<HangMonitorAlert>,
control_port: IpcReceiver<BackgroundHangMonitorControlMsg>,
monitoring_enabled: bool,
) -> Box<dyn BackgroundHangMonitorRegister> {
// Create a channel to pass messages of type `MonitoredComponentMsg`.
// See the discussion in `<HangMonitorRegister as
// BackgroundHangMonitorRegister>::register_component` for why we wrap
// the sender with `Arc` and why `HangMonitorRegister` only maintains
// a weak reference to it.
) -> (Box<dyn BackgroundHangMonitorRegister>, JoinHandle<()>) {
let (sender, port) = unbounded();
let sender = Arc::new(sender);
let sender_weak = Arc::downgrade(&sender);
let sender_clone = sender.clone();
// Create a "tether" channel, whose sole purpose is to keep the worker
// thread alive. The worker thread will terminates when all copies of
// `tether` are dropped.
let (tether, tether_port) = unbounded();
let _ = thread::Builder::new()
let join_handle = Builder::new()
.name("BackgroundHangMonitor".to_owned())
.spawn(move || {
let mut monitor = BackgroundHangMonitorWorker::new(
constellation_chan,
control_port,
(sender, port),
tether_port,
port,
monitoring_enabled,
);
while monitor.run() {
@@ -63,11 +48,13 @@ impl HangMonitorRegister {
}
})
.expect("Couldn't start BHM worker.");
Box::new(HangMonitorRegister {
sender: sender_weak,
tether,
monitoring_enabled,
})
(
Box::new(HangMonitorRegister {
sender: sender_clone,
monitoring_enabled,
}),
join_handle,
)
}
}
@@ -80,11 +67,10 @@ impl BackgroundHangMonitorRegister for HangMonitorRegister {
component_id: MonitoredComponentId,
transient_hang_timeout: Duration,
permanent_hang_timeout: Duration,
exit_signal: Option<Box<dyn BackgroundHangMonitorExitSignal>>,
exit_signal: Box<dyn BackgroundHangMonitorExitSignal>,
) -> Box<dyn BackgroundHangMonitor> {
let bhm_chan = BackgroundHangMonitorChan::new(
self.sender.clone(),
self.tether.clone(),
component_id,
self.monitoring_enabled,
);
@@ -124,54 +110,12 @@ impl BackgroundHangMonitorRegister for HangMonitorRegister {
))]
let sampler = crate::sampler::DummySampler::new_boxed();
// When a component is registered, and there's an exit request that
// reached BHM, we want an exit signal to be delivered to the
// component's exit signal handler eventually. However, there's a race
// condition between the reception of `BackgroundHangMonitorControlMsg::
// Exit` and `MonitoredComponentMsg::Register` that needs to handled
// carefully. When the worker receives an `Exit` message, it stops
// processing messages, and any further `Register` messages sent to the
// worker thread are ignored. If the submissions of `Exit` and
// `Register` messages are far apart enough, the channel is closed by
// the time the client attempts to send a `Register` message, and
// therefore the client can figure out by `Sender::send`'s return value
// that it must deliver an exit signal. However, if these message
// submissions are close enough, the `Register` message is still sent,
// but the worker thread might exit before it sees the message, leaving
// the message unprocessed and the exit signal unsent.
//
// To fix this, we wrap the exit signal handler in an RAII wrapper of
// type `SignalToExitOnDrop` to automatically send a signal when it's
// dropped. This way, we can make sure the exit signal is sent even if
// the message couldn't reach the worker thread and be processed.
//
// However, as it turns out, `crossbeam-channel`'s channels don't drop
// remaining messages until all associated senders *and* receivers are
// dropped. This means the exit signal won't be delivered as long as
// there's at least one `HangMonitorRegister` or
// `BackgroundHangMonitorChan` maintaining a copy of the sender. To work
// around this and guarantee a rapid delivery of the exit signal, the
// sender is wrapped in `Arc`, and only the worker thread maintains a
// strong reference, thus ensuring both the sender and receiver are
// dropped as soon as the worker thread exits.
let exit_signal = SignalToExitOnDrop(exit_signal);
// If the tether is dropped after this call, the worker thread might
// exit before processing the `Register` message because there's no
// implicit ordering guarantee between two channels. If this happens,
// an exit signal will be sent despite we haven't received a
// corresponding exit request. To enforce the correct ordering and
// prevent a false exit signal from being sent, we include a copy of
// `self.tether` in the `Register` message.
let tether = self.tether.clone();
bhm_chan.send(MonitoredComponentMsg::Register(
sampler,
thread::current().name().map(str::to_owned),
transient_hang_timeout,
permanent_hang_timeout,
exit_signal,
tether,
));
Box::new(bhm_chan)
}
@@ -191,8 +135,7 @@ enum MonitoredComponentMsg {
Option<String>,
Duration,
Duration,
SignalToExitOnDrop,
Sender<Never>,
Box<dyn BackgroundHangMonitorExitSignal>,
),
/// Unregister component for monitoring.
Unregister,
@@ -202,54 +145,32 @@ enum MonitoredComponentMsg {
NotifyWait,
}
/// Stable equivalent to the `!` type
enum Never {}
/// A wrapper around a sender to the monitor,
/// which will send the Id of the monitored component along with each message,
/// and keep track of whether the monitor is still listening on the other end.
struct BackgroundHangMonitorChan {
sender: Weak<Sender<(MonitoredComponentId, MonitoredComponentMsg)>>,
_tether: Sender<Never>,
sender: MonitoredComponentSender,
component_id: MonitoredComponentId,
disconnected: Cell<bool>,
monitoring_enabled: bool,
}
impl BackgroundHangMonitorChan {
fn new(
sender: Weak<Sender<(MonitoredComponentId, MonitoredComponentMsg)>>,
tether: Sender<Never>,
sender: MonitoredComponentSender,
component_id: MonitoredComponentId,
monitoring_enabled: bool,
) -> Self {
BackgroundHangMonitorChan {
sender,
_tether: tether,
component_id,
disconnected: Default::default(),
monitoring_enabled,
}
}
fn send(&self, msg: MonitoredComponentMsg) {
if self.disconnected.get() {
return;
}
// The worker thread owns both the receiver *and* the only strong
// reference to the sender. An `upgrade` failure means the latter is
// gone, and a `send` failure means the former is gone. They are dropped
// simultaneously, but we might observe an intermediate state.
if self
.sender
.upgrade()
.and_then(|sender| sender.send((self.component_id.clone(), msg)).ok())
.is_none()
{
warn!("BackgroundHangMonitor has gone away");
self.disconnected.set(true);
}
self.sender
.send((self.component_id.clone(), msg))
.expect("BHM is gone");
}
}
@@ -272,33 +193,6 @@ impl BackgroundHangMonitor for BackgroundHangMonitorChan {
}
}
/// Wraps [`BackgroundHangMonitorExitSignal`] and calls `signal_to_exit` when
/// dropped.
struct SignalToExitOnDrop(Option<Box<dyn BackgroundHangMonitorExitSignal>>);
impl SignalToExitOnDrop {
/// Call `BackgroundHangMonitorExitSignal::signal_to_exit` now.
fn signal_to_exit(&mut self) {
if let Some(signal) = self.0.take() {
signal.signal_to_exit();
}
}
/// Disassociate `BackgroundHangMonitorExitSignal` from itself, preventing
/// `BackgroundHangMonitorExitSignal::signal_to_exit` from being called in
/// the future.
fn release(&mut self) {
self.0 = None;
}
}
impl Drop for SignalToExitOnDrop {
#[inline]
fn drop(&mut self) {
self.signal_to_exit();
}
}
struct MonitoredComponent {
sampler: Box<dyn Sampler>,
last_activity: Instant,
@@ -308,7 +202,7 @@ struct MonitoredComponent {
sent_transient_alert: bool,
sent_permanent_alert: bool,
is_waiting: bool,
exit_signal: SignalToExitOnDrop,
exit_signal: Box<dyn BackgroundHangMonitorExitSignal>,
}
struct Sample(MonitoredComponentId, Instant, NativeStack);
@@ -318,8 +212,6 @@ struct BackgroundHangMonitorWorker {
monitored_components: HashMap<MonitoredComponentId, MonitoredComponent>,
constellation_chan: IpcSender<HangMonitorAlert>,
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
_port_sender: Arc<Sender<(MonitoredComponentId, MonitoredComponentMsg)>>,
tether_port: Receiver<Never>,
control_port: Receiver<BackgroundHangMonitorControlMsg>,
sampling_duration: Option<Duration>,
sampling_max_duration: Option<Duration>,
@@ -328,6 +220,7 @@ struct BackgroundHangMonitorWorker {
sampling_baseline: Instant,
samples: VecDeque<Sample>,
monitoring_enabled: bool,
shutting_down: bool,
}
type MonitoredComponentSender = Sender<(MonitoredComponentId, MonitoredComponentMsg)>;
@@ -337,8 +230,7 @@ impl BackgroundHangMonitorWorker {
fn new(
constellation_chan: IpcSender<HangMonitorAlert>,
control_port: IpcReceiver<BackgroundHangMonitorControlMsg>,
(port_sender, port): (Arc<MonitoredComponentSender>, MonitoredComponentReceiver),
tether_port: Receiver<Never>,
port: MonitoredComponentReceiver,
monitoring_enabled: bool,
) -> Self {
let control_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(control_port);
@@ -347,8 +239,6 @@ impl BackgroundHangMonitorWorker {
monitored_components: Default::default(),
constellation_chan,
port,
_port_sender: port_sender,
tether_port,
control_port,
sampling_duration: None,
sampling_max_duration: None,
@@ -357,6 +247,7 @@ impl BackgroundHangMonitorWorker {
creation: Instant::now(),
samples: Default::default(),
monitoring_enabled,
shutting_down: Default::default(),
}
}
@@ -415,19 +306,14 @@ impl BackgroundHangMonitorWorker {
let received = select! {
recv(self.port) -> event => {
// Since we own the `Arc<Sender<_>>`, the channel never
// gets disconnected.
Some(event.unwrap())
},
recv(self.tether_port) -> _ => {
// This arm can only reached by a tether disconnection
// All associated `HangMonitorRegister` and
// `BackgroundHangMonitorChan` have been dropped. Suppress
// `signal_to_exit` and exit the BHM.
for component in self.monitored_components.values_mut() {
component.exit_signal.release();
if let Ok(event) = event {
Some(event)
} else {
// All senders have dropped,
// which means all monitored components have shut down,
// and so we can as well.
return false;
}
return false;
},
recv(self.control_port) -> event => {
match event {
@@ -444,16 +330,21 @@ impl BackgroundHangMonitorWorker {
}
None
},
Ok(BackgroundHangMonitorControlMsg::Exit(sender)) => {
Ok(BackgroundHangMonitorControlMsg::Exit) => {
for component in self.monitored_components.values_mut() {
component.exit_signal.signal_to_exit();
}
// Confirm exit with to the constellation.
let _ = sender.send(());
// Note the start of shutdown,
// to ensure exit propagates,
// even to components that have yet to register themselves,
// from this point on.
self.shutting_down = true;
// Also exit the BHM.
return false;
// Keep running; this worker thread will shutdown
// when the monitored components have shutdown,
// which we know has happened when `self.port` disconnects.
None
},
Err(_) => return false,
}
@@ -492,9 +383,16 @@ impl BackgroundHangMonitorWorker {
transient_hang_timeout,
permanent_hang_timeout,
exit_signal,
_tether,
),
) => {
// If we are shutting down,
// propagate it to the component,
// and register it(the component will unregister itself
// as part of handling the exit).
if self.shutting_down {
exit_signal.signal_to_exit();
}
let component = MonitoredComponent {
sampler,
last_activity: Instant::now(),
@@ -517,13 +415,9 @@ impl BackgroundHangMonitorWorker {
);
},
(component_id, MonitoredComponentMsg::Unregister) => {
let (_, mut component) = self
.monitored_components
self.monitored_components
.remove_entry(&component_id)
.expect("Received Unregister for an unknown component");
// Prevent `signal_to_exit` from being called
component.exit_signal.release();
},
(component_id, MonitoredComponentMsg::NotifyActivity(annotation)) => {
let component = self

View File

@@ -173,7 +173,7 @@ impl Sampler for LinuxSampler {
let ip = frame.ip();
let sp = frame.sp();
//This return value here determines whether we proceed to the next stack frame or not.
// This return value here determines whether we proceed to the next stack frame or not.
native_stack.process_register(ip, sp).is_ok()
})
};

View File

@@ -27,16 +27,23 @@ fn test_hang_monitoring() {
ipc::channel().expect("ipc channel failure");
let (_sampler_sender, sampler_receiver) = ipc::channel().expect("ipc channel failure");
let background_hang_monitor_register = HangMonitorRegister::init(
let (background_hang_monitor_register, join_handle) = HangMonitorRegister::init(
background_hang_monitor_ipc_sender.clone(),
sampler_receiver,
true,
);
struct BHMExitSignal;
impl BackgroundHangMonitorExitSignal for BHMExitSignal {
fn signal_to_exit(&self) {}
}
let background_hang_monitor = background_hang_monitor_register.register_component(
MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
Duration::from_millis(10),
Duration::from_millis(1000),
None,
Box::new(BHMExitSignal),
);
// Start an activity.
@@ -119,6 +126,11 @@ fn test_hang_monitoring() {
// Still no new alerts because the hang monitor has shut-down already.
assert!(background_hang_monitor_receiver.try_recv().is_err());
// Join on the worker thread(channels are dropped above).
join_handle
.join()
.expect("Failed to join on the BHM worker thread");
}
#[test]
@@ -131,16 +143,23 @@ fn test_hang_monitoring_unregister() {
ipc::channel().expect("ipc channel failure");
let (_sampler_sender, sampler_receiver) = ipc::channel().expect("ipc channel failure");
let background_hang_monitor_register = HangMonitorRegister::init(
let (background_hang_monitor_register, join_handle) = HangMonitorRegister::init(
background_hang_monitor_ipc_sender.clone(),
sampler_receiver,
true,
);
struct BHMExitSignal;
impl BackgroundHangMonitorExitSignal for BHMExitSignal {
fn signal_to_exit(&self) {}
}
let background_hang_monitor = background_hang_monitor_register.register_component(
MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
Duration::from_millis(10),
Duration::from_millis(1000),
None,
Box::new(BHMExitSignal),
);
// Start an activity.
@@ -155,6 +174,13 @@ fn test_hang_monitoring_unregister() {
// No new alert yet
assert!(background_hang_monitor_receiver.try_recv().is_err());
// Drop the channels and join on the worker thread.
drop(background_hang_monitor);
drop(background_hang_monitor_register);
join_handle
.join()
.expect("Failed to join on the BHM worker thread");
}
// Perform two certain steps in `test_hang_monitoring_exit_signal_inner` in
@@ -218,15 +244,13 @@ fn test_hang_monitoring_exit_signal_inner(op_order: fn(&mut dyn FnMut(), &mut dy
}));
// Init a worker, without active monitoring.
let background_hang_monitor_register = HangMonitorRegister::init(
let (background_hang_monitor_register, join_handle) = HangMonitorRegister::init(
background_hang_monitor_ipc_sender.clone(),
control_receiver,
false,
);
let mut background_hang_monitor = None;
let (exit_sender, exit_receiver) = ipc::channel().expect("Failed to create IPC channel!");
let mut exit_sender = Some(exit_sender);
// `op_order` determines the order in which these two closures are
// executed.
@@ -237,24 +261,26 @@ fn test_hang_monitoring_exit_signal_inner(op_order: fn(&mut dyn FnMut(), &mut dy
MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
Duration::from_millis(10),
Duration::from_millis(1000),
Some(signal.take().unwrap()),
signal.take().unwrap(),
));
},
&mut || {
// Send the exit message.
control_sender
.send(BackgroundHangMonitorControlMsg::Exit(
exit_sender.take().unwrap(),
))
.send(BackgroundHangMonitorControlMsg::Exit)
.unwrap();
},
);
// Assert we receive a confirmation back.
assert!(exit_receiver.recv().is_ok());
// Assert we get the exit signal.
while !closing.load(Ordering::SeqCst) {
thread::sleep(Duration::from_millis(10));
}
// Drop the channels and join on the worker thread.
drop(background_hang_monitor);
drop(background_hang_monitor_register);
join_handle
.join()
.expect("Failed to join on the BHM worker thread");
}

View File

@@ -11,24 +11,39 @@ rust-version.workspace = true
name = "canvas"
path = "lib.rs"
[features]
vello = ["dep:vello", "dep:pollster", "dep:futures-intrusive", "dep:peniko"]
vello_cpu = ["dep:vello_cpu", "dep:peniko"]
raqote = ["dep:raqote", "dep:font-kit"]
tracing = ["dep:tracing"]
[dependencies]
app_units = { workspace = true }
bytemuck = { workspace = true, features = ["extern_crate_alloc"] }
canvas_traits = { workspace = true }
compositing_traits = { workspace = true }
crossbeam-channel = { workspace = true }
cssparser = { workspace = true }
euclid = { workspace = true }
font-kit = "0.14"
fonts = { path = "../fonts" }
font-kit = { version = "0.14", optional = true }
fonts = { path = "../fonts", default-features = false }
ipc-channel = { workspace = true }
kurbo = { workspace = true }
log = { workspace = true }
lyon_geom = "1.0.4"
net_traits = { workspace = true }
peniko = { workspace = true, optional = true }
pixels = { path = "../pixels" }
range = { path = "../range" }
raqote = "0.8.5"
raqote = { version = "0.8.5", optional = true }
servo_arc = { workspace = true }
stylo = { workspace = true }
unicode-script = { workspace = true }
webrender_api = { workspace = true }
servo_config = { path = "../config" }
vello = { workspace = true, optional = true }
vello_cpu = { workspace = true, optional = true }
pollster = { version = "0.4", optional = true }
futures-intrusive = { version = "0.5", optional = true }
tracing = { workspace = true, optional = true }
servo-tracing = { workspace = true }

View File

@@ -3,135 +3,100 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use canvas_traits::canvas::{
CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, Path,
CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions,
};
use compositing_traits::SerializableImageData;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
use pixels::Snapshot;
use style::color::AbsoluteColor;
use webrender_api::ImageDescriptor;
use crate::canvas_data::{CanvasPaintState, Filter, TextRun};
pub(crate) trait Backend: Clone + Sized {
type Pattern<'a>: PatternHelpers + Clone;
type StrokeOptions: StrokeOptionsHelpers + Clone;
type Color: Clone;
type DrawOptions: DrawOptionsHelpers + Clone;
type CompositionOp;
type DrawTarget: GenericDrawTarget<Self>;
type SourceSurface;
type GradientStop;
type GradientStops;
fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp;
fn need_to_draw_shadow(&self, color: &Self::Color) -> bool;
fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>);
fn set_fill_style(
&mut self,
style: FillOrStrokeStyle,
state: &mut CanvasPaintState<'_, Self>,
drawtarget: &Self::DrawTarget,
);
fn set_stroke_style(
&mut self,
style: FillOrStrokeStyle,
state: &mut CanvasPaintState<'_, Self>,
drawtarget: &Self::DrawTarget,
);
fn set_global_composition(
&mut self,
op: CompositionOrBlending,
state: &mut CanvasPaintState<'_, Self>,
);
fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget;
fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self>;
}
use crate::canvas_data::{Filter, TextRun};
// This defines required methods for a DrawTarget (currently only implemented for raqote). The
// prototypes are derived from the now-removed Azure backend's methods.
pub(crate) trait GenericDrawTarget<B: Backend> {
fn clear_rect(&mut self, rect: &Rect<f32>);
pub(crate) trait GenericDrawTarget {
type SourceSurface;
fn new(size: Size2D<u32>) -> Self;
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>);
fn copy_surface(
&mut self,
surface: B::SourceSurface,
surface: Self::SourceSurface,
source: Rect<i32>,
destination: Point2D<i32>,
);
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<B::SourceSurface>;
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface>;
fn draw_surface(
&mut self,
surface: B::SourceSurface,
surface: Self::SourceSurface,
dest: Rect<f64>,
source: Rect<f64>,
filter: Filter,
draw_options: &B::DrawOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
);
fn draw_surface_with_shadow(
&self,
surface: B::SourceSurface,
surface: Self::SourceSurface,
dest: &Point2D<f32>,
color: &B::Color,
offset: &Vector2D<f32>,
sigma: f32,
operator: B::CompositionOp,
shadow_options: ShadowOptions,
composition_options: CompositionOptions,
);
fn fill(
&mut self,
path: &Path,
fill_rule: FillRule,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
);
fn fill(&mut self, path: &Path, pattern: &B::Pattern<'_>, draw_options: &B::DrawOptions);
fn fill_text(
&mut self,
text_runs: Vec<TextRun>,
start: Point2D<f32>,
pattern: &B::Pattern<'_>,
draw_options: &B::DrawOptions,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
);
fn fill_rect(
&mut self,
rect: &Rect<f32>,
pattern: &B::Pattern<'_>,
draw_options: &B::DrawOptions,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
);
fn get_size(&self) -> Size2D<i32>;
fn get_transform(&self) -> Transform2D<f32>;
fn pop_clip(&mut self);
fn push_clip(&mut self, path: &Path);
fn push_clip(&mut self, path: &Path, fill_rule: FillRule, transform: Transform2D<f64>);
fn push_clip_rect(&mut self, rect: &Rect<i32>);
fn set_transform(&mut self, matrix: &Transform2D<f32>);
fn stroke(
&mut self,
path: &Path,
pattern: &B::Pattern<'_>,
stroke_options: &B::StrokeOptions,
draw_options: &B::DrawOptions,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
);
fn stroke_rect(
&mut self,
rect: &Rect<f32>,
pattern: &B::Pattern<'_>,
stroke_options: &B::StrokeOptions,
draw_options: &B::DrawOptions,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
);
fn surface(&self) -> B::SourceSurface;
fn image_descriptor_and_serializable_data(&self) -> (ImageDescriptor, SerializableImageData);
fn snapshot(&self) -> Snapshot;
fn surface(&mut self) -> Self::SourceSurface;
fn image_descriptor_and_serializable_data(
&mut self,
) -> (ImageDescriptor, SerializableImageData);
fn snapshot(&mut self) -> Snapshot;
}
pub(crate) trait PatternHelpers {
fn is_zero_size_gradient(&self) -> bool;
fn x_bound(&self) -> Option<u32>;
fn y_bound(&self) -> Option<u32>;
}
pub(crate) trait StrokeOptionsHelpers {
fn set_line_width(&mut self, _val: f32);
fn set_miter_limit(&mut self, _val: f32);
fn set_line_join(&mut self, val: LineJoinStyle);
fn set_line_cap(&mut self, val: LineCapStyle);
fn set_line_dash(&mut self, items: Vec<f32>);
fn set_line_dash_offset(&mut self, offset: f32);
}
pub(crate) trait DrawOptionsHelpers {
fn set_alpha(&mut self, val: f32);
fn is_clear(&self) -> bool;
#[allow(dead_code)] // used by gated backends
/// A version of the `Into<T>` trait from the standard library that can be used
/// to convert between two types that are not defined in the canvas crate.
pub(crate) trait Convert<T> {
fn convert(self) -> T;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,18 @@
#![deny(unsafe_code)]
mod backend;
#[cfg(feature = "raqote")]
mod raqote_backend;
#[cfg(any(feature = "vello", feature = "vello_cpu"))]
mod peniko_conversions;
#[cfg(feature = "vello")]
mod vello_backend;
#[cfg(feature = "vello_cpu")]
mod vello_cpu_backend;
pub mod canvas_data;
pub mod canvas_paint_thread;

View File

@@ -0,0 +1,225 @@
/* 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 canvas_traits::canvas::*;
use pixels::{SnapshotAlphaMode, SnapshotPixelFormat};
use style::color::AbsoluteColor;
use crate::backend::Convert;
use crate::canvas_data::Filter;
impl Convert<peniko::Font> for fonts::FontDataAndIndex {
fn convert(self) -> peniko::Font {
use std::sync::Arc;
peniko::Font::new(peniko::Blob::new(Arc::new(self.data)), self.index)
}
}
impl Convert<kurbo::Join> for LineJoinStyle {
fn convert(self) -> kurbo::Join {
match self {
LineJoinStyle::Round => kurbo::Join::Round,
LineJoinStyle::Bevel => kurbo::Join::Bevel,
LineJoinStyle::Miter => kurbo::Join::Miter,
}
}
}
impl Convert<kurbo::Cap> for LineCapStyle {
fn convert(self) -> kurbo::Cap {
match self {
LineCapStyle::Butt => kurbo::Cap::Butt,
LineCapStyle::Round => kurbo::Cap::Round,
LineCapStyle::Square => kurbo::Cap::Square,
}
}
}
impl Convert<peniko::Color> for AbsoluteColor {
fn convert(self) -> peniko::Color {
let srgb = self.into_srgb_legacy();
peniko::Color::new([
srgb.components.0,
srgb.components.1,
srgb.components.2,
srgb.alpha,
])
}
}
impl Convert<peniko::BlendMode> for CompositionOrBlending {
fn convert(self) -> peniko::BlendMode {
match self {
CompositionOrBlending::Composition(composition_style) => {
composition_style.convert().into()
},
CompositionOrBlending::Blending(blending_style) => blending_style.convert().into(),
}
}
}
impl Convert<peniko::Compose> for CompositionStyle {
fn convert(self) -> peniko::Compose {
match self {
CompositionStyle::SourceIn => peniko::Compose::SrcIn,
CompositionStyle::SourceOut => peniko::Compose::SrcOut,
CompositionStyle::SourceOver => peniko::Compose::SrcOver,
CompositionStyle::SourceAtop => peniko::Compose::SrcAtop,
CompositionStyle::DestinationIn => peniko::Compose::DestIn,
CompositionStyle::DestinationOut => peniko::Compose::DestOut,
CompositionStyle::DestinationOver => peniko::Compose::DestOver,
CompositionStyle::DestinationAtop => peniko::Compose::DestAtop,
CompositionStyle::Copy => peniko::Compose::Copy,
CompositionStyle::Lighter => peniko::Compose::Plus,
CompositionStyle::Xor => peniko::Compose::Xor,
CompositionStyle::Clear => peniko::Compose::Clear,
}
}
}
impl Convert<peniko::Mix> for BlendingStyle {
fn convert(self) -> peniko::Mix {
match self {
BlendingStyle::Multiply => peniko::Mix::Multiply,
BlendingStyle::Screen => peniko::Mix::Screen,
BlendingStyle::Overlay => peniko::Mix::Overlay,
BlendingStyle::Darken => peniko::Mix::Darken,
BlendingStyle::Lighten => peniko::Mix::Lighten,
BlendingStyle::ColorDodge => peniko::Mix::ColorDodge,
BlendingStyle::ColorBurn => peniko::Mix::ColorBurn,
BlendingStyle::HardLight => peniko::Mix::HardLight,
BlendingStyle::SoftLight => peniko::Mix::SoftLight,
BlendingStyle::Difference => peniko::Mix::Difference,
BlendingStyle::Exclusion => peniko::Mix::Exclusion,
BlendingStyle::Hue => peniko::Mix::Hue,
BlendingStyle::Saturation => peniko::Mix::Saturation,
BlendingStyle::Color => peniko::Mix::Color,
BlendingStyle::Luminosity => peniko::Mix::Luminosity,
}
}
}
impl Convert<kurbo::Stroke> for LineOptions {
fn convert(self) -> kurbo::Stroke {
let LineOptions {
width,
cap_style,
join_style,
miter_limit,
dash,
dash_offset,
} = self;
kurbo::Stroke {
width,
join: join_style.convert(),
miter_limit,
start_cap: cap_style.convert(),
end_cap: cap_style.convert(),
dash_pattern: dash.iter().map(|x| *x as f64).collect(),
dash_offset,
}
}
}
impl Convert<peniko::Brush> for FillOrStrokeStyle {
fn convert(self) -> peniko::Brush {
use canvas_traits::canvas::FillOrStrokeStyle::*;
match self {
Color(absolute_color) => peniko::Brush::Solid(absolute_color.convert()),
LinearGradient(style) => {
let start = kurbo::Point::new(style.x0, style.y0);
let end = kurbo::Point::new(style.x1, style.y1);
let mut gradient = peniko::Gradient::new_linear(start, end);
gradient.stops = style.stops.convert();
peniko::Brush::Gradient(gradient)
},
RadialGradient(style) => {
let center1 = kurbo::Point::new(style.x0, style.y0);
let center2 = kurbo::Point::new(style.x1, style.y1);
let mut gradient = peniko::Gradient::new_two_point_radial(
center1,
style.r0 as f32,
center2,
style.r1 as f32,
);
gradient.stops = style.stops.convert();
peniko::Brush::Gradient(gradient)
},
Surface(surface_style) => {
let data = surface_style
.surface_data
.to_owned()
.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: false,
}),
Some(SnapshotPixelFormat::RGBA),
)
.0;
peniko::Brush::Image(peniko::Image {
data: peniko::Blob::from(data),
format: peniko::ImageFormat::Rgba8,
width: surface_style.surface_size.width,
height: surface_style.surface_size.height,
x_extend: if surface_style.repeat_x {
peniko::Extend::Repeat
} else {
peniko::Extend::Pad
},
y_extend: if surface_style.repeat_y {
peniko::Extend::Repeat
} else {
peniko::Extend::Pad
},
quality: peniko::ImageQuality::Low,
alpha: 1.0,
})
},
}
}
}
impl Convert<peniko::color::DynamicColor> for AbsoluteColor {
fn convert(self) -> peniko::color::DynamicColor {
peniko::color::DynamicColor::from_alpha_color(self.convert())
}
}
impl Convert<peniko::ColorStop> for CanvasGradientStop {
fn convert(self) -> peniko::ColorStop {
peniko::ColorStop {
offset: self.offset as f32,
color: self.color.convert(),
}
}
}
impl Convert<peniko::ColorStops> for Vec<CanvasGradientStop> {
fn convert(self) -> peniko::ColorStops {
let mut stops = peniko::ColorStops(self.into_iter().map(|item| item.convert()).collect());
// https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap
stops
.0
.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap());
stops
}
}
impl Convert<peniko::ImageQuality> for Filter {
fn convert(self) -> peniko::ImageQuality {
match self {
Filter::Bilinear => peniko::ImageQuality::Medium,
Filter::Nearest => peniko::ImageQuality::Low,
}
}
}
impl Convert<peniko::Fill> for FillRule {
fn convert(self) -> peniko::Fill {
match self {
FillRule::Nonzero => peniko::Fill::NonZero,
FillRule::Evenodd => peniko::Fill::EvenOdd,
}
}
}

View File

@@ -8,21 +8,19 @@ use std::collections::HashMap;
use canvas_traits::canvas::*;
use compositing_traits::SerializableImageData;
use cssparser::color::clamp_unit_f32;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
use font_kit::font::Font;
use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods};
use ipc_channel::ipc::IpcSharedMemory;
use log::warn;
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
use range::Range;
use raqote::PathBuilder;
use raqote::{DrawOptions, PathBuilder, StrokeStyle};
use style::color::AbsoluteColor;
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
use crate::backend::{
Backend, DrawOptionsHelpers, GenericDrawTarget, PatternHelpers, StrokeOptionsHelpers,
};
use crate::canvas_data::{CanvasPaintState, Filter, TextRun};
use crate::backend::GenericDrawTarget;
use crate::canvas_data::{Filter, TextRun};
thread_local! {
/// The shared font cache used by all canvases that render on a thread. It would be nicer
@@ -32,86 +30,6 @@ thread_local! {
static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, Font>> = RefCell::default();
}
#[derive(Clone, Default)]
pub(crate) struct RaqoteBackend;
impl Backend for RaqoteBackend {
type Pattern<'a> = Pattern;
type StrokeOptions = raqote::StrokeStyle;
type Color = raqote::SolidSource;
type DrawOptions = raqote::DrawOptions;
type CompositionOp = raqote::BlendMode;
type DrawTarget = raqote::DrawTarget;
type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?)
type GradientStop = raqote::GradientStop;
type GradientStops = Vec<raqote::GradientStop>;
fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp {
opts.blend_mode
}
fn need_to_draw_shadow(&self, color: &Self::Color) -> bool {
color.a != 0
}
fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>) {
state.shadow_color = color.to_raqote_style();
}
fn set_fill_style(
&mut self,
style: FillOrStrokeStyle,
state: &mut CanvasPaintState<'_, Self>,
_drawtarget: &Self::DrawTarget,
) {
if let Some(pattern) = style.to_raqote_pattern() {
state.fill_style = pattern;
}
}
fn set_stroke_style(
&mut self,
style: FillOrStrokeStyle,
state: &mut CanvasPaintState<'_, Self>,
_drawtarget: &Self::DrawTarget,
) {
if let Some(pattern) = style.to_raqote_pattern() {
state.stroke_style = pattern;
}
}
fn set_global_composition(
&mut self,
op: CompositionOrBlending,
state: &mut CanvasPaintState<'_, Self>,
) {
state.draw_options.blend_mode = op.to_raqote_style();
}
fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget {
raqote::DrawTarget::new(size.width as i32, size.height as i32)
}
fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self> {
let pattern = Pattern::Color(255, 0, 0, 0);
CanvasPaintState {
draw_options: raqote::DrawOptions::new(),
fill_style: pattern.clone(),
stroke_style: pattern,
stroke_opts: Default::default(),
transform: Transform2D::identity(),
shadow_offset_x: 0.0,
shadow_offset_y: 0.0,
shadow_blur: 0.0,
shadow_color: raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0),
font_style: None,
text_align: TextAlign::default(),
text_baseline: TextBaseline::default(),
_backend: std::marker::PhantomData,
}
}
}
#[derive(Clone)]
pub enum Pattern {
// argb
@@ -170,7 +88,6 @@ pub struct SurfacePattern {
image: Snapshot,
filter: raqote::FilterMode,
extend: raqote::ExtendMode,
repeat: Repetition,
transform: Transform2D<f32>,
}
@@ -191,14 +108,9 @@ impl SurfacePattern {
image,
filter,
extend,
repeat,
transform,
}
}
pub fn repetition(&self) -> &Repetition {
&self.repeat
}
}
#[derive(Clone, Copy, Debug)]
@@ -223,7 +135,7 @@ impl Repetition {
}
}
pub fn source(pattern: &Pattern) -> raqote::Source {
pub fn source(pattern: &Pattern) -> raqote::Source<'_> {
match pattern {
Pattern::Color(a, r, g, b) => raqote::Source::Solid(
raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b),
@@ -242,93 +154,16 @@ pub fn source(pattern: &Pattern) -> raqote::Source {
pattern.radius2,
raqote::Spread::Pad,
),
Pattern::Surface(pattern) => {
#[allow(unsafe_code)]
let data = unsafe {
let data = pattern.image.as_raw_bytes();
std::slice::from_raw_parts(
data.as_ptr() as *const u32,
data.len() / std::mem::size_of::<u32>(),
)
};
raqote::Source::Image(
raqote::Image {
width: pattern.image.size().width as i32,
height: pattern.image.size().height as i32,
data,
},
pattern.extend,
pattern.filter,
pattern.transform,
)
},
}
}
impl PatternHelpers for Pattern {
fn is_zero_size_gradient(&self) -> bool {
match self {
Pattern::RadialGradient(pattern) => {
let centers_equal = pattern.center1 == pattern.center2;
let radii_equal = pattern.radius1 == pattern.radius2;
(centers_equal && radii_equal) || pattern.gradient.stops.is_empty()
Pattern::Surface(pattern) => raqote::Source::Image(
raqote::Image {
width: pattern.image.size().width as i32,
height: pattern.image.size().height as i32,
data: bytemuck::cast_slice(pattern.image.as_raw_bytes()),
},
Pattern::LinearGradient(pattern) => {
(pattern.start == pattern.end) || pattern.gradient.stops.is_empty()
},
Pattern::Color(..) | Pattern::Surface(..) => false,
}
}
fn x_bound(&self) -> Option<u32> {
match self {
Pattern::Surface(pattern) => match pattern.repetition() {
Repetition::RepeatX | Repetition::Repeat => None, // x is not bounded
Repetition::RepeatY | Repetition::NoRepeat => Some(pattern.image.size().width),
},
Pattern::Color(..) | Pattern::LinearGradient(..) | Pattern::RadialGradient(..) => None,
}
}
fn y_bound(&self) -> Option<u32> {
match self {
Pattern::Surface(pattern) => match pattern.repetition() {
Repetition::RepeatY | Repetition::Repeat => None, // y is not bounded
Repetition::RepeatX | Repetition::NoRepeat => Some(pattern.image.size().height),
},
Pattern::Color(..) | Pattern::LinearGradient(..) | Pattern::RadialGradient(..) => None,
}
}
}
impl StrokeOptionsHelpers for raqote::StrokeStyle {
fn set_line_width(&mut self, _val: f32) {
self.width = _val;
}
fn set_miter_limit(&mut self, _val: f32) {
self.miter_limit = _val;
}
fn set_line_join(&mut self, val: LineJoinStyle) {
self.join = val.to_raqote_style();
}
fn set_line_cap(&mut self, val: LineCapStyle) {
self.cap = val.to_raqote_style();
}
fn set_line_dash(&mut self, items: Vec<f32>) {
self.dash_array = items;
}
fn set_line_dash_offset(&mut self, offset: f32) {
self.dash_offset = offset;
}
}
impl DrawOptionsHelpers for raqote::DrawOptions {
fn set_alpha(&mut self, val: f32) {
self.alpha = val;
}
fn is_clear(&self) -> bool {
matches!(self.blend_mode, raqote::BlendMode::Clear)
pattern.extend,
pattern.filter,
pattern.transform,
),
}
}
@@ -342,150 +177,133 @@ fn create_gradient_stops(gradient_stops: Vec<CanvasGradientStop>) -> Vec<raqote:
stops
}
impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
fn clear_rect(&mut self, rect: &Rect<f32>) {
let rect = rect.cast();
let mut pb = canvas_traits::canvas::Path::new();
pb.rect(
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
);
let mut options = raqote::DrawOptions::new();
options.blend_mode = raqote::BlendMode::Clear;
let pattern = Pattern::Color(0, 0, 0, 0);
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb, &pattern, &options);
impl GenericDrawTarget for raqote::DrawTarget {
type SourceSurface = Vec<u32>; // TODO: See if we can avoid the alloc (probably?)
fn new(size: Size2D<u32>) -> Self {
raqote::DrawTarget::new(size.width as i32, size.height as i32)
}
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) {
<Self as GenericDrawTarget>::fill_rect(
self,
rect,
FillOrStrokeStyle::Color(AbsoluteColor::TRANSPARENT_BLACK),
CompositionOptions {
alpha: 1.0,
composition_operation: CompositionOrBlending::Composition(CompositionStyle::Clear),
},
transform,
);
}
#[allow(unsafe_code)]
fn copy_surface(
&mut self,
surface: <RaqoteBackend as Backend>::SourceSurface,
surface: Self::SourceSurface,
source: Rect<i32>,
destination: Point2D<i32>,
) {
let mut dt = raqote::DrawTarget::new(source.size.width, source.size.height);
let data = surface;
let s = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) };
dt.get_data_mut().copy_from_slice(s);
let dt = raqote::DrawTarget::from_vec(source.size.width, source.size.height, surface);
raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination);
}
fn create_similar_draw_target(
&self,
size: &Size2D<i32>,
) -> <RaqoteBackend as Backend>::DrawTarget {
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
raqote::DrawTarget::new(size.width, size.height)
}
fn create_source_surface_from_data(
&self,
data: Snapshot,
) -> Option<<RaqoteBackend as Backend>::SourceSurface> {
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface> {
Some(
data.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: true,
}),
Some(SnapshotPixelFormat::BGRA),
bytemuck::try_cast_vec(
data.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: true,
}),
Some(SnapshotPixelFormat::BGRA),
)
.0,
)
.0,
.unwrap_or_else(|(_, surface)| bytemuck::pod_collect_to_vec(&surface)),
)
}
#[allow(unsafe_code)]
fn draw_surface(
&mut self,
surface: <RaqoteBackend as Backend>::SourceSurface,
surface: Self::SourceSurface,
dest: Rect<f64>,
source: Rect<f64>,
src: Rect<f64>,
filter: Filter,
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
let image = Snapshot::from_vec(
source.size.cast(),
SnapshotPixelFormat::BGRA,
SnapshotAlphaMode::Transparent {
premultiplied: true,
},
surface,
);
let transform =
let paint_transform =
raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32)
.then_scale(
source.size.width as f32 / dest.size.width as f32,
source.size.height as f32 / dest.size.height as f32,
src.size.width as f32 / dest.size.width as f32,
src.size.height as f32 / dest.size.height as f32,
);
let pattern = Pattern::Surface(SurfacePattern::new(
image,
filter.to_raqote(),
Repetition::NoRepeat,
transform,
));
let mut pb = canvas_traits::canvas::Path::new();
self.set_transform(&transform.cast());
let dest = dest.cast();
let mut pb = raqote::PathBuilder::new();
pb.rect(
dest.origin.x,
dest.origin.y,
dest.size.width,
dest.size.height,
);
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb, &pattern, draw_options);
let size = src.size.cast();
fill_draw_target(
self,
draw_options(composition_options),
&raqote::Source::Image(
raqote::Image {
width: size.width,
height: size.height,
data: &surface,
},
raqote::ExtendMode::Pad,
filter.to_raqote(),
paint_transform,
),
pb.finish(),
);
}
fn draw_surface_with_shadow(
&self,
_surface: <RaqoteBackend as Backend>::SourceSurface,
_surface: Self::SourceSurface,
_dest: &Point2D<f32>,
_color: &<RaqoteBackend as Backend>::Color,
_offset: &Vector2D<f32>,
_sigma: f32,
_operator: <RaqoteBackend as Backend>::CompositionOp,
_shadow_options: ShadowOptions,
_composition_options: CompositionOptions,
) {
warn!("no support for drawing shadows");
}
fn fill(
&mut self,
path: &canvas_traits::canvas::Path,
pattern: &Pattern,
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
fill_rule: FillRule,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
let path = to_path(path);
match draw_options.blend_mode {
raqote::BlendMode::Src => {
self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0));
self.fill(&path, &source(pattern), draw_options);
},
raqote::BlendMode::Clear |
raqote::BlendMode::SrcAtop |
raqote::BlendMode::DstOut |
raqote::BlendMode::Add |
raqote::BlendMode::Xor |
raqote::BlendMode::DstOver |
raqote::BlendMode::SrcOver => {
self.fill(&path, &source(pattern), draw_options);
},
raqote::BlendMode::SrcIn |
raqote::BlendMode::SrcOut |
raqote::BlendMode::DstIn |
raqote::BlendMode::DstAtop => {
let mut options = *draw_options;
self.push_layer_with_blend(1., options.blend_mode);
options.blend_mode = raqote::BlendMode::SrcOver;
self.fill(&path, &source(pattern), &options);
self.pop_layer();
},
_ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode),
}
self.set_transform(&transform.cast());
let draw_options = draw_options(composition_options);
let pattern = style.to_raqote_pattern();
let mut path = to_path(path);
path.winding = match fill_rule {
FillRule::Nonzero => raqote::Winding::NonZero,
FillRule::Evenodd => raqote::Winding::EvenOdd,
};
fill_draw_target(self, draw_options, &source(&pattern), path);
}
fn fill_text(
&mut self,
text_runs: Vec<TextRun>,
start: Point2D<f32>,
pattern: &Pattern,
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.set_transform(&transform.cast());
let draw_options = draw_options(composition_options);
let pattern = style.to_raqote_pattern();
let mut advance = 0.;
for run in text_runs.iter() {
let mut positions = Vec::new();
@@ -515,8 +333,11 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
SHARED_FONT_CACHE.with(|font_cache| {
let identifier = template.identifier();
if !font_cache.borrow().contains_key(&identifier) {
let data = std::sync::Arc::new(run.font.data().as_ref().to_vec());
let Ok(font) = Font::from_bytes(data, identifier.index()) else {
let Ok(font_data_and_index) = run.font.font_data_and_index() else {
return;
};
let data = std::sync::Arc::new(font_data_and_index.data.as_ref().to_vec());
let Ok(font) = Font::from_bytes(data, font_data_and_index.index) else {
return;
};
font_cache.borrow_mut().insert(identifier.clone(), font);
@@ -532,8 +353,8 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
run.font.descriptor.pt_size.to_f32_px(),
&ids,
&positions,
&source(pattern),
draw_options,
&source(&pattern),
&draw_options,
);
})
}
@@ -542,8 +363,9 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
fn fill_rect(
&mut self,
rect: &Rect<f32>,
pattern: &<RaqoteBackend as Backend>::Pattern<'_>,
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
let rect = rect.cast();
let mut pb = canvas_traits::canvas::Path::new();
@@ -554,50 +376,70 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
rect.size.height,
);
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb, pattern, draw_options);
<Self as GenericDrawTarget>::fill(
self,
&pb,
FillRule::Nonzero,
style,
composition_options,
transform,
);
}
fn get_size(&self) -> Size2D<i32> {
Size2D::new(self.width(), self.height())
}
fn get_transform(&self) -> Transform2D<f32> {
*self.get_transform()
}
fn pop_clip(&mut self) {
self.pop_clip();
}
fn push_clip(&mut self, path: &canvas_traits::canvas::Path) {
self.push_clip(&to_path(path));
fn push_clip(
&mut self,
path: &canvas_traits::canvas::Path,
fill_rule: FillRule,
transform: Transform2D<f64>,
) {
self.set_transform(&transform.cast());
let mut path = to_path(path);
path.winding = match fill_rule {
FillRule::Nonzero => raqote::Winding::NonZero,
FillRule::Evenodd => raqote::Winding::EvenOdd,
};
self.push_clip(&path);
}
fn push_clip_rect(&mut self, rect: &Rect<i32>) {
self.push_clip_rect(rect.to_box2d());
}
fn set_transform(&mut self, matrix: &Transform2D<f32>) {
self.set_transform(matrix);
}
fn surface(&self) -> <RaqoteBackend as Backend>::SourceSurface {
self.get_data_u8().to_vec()
fn surface(&mut self) -> Self::SourceSurface {
self.get_data().to_vec()
}
fn stroke(
&mut self,
path: &canvas_traits::canvas::Path,
pattern: &Pattern,
stroke_options: &<RaqoteBackend as Backend>::StrokeOptions,
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
let pattern = style.to_raqote_pattern();
let options = draw_options(composition_options);
self.set_transform(&transform.cast());
self.stroke(
&to_path(path),
&source(pattern),
stroke_options,
draw_options,
&source(&pattern),
&line_options.to_raqote_style(),
&options,
);
}
fn stroke_rect(
&mut self,
rect: &Rect<f32>,
pattern: &<RaqoteBackend as Backend>::Pattern<'_>,
stroke_options: &<RaqoteBackend as Backend>::StrokeOptions,
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.set_transform(&transform.cast());
let pattern = style.to_raqote_pattern();
let options = draw_options(composition_options);
let mut pb = raqote::PathBuilder::new();
pb.rect(
rect.origin.x,
@@ -606,11 +448,16 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
rect.size.height,
);
self.stroke(&pb.finish(), &source(pattern), stroke_options, draw_options);
self.stroke(
&pb.finish(),
&source(&pattern),
&line_options.to_raqote_style(),
&options,
);
}
fn image_descriptor_and_serializable_data(
&self,
&mut self,
) -> (
webrender_api::ImageDescriptor,
compositing_traits::SerializableImageData,
@@ -626,7 +473,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
(descriptor, data)
}
fn snapshot(&self) -> Snapshot {
fn snapshot(&mut self) -> Snapshot {
Snapshot::from_vec(
self.get_size().cast(),
SnapshotPixelFormat::BGRA,
@@ -638,6 +485,40 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
}
}
fn fill_draw_target(
draw_target: &mut raqote::DrawTarget,
draw_options: DrawOptions,
source: &raqote::Source<'_>,
path: raqote::Path,
) {
match draw_options.blend_mode {
raqote::BlendMode::Src => {
draw_target.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0));
draw_target.fill(&path, source, &draw_options);
},
raqote::BlendMode::Clear |
raqote::BlendMode::SrcAtop |
raqote::BlendMode::DstOut |
raqote::BlendMode::Add |
raqote::BlendMode::Xor |
raqote::BlendMode::DstOver |
raqote::BlendMode::SrcOver => {
draw_target.fill(&path, source, &draw_options);
},
raqote::BlendMode::SrcIn |
raqote::BlendMode::SrcOut |
raqote::BlendMode::DstIn |
raqote::BlendMode::DstAtop => {
let mut options = draw_options;
draw_target.push_layer_with_blend(1., options.blend_mode);
options.blend_mode = raqote::BlendMode::SrcOver;
draw_target.fill(&path, source, &options);
draw_target.pop_layer();
},
_ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode),
}
}
impl Filter {
fn to_raqote(self) -> raqote::FilterMode {
match self {
@@ -676,6 +557,29 @@ pub trait ToRaqoteStyle {
fn to_raqote_style(self) -> Self::Target;
}
impl ToRaqoteStyle for LineOptions {
type Target = StrokeStyle;
fn to_raqote_style(self) -> Self::Target {
let LineOptions {
width,
cap_style,
join_style,
miter_limit,
dash,
dash_offset,
} = self;
StrokeStyle {
width: width as f32,
cap: cap_style.to_raqote_style(),
join: join_style.to_raqote_style(),
miter_limit: miter_limit as f32,
dash_array: dash,
dash_offset: dash_offset as f32,
}
}
}
impl ToRaqoteStyle for LineJoinStyle {
type Target = raqote::LineJoin;
@@ -701,7 +605,7 @@ impl ToRaqoteStyle for LineCapStyle {
}
pub trait ToRaqotePattern {
fn to_raqote_pattern(self) -> Option<Pattern>;
fn to_raqote_pattern(self) -> Pattern;
}
pub trait ToRaqoteGradientStop {
@@ -723,39 +627,36 @@ impl ToRaqoteGradientStop for CanvasGradientStop {
}
impl ToRaqotePattern for FillOrStrokeStyle {
#[allow(unsafe_code)]
fn to_raqote_pattern(self) -> Option<Pattern> {
fn to_raqote_pattern(self) -> Pattern {
use canvas_traits::canvas::FillOrStrokeStyle::*;
match self {
Color(color) => {
let srgb = color.into_srgb_legacy();
Some(Pattern::Color(
Pattern::Color(
clamp_unit_f32(srgb.alpha),
clamp_unit_f32(srgb.components.0),
clamp_unit_f32(srgb.components.1),
clamp_unit_f32(srgb.components.2),
))
)
},
LinearGradient(style) => {
let start = Point2D::new(style.x0 as f32, style.y0 as f32);
let end = Point2D::new(style.x1 as f32, style.y1 as f32);
let stops = create_gradient_stops(style.stops);
Some(Pattern::LinearGradient(LinearGradientPattern::new(
start, end, stops,
)))
Pattern::LinearGradient(LinearGradientPattern::new(start, end, stops))
},
RadialGradient(style) => {
let center1 = Point2D::new(style.x0 as f32, style.y0 as f32);
let center2 = Point2D::new(style.x1 as f32, style.y1 as f32);
let stops = create_gradient_stops(style.stops);
Some(Pattern::RadialGradient(RadialGradientPattern::new(
Pattern::RadialGradient(RadialGradientPattern::new(
center1,
style.r0 as f32,
center2,
style.r1 as f32,
stops,
)))
))
},
Surface(style) => {
let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y);
@@ -766,12 +667,12 @@ impl ToRaqotePattern for FillOrStrokeStyle {
},
SnapshotPixelFormat::BGRA,
);
Some(Pattern::Surface(SurfacePattern::new(
Pattern::Surface(SurfacePattern::new(
snapshot,
raqote::FilterMode::Nearest,
repeat,
style.transform,
)))
))
},
}
}
@@ -869,3 +770,11 @@ fn to_path(path: &canvas_traits::canvas::Path) -> raqote::Path {
}
pb.finish()
}
fn draw_options(composition_options: CompositionOptions) -> DrawOptions {
DrawOptions {
blend_mode: composition_options.composition_operation.to_raqote_style(),
alpha: composition_options.alpha as f32,
..Default::default()
}
}

View File

@@ -0,0 +1,705 @@
/* 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/. */
//! Vello implementation of 2D canvas backend.
//!
//! Vello only encodes commands for GPU, then runs rendering when
//! image is explicitly requested. This requires to copy image
//! from texture to buffer, then download buffer to CPU
//! (where we also need to un pad it).
//!
//! All Vello images are in no alpha premultiplied RGBA8 pixel format.
use std::cell::RefCell;
use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::rc::Rc;
use canvas_traits::canvas::{
CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
LineOptions, Path, ShadowOptions,
};
use compositing_traits::SerializableImageData;
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods as _};
use ipc_channel::ipc::IpcSharedMemory;
use kurbo::Shape as _;
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
use range::Range;
use vello::wgpu::{
BackendOptions, Backends, Buffer, BufferDescriptor, BufferUsages, COPY_BYTES_PER_ROW_ALIGNMENT,
CommandEncoderDescriptor, Device, Extent3d, Instance, InstanceDescriptor, InstanceFlags,
MapMode, Origin3d, Queue, TexelCopyBufferInfo, TexelCopyBufferLayout, TexelCopyTextureInfoBase,
Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
TextureViewDescriptor,
};
use vello::{kurbo, peniko};
use webrender_api::{ImageDescriptor, ImageDescriptorFlags};
use crate::backend::{Convert as _, GenericDrawTarget};
use crate::canvas_data::{Filter, TextRun};
thread_local! {
/// The shared font cache used by all canvases that render on a thread. It would be nicer
/// to have a global cache, but it looks like font-kit uses a per-thread FreeType, so
/// in order to ensure that fonts are particular to a thread we have to make our own
/// cache thread local as well.
static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, peniko::Font>> = RefCell::default();
}
pub(crate) struct VelloDrawTarget {
device: Device,
queue: Queue,
renderer: Rc<RefCell<vello::Renderer>>,
scene: vello::Scene,
size: Size2D<u32>,
clips: Vec<Path>,
state: State,
render_texture: Texture,
render_texture_view: TextureView,
render_image: peniko::Image,
padded_byte_width: u32,
rendered_buffer: Buffer,
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
enum State {
/// Scene is drawing. It will be consumed when rendered.
Drawing,
/// Scene is already rendered
/// Before next draw we need to put current rendering
/// in the background by calling [`VelloDrawTarget::ensure_drawing`].
RenderedToTexture,
RenderedToBuffer,
}
impl VelloDrawTarget {
fn new_with_renderer(
device: Device,
queue: Queue,
renderer: Rc<RefCell<vello::Renderer>>,
size: Size2D<u32>,
) -> Self {
let render_texture = device.create_texture(&TextureDescriptor {
label: None,
size: extend3d(size),
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::COPY_SRC | TextureUsages::STORAGE_BINDING,
view_formats: &[],
});
let render_texture_view = render_texture.create_view(&TextureViewDescriptor::default());
let render_image = peniko::Image {
data: vec![].into(),
format: peniko::ImageFormat::Rgba8,
width: size.width,
height: size.height,
x_extend: peniko::Extend::Pad,
y_extend: peniko::Extend::Pad,
quality: peniko::ImageQuality::Low,
alpha: 1.0,
};
renderer.borrow_mut().override_image(
&render_image,
Some(TexelCopyTextureInfoBase {
texture: render_texture.clone(),
mip_level: 0,
origin: Origin3d::ZERO,
aspect: vello::wgpu::TextureAspect::All,
}),
);
let padded_byte_width = (size.width * 4).next_multiple_of(COPY_BYTES_PER_ROW_ALIGNMENT);
let buffer_size = padded_byte_width as u64 * size.height as u64;
let rendered_buffer = device.create_buffer(&BufferDescriptor {
label: Some("val"),
size: buffer_size,
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Self {
device,
queue,
renderer,
scene: vello::Scene::new(),
size,
clips: Vec::new(),
state: State::RenderedToBuffer,
render_texture,
render_texture_view,
render_image,
padded_byte_width,
rendered_buffer,
}
}
fn with_composition<F: FnOnce(&mut Self)>(
&mut self,
composition_operation: CompositionOrBlending,
f: F,
) {
// Fast-path for default and most common composition operation
if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver)
{
f(self);
return;
}
self.scene.push_layer(
composition_operation.convert(),
1.0,
kurbo::Affine::IDENTITY,
&kurbo::Rect::ZERO.with_size(self.size.cast()),
);
f(self);
self.scene.pop_layer();
}
fn ignore_clips(&mut self, f: impl FnOnce(&mut Self)) {
// pop all clip layers
for _ in &self.clips {
self.scene.pop_layer();
}
f(self);
// push all clip layers back
for path in &self.clips {
self.scene
.push_layer(peniko::Mix::Clip, 1.0, kurbo::Affine::IDENTITY, &path.0);
}
}
fn is_viewport_cleared(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) -> bool {
let transformed_rect = transform.outer_transformed_rect(&rect.cast());
if transformed_rect.is_empty() {
return false;
}
let viewport: Rect<f64> = Rect::from_size(self.get_size().cast());
let Some(clip) = self.clips.iter().try_fold(viewport, |acc, e| {
acc.intersection(&e.0.bounding_box().into())
}) else {
// clip makes no visible side effects
return false;
};
transformed_rect.cast().contains_rect(&viewport) && // whole viewport is cleared
clip.contains_rect(&viewport) // viewport is not clipped
}
fn ensure_drawing(&mut self) {
match self.state {
State::Drawing => {},
State::RenderedToBuffer | State::RenderedToTexture => {
self.ignore_clips(|self_| {
self_
.scene
.draw_image(&self_.render_image, kurbo::Affine::IDENTITY);
});
self.state = State::Drawing;
},
}
}
}
impl GenericDrawTarget for VelloDrawTarget {
type SourceSurface = Vec<u8>; // TODO: this should be texture
fn new(size: Size2D<u32>) -> Self {
// TODO: we should read prefs instead of env
// we forbid GL because it clashes with servo's GL usage
let backends = Backends::from_env().unwrap_or_default() - Backends::GL;
let flags = InstanceFlags::from_build_config().with_env();
let backend_options = BackendOptions::from_env_or_default();
let instance = Instance::new(&InstanceDescriptor {
backends,
flags,
backend_options,
});
let mut context = vello::util::RenderContext {
instance,
devices: Vec::new(),
};
let device_id = pollster::block_on(context.device(None)).unwrap();
let device_handle = &mut context.devices[device_id];
let device = device_handle.device.clone();
let queue = device_handle.queue.clone();
let renderer = vello::Renderer::new(
&device,
vello::RendererOptions {
use_cpu: false,
num_init_threads: NonZeroUsize::new(1),
antialiasing_support: vello::AaSupport::area_only(),
pipeline_cache: None,
},
)
.unwrap();
device.on_uncaptured_error(Box::new(|error| {
log::error!("VELLO WGPU ERROR: {error}");
}));
Self::new_with_renderer(device, queue, Rc::new(RefCell::new(renderer)), size)
}
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) {
// vello scene only ever grows,
// so we use every opportunity to shrink it
if self.is_viewport_cleared(rect, transform) {
self.scene.reset();
self.clips.clear(); // no clips are affecting rendering
self.state = State::Drawing;
return;
}
self.ensure_drawing();
let rect: kurbo::Rect = rect.cast().into();
let transform = transform.into();
self.scene
.push_layer(peniko::Compose::Clear, 0.0, transform, &rect);
self.scene.fill(
peniko::Fill::NonZero,
transform,
peniko::BrushRef::Solid(peniko::color::AlphaColor::TRANSPARENT),
None,
&rect,
);
self.scene.pop_layer();
}
fn copy_surface(&mut self, surface: Vec<u8>, source: Rect<i32>, destination: Point2D<i32>) {
self.ensure_drawing();
let destination: kurbo::Point = destination.cast::<f64>().into();
let rect = kurbo::Rect::from_origin_size(destination, source.size.cast());
self.ignore_clips(|self_| {
self_
.scene
.push_layer(peniko::Compose::Copy, 1.0, kurbo::Affine::IDENTITY, &rect);
self_.scene.fill(
peniko::Fill::NonZero,
kurbo::Affine::IDENTITY,
&peniko::Image {
data: peniko::Blob::from(surface),
format: peniko::ImageFormat::Rgba8,
width: source.size.width as u32,
height: source.size.height as u32,
x_extend: peniko::Extend::Pad,
y_extend: peniko::Extend::Pad,
quality: peniko::ImageQuality::Low,
alpha: 1.0,
},
Some(kurbo::Affine::translate(destination.to_vec2())),
&rect,
);
self_.scene.pop_layer();
});
}
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
Self::new_with_renderer(
self.device.clone(),
self.queue.clone(),
self.renderer.clone(),
size.cast(),
)
}
fn draw_surface(
&mut self,
surface: Vec<u8>,
dest: Rect<f64>,
source: Rect<f64>,
filter: Filter,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
let shape: kurbo::Rect = dest.into();
self.with_composition(composition_options.composition_operation, move |self_| {
self_.scene.fill(
peniko::Fill::NonZero,
transform.cast().into(),
&peniko::Image {
data: peniko::Blob::from(surface),
format: peniko::ImageFormat::Rgba8,
width: source.size.width as u32,
height: source.size.height as u32,
x_extend: peniko::Extend::Pad,
y_extend: peniko::Extend::Pad,
// we should only do bicubic when scaling up
quality: if scale_up {
filter.convert()
} else {
peniko::ImageQuality::Low
},
alpha: composition_options.alpha as f32,
},
Some(
kurbo::Affine::translate((dest.origin.x, dest.origin.y)).pre_scale_non_uniform(
dest.size.width / source.size.width,
dest.size.height / source.size.height,
),
),
&shape,
)
})
}
fn draw_surface_with_shadow(
&self,
_surface: Vec<u8>,
_dest: &Point2D<f32>,
_shadow_options: ShadowOptions,
_composition_options: CompositionOptions,
) {
log::warn!("no support for drawing shadows");
/*
We will need to do some changes to support drawing shadows with vello, as current abstraction is made for azure.
In vello we do not need new draw target (we will use layers) and we need to pass whole rect.
offsets will be applied to rect directly. shadow blur will be passed directly to let backend do transforms.
*/
// self_.scene.draw_blurred_rounded_rect(self_.transform, rect, color, 0.0, sigma);
}
fn fill(
&mut self,
path: &Path,
fill_rule: FillRule,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.with_composition(composition_options.composition_operation, |self_| {
self_.scene.fill(
fill_rule.convert(),
transform.cast().into(),
&convert_to_brush(style, composition_options),
None,
&path.0,
);
})
}
fn fill_text(
&mut self,
text_runs: Vec<TextRun>,
start: Point2D<f32>,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
let pattern = convert_to_brush(style, composition_options);
let transform = transform.cast().into();
self.with_composition(composition_options.composition_operation, |self_| {
let mut advance = 0.;
for run in text_runs.iter() {
let glyphs = &run.glyphs;
let template = &run.font.template;
SHARED_FONT_CACHE.with(|font_cache| {
let identifier = template.identifier();
if !font_cache.borrow().contains_key(&identifier) {
let Ok(font) = run.font.font_data_and_index() else {
return;
};
let font = font.clone().convert();
font_cache.borrow_mut().insert(identifier.clone(), font);
}
let font_cache = font_cache.borrow();
let Some(font) = font_cache.get(&identifier) else {
return;
};
self_
.scene
.draw_glyphs(font)
.transform(transform)
.brush(&pattern)
.font_size(run.font.descriptor.pt_size.to_f32_px())
.draw(
peniko::Fill::NonZero,
glyphs
.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), glyphs.len()))
.map(|glyph| {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let x = advance + start.x + glyph_offset.x.to_f32_px();
let y = start.y + glyph_offset.y.to_f32_px();
advance += glyph.advance().to_f32_px();
vello::Glyph {
id: glyph.id(),
x,
y,
}
}),
);
});
}
})
}
fn fill_rect(
&mut self,
rect: &Rect<f32>,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
let pattern = convert_to_brush(style, composition_options);
let transform = transform.cast().into();
let rect: kurbo::Rect = rect.cast().into();
self.with_composition(composition_options.composition_operation, |self_| {
self_
.scene
.fill(peniko::Fill::NonZero, transform, &pattern, None, &rect);
})
}
fn get_size(&self) -> Size2D<i32> {
self.size.cast()
}
fn pop_clip(&mut self) {
if self.clips.pop().is_some() {
self.scene.pop_layer();
}
}
fn push_clip(&mut self, path: &Path, _fill_rule: FillRule, transform: Transform2D<f64>) {
self.scene
.push_layer(peniko::Mix::Clip, 1.0, transform.cast().into(), &path.0);
let mut path = path.clone();
path.transform(transform.cast());
self.clips.push(path);
}
fn push_clip_rect(&mut self, rect: &Rect<i32>) {
let mut path = Path::new();
let rect = rect.cast();
path.rect(
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
);
self.push_clip(&path, FillRule::Nonzero, Transform2D::identity());
}
fn stroke(
&mut self,
path: &Path,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.with_composition(composition_options.composition_operation, |self_| {
self_.scene.stroke(
&line_options.convert(),
transform.cast().into(),
&convert_to_brush(style, composition_options),
None,
&path.0,
);
})
}
fn stroke_rect(
&mut self,
rect: &Rect<f32>,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
let rect: kurbo::Rect = rect.cast().into();
self.with_composition(composition_options.composition_operation, |self_| {
self_.scene.stroke(
&line_options.convert(),
transform.cast().into(),
&convert_to_brush(style, composition_options),
None,
&rect,
);
})
}
fn image_descriptor_and_serializable_data(
&mut self,
) -> (ImageDescriptor, SerializableImageData) {
let size = self.size;
let stride = self.padded_byte_width;
self.map_read(|data| {
let image_desc = ImageDescriptor {
format: webrender_api::ImageFormat::RGBA8,
size: size.cast().cast_unit(),
stride: data.map(|_| stride as i32),
offset: 0,
flags: ImageDescriptorFlags::empty(),
};
let data = SerializableImageData::Raw(if let Some(data) = data {
let mut data = IpcSharedMemory::from_bytes(data);
#[allow(unsafe_code)]
unsafe {
pixels::generic_transform_inplace::<1, false, false>(data.deref_mut());
};
data
} else {
IpcSharedMemory::from_byte(0, size.area() as usize * 4)
});
(image_desc, data)
})
}
fn snapshot(&mut self) -> pixels::Snapshot {
let size = self.size;
let padded_byte_width = self.padded_byte_width;
self.map_read(|data| {
let data = data
.map(|data| {
let mut result_unpadded = Vec::<u8>::with_capacity(size.area() as usize * 4);
for row in 0..size.height {
let start = (row * padded_byte_width).try_into().unwrap();
result_unpadded.extend(&data[start..start + (size.width * 4) as usize]);
}
result_unpadded
})
.unwrap_or_else(|| vec![0; size.area() as usize * 4]);
Snapshot::from_vec(
size,
SnapshotPixelFormat::RGBA,
SnapshotAlphaMode::Transparent {
premultiplied: false,
},
data,
)
})
}
fn surface(&mut self) -> Vec<u8> {
self.snapshot().to_vec(None, None).0
}
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Vec<u8>> {
let (data, _, _) = data.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: false,
}),
Some(SnapshotPixelFormat::RGBA),
);
Some(data)
}
}
impl Drop for VelloDrawTarget {
fn drop(&mut self) {
self.renderer
.borrow_mut()
.override_image(&self.render_image, None);
}
}
fn convert_to_brush(
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
) -> peniko::Brush {
let brush: peniko::Brush = style.convert();
brush.multiply_alpha(composition_options.alpha as f32)
}
impl VelloDrawTarget {
fn render_to_texture(&mut self) {
if matches!(
self.state,
State::RenderedToTexture | State::RenderedToBuffer
) {
return;
}
self.renderer
.borrow_mut()
.render_to_texture(
&self.device,
&self.queue,
&self.scene,
&self.render_texture_view,
&vello::RenderParams {
base_color: peniko::color::AlphaColor::TRANSPARENT,
width: self.size.width,
height: self.size.height,
antialiasing_method: vello::AaConfig::Area,
},
)
.unwrap();
self.state = State::RenderedToTexture;
// prune scene
self.scene.reset();
// push all clip layers back
for path in &self.clips {
self.scene
.push_layer(peniko::Mix::Clip, 1.0, kurbo::Affine::IDENTITY, &path.0);
}
}
fn render_to_buffer(&mut self) {
if matches!(self.state, State::RenderedToBuffer) {
return;
}
self.render_to_texture();
let size = extend3d(self.size);
// TODO(perf): do a render pass that will multiply with alpha on GPU
let mut encoder = self
.device
.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Copy out buffer"),
});
encoder.copy_texture_to_buffer(
self.render_texture.as_image_copy(),
TexelCopyBufferInfo {
buffer: &self.rendered_buffer,
layout: TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(self.padded_byte_width),
rows_per_image: None,
},
},
size,
);
self.queue.submit([encoder.finish()]);
self.state = State::RenderedToBuffer;
}
fn map_read<R>(&mut self, f: impl FnOnce(Option<&[u8]>) -> R) -> R {
self.render_to_buffer();
let result = {
let buf_slice = self.rendered_buffer.slice(..);
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
buf_slice.map_async(MapMode::Read, move |v| sender.send(v).unwrap());
if let Err(error) =
vello::util::block_on_wgpu(&self.device, receiver.receive()).unwrap()
{
log::warn!("VELLO WGPU MAP ASYNC ERROR {error}");
return f(None);
}
let data = buf_slice.get_mapped_range();
f(Some(&data))
};
self.rendered_buffer.unmap();
result
}
}
fn extend3d(size: Size2D<u32>) -> Extent3d {
Extent3d {
width: size.width,
height: size.height,
depth_or_array_layers: 1,
}
}

View File

@@ -0,0 +1,548 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
use canvas_traits::canvas::{
CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
LineOptions, Path, ShadowOptions,
};
use compositing_traits::SerializableImageData;
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods as _};
use ipc_channel::ipc::IpcSharedMemory;
use kurbo::Shape;
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
use range::Range;
use vello_cpu::{kurbo, peniko};
use webrender_api::{ImageDescriptor, ImageDescriptorFlags};
use crate::backend::{Convert, GenericDrawTarget};
use crate::canvas_data::{Filter, TextRun};
thread_local! {
/// The shared font cache used by all canvases that render on a thread. It would be nicer
/// to have a global cache, but it looks like font-kit uses a per-thread FreeType, so
/// in order to ensure that fonts are particular to a thread we have to make our own
/// cache thread local as well.
static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, peniko::Font>> = RefCell::default();
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
enum State {
/// Scene is drawing. It will be consumed when rendered.
Drawing,
/// Scene is already rendered
/// Before next draw we need to put current rendering
/// in the background by calling [`VelloCPUDrawTarget::ensure_drawing`].
Rendered,
}
pub(crate) struct VelloCPUDrawTarget {
/// Because this is stateful context
/// caller cannot assume anything about transform, paint, stroke,
/// so it should provide it's own used by each command
/// but it can assume paint_transform to be identity
/// and fill rule to be `peniko::Fill::NonZero`
///
/// This is because paint_transform is rarely set,
/// so it's cheaper to always reset it after use.
ctx: vello_cpu::RenderContext,
pixmap: vello_cpu::Pixmap,
clips: Vec<Path>,
state: State,
}
impl VelloCPUDrawTarget {
fn with_composition(
&mut self,
composition_operation: CompositionOrBlending,
f: impl FnOnce(&mut Self),
) {
// Fast-path for default and most common composition operation
if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver)
{
f(self);
return;
}
self.ctx.push_blend_layer(composition_operation.convert());
f(self);
self.ctx.pop_layer();
}
fn ignore_clips(&mut self, f: impl FnOnce(&mut Self)) {
// pop all clip layers
for _ in &self.clips {
self.ctx.pop_layer();
}
f(self);
// push all clip layers back
for path in &self.clips {
self.ctx.push_clip_layer(&path.0);
}
}
fn ensure_drawing(&mut self) {
match self.state {
State::Drawing => {},
State::Rendered => {
self.ignore_clips(|self_| {
self_.ctx.set_transform(kurbo::Affine::IDENTITY);
self_.ctx.set_paint(vello_cpu::Image {
source: vello_cpu::ImageSource::Pixmap(Arc::new(self_.pixmap.clone())),
x_extend: peniko::Extend::Pad,
y_extend: peniko::Extend::Pad,
quality: peniko::ImageQuality::Low,
});
self_.ctx.fill_rect(&kurbo::Rect::from_origin_size(
(0., 0.),
self_.size().cast(),
));
});
self.state = State::Drawing;
},
}
}
fn pixmap(&mut self) -> &[u8] {
if self.state == State::Drawing {
self.ignore_clips(|self_| {
self_.ctx.flush();
self_
.ctx
.render_to_pixmap(&mut self_.pixmap, vello_cpu::RenderMode::OptimizeSpeed);
self_.ctx.reset();
self_.state = State::Rendered;
});
}
self.pixmap.data_as_u8_slice()
}
fn size(&self) -> Size2D<u32> {
Size2D::new(self.ctx.width(), self.ctx.height()).cast()
}
fn is_viewport_cleared(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) -> bool {
let transformed_rect = transform.outer_transformed_rect(&rect.cast());
if transformed_rect.is_empty() {
return false;
}
let viewport: Rect<f64> = Rect::from_size(self.get_size().cast());
let Some(clip) = self.clips.iter().try_fold(viewport, |acc, e| {
acc.intersection(&e.0.bounding_box().into())
}) else {
// clip makes no visible side effects
return false;
};
transformed_rect.cast().contains_rect(&viewport) && // whole viewport is cleared
clip.contains_rect(&viewport) // viewport is not clipped
}
}
impl GenericDrawTarget for VelloCPUDrawTarget {
type SourceSurface = Arc<vello_cpu::Pixmap>;
fn new(size: Size2D<u32>) -> Self {
let size = size.cast();
Self {
ctx: vello_cpu::RenderContext::new(size.width, size.height),
pixmap: vello_cpu::Pixmap::new(size.width, size.height),
clips: Vec::new(),
state: State::Rendered,
}
}
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) {
// vello_cpu RenderingContext only ever grows,
// so we need to use every opportunity to shrink it
if self.is_viewport_cleared(rect, transform) {
self.ctx.reset();
self.clips.clear(); // no clips are affecting rendering
self.state = State::Drawing;
return;
}
self.ensure_drawing();
let rect: kurbo::Rect = rect.cast().into();
let mut clip_path = rect.to_path(0.1);
clip_path.apply_affine(transform.cast().into());
let blend_mode = peniko::Compose::Clear;
self.ctx.push_layer(
Some(&clip_path.to_path(0.1)),
Some(blend_mode.into()),
None,
None,
);
self.ctx.pop_layer();
}
fn copy_surface(
&mut self,
surface: Self::SourceSurface,
source: Rect<i32>,
destination: Point2D<i32>,
) {
self.ensure_drawing();
let destination: kurbo::Point = destination.cast::<f64>().into();
let rect = kurbo::Rect::from_origin_size(destination, source.size.cast());
self.ctx.set_transform(kurbo::Affine::IDENTITY);
self.ignore_clips(|self_| {
// Clipped blending does not work correctly:
// https://github.com/linebender/vello/issues/1119
// self_.push_layer(Some(rect.to_path(0.1)), Some(peniko::Compose::Copy.into()), None, None);
self_.ctx.set_paint(vello_cpu::Image {
source: vello_cpu::ImageSource::Pixmap(surface),
x_extend: peniko::Extend::Pad,
y_extend: peniko::Extend::Pad,
quality: peniko::ImageQuality::Low,
});
self_.ctx.fill_rect(&rect);
// self_.ctx.pop_layer();
});
}
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
Self::new(size.cast())
}
fn draw_surface(
&mut self,
mut surface: Self::SourceSurface,
dest: Rect<f64>,
source: Rect<f64>,
filter: Filter,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
if composition_options.alpha != 1.0 {
Arc::get_mut(&mut surface)
.expect("surface should be owned")
.multiply_alpha((composition_options.alpha * 255.0) as u8);
}
self.with_composition(composition_options.composition_operation, move |self_| {
self_.ctx.set_transform(transform.cast().into());
self_.ctx.set_paint(vello_cpu::Image {
source: vello_cpu::ImageSource::Pixmap(surface),
x_extend: peniko::Extend::Pad,
y_extend: peniko::Extend::Pad,
// we should only do bicubic when scaling up
quality: if scale_up {
filter.convert()
} else {
peniko::ImageQuality::Low
},
});
self_.ctx.set_paint_transform(
kurbo::Affine::translate((dest.origin.x, dest.origin.y)).pre_scale_non_uniform(
dest.size.width / source.size.width,
dest.size.height / source.size.height,
),
);
self_.ctx.fill_rect(&dest.into());
self_.ctx.reset_paint_transform();
})
}
fn draw_surface_with_shadow(
&self,
_surface: Self::SourceSurface,
_dest: &Point2D<f32>,
_shadow_options: ShadowOptions,
_composition_options: CompositionOptions,
) {
log::warn!("no support for drawing shadows");
/*
We will need to do some changes to support drawing shadows with vello, as current abstraction is made for azure.
In vello we do not need new draw target (we will use layers) and we need to pass whole rect.
offsets will be applied to rect directly. shadow blur will be passed directly to let backend do transforms.
*/
// self_.scene.draw_blurred_rounded_rect(self_.transform, rect, color, 0.0, sigma);
}
fn fill(
&mut self,
path: &Path,
fill_rule: FillRule,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.with_composition(composition_options.composition_operation, |self_| {
self_.ctx.set_transform(transform.cast().into());
self_.ctx.set_fill_rule(fill_rule.convert());
self_.ctx.set_paint(paint(style, composition_options.alpha));
self_.ctx.fill_path(&path.0);
});
self.ctx.set_fill_rule(peniko::Fill::NonZero);
}
fn fill_text(
&mut self,
text_runs: Vec<TextRun>,
start: Point2D<f32>,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.ctx.set_paint(paint(style, composition_options.alpha));
self.ctx.set_transform(transform.cast().into());
self.with_composition(composition_options.composition_operation, |self_| {
let mut advance = 0.;
for run in text_runs.iter() {
let glyphs = &run.glyphs;
let template = &run.font.template;
SHARED_FONT_CACHE.with(|font_cache| {
let identifier = template.identifier();
if !font_cache.borrow().contains_key(&identifier) {
let Ok(font) = run.font.font_data_and_index() else {
return;
};
let font = font.clone().convert();
font_cache.borrow_mut().insert(identifier.clone(), font);
}
let font_cache = font_cache.borrow();
let Some(font) = font_cache.get(&identifier) else {
return;
};
self_
.ctx
.glyph_run(font)
.font_size(run.font.descriptor.pt_size.to_f32_px())
.fill_glyphs(
glyphs
.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), glyphs.len()))
.map(|glyph| {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let x = advance + start.x + glyph_offset.x.to_f32_px();
let y = start.y + glyph_offset.y.to_f32_px();
advance += glyph.advance().to_f32_px();
vello_cpu::Glyph {
id: glyph.id(),
x,
y,
}
}),
);
});
}
})
}
fn fill_rect(
&mut self,
rect: &Rect<f32>,
style: FillOrStrokeStyle,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.with_composition(composition_options.composition_operation, |self_| {
self_.ctx.set_transform(transform.cast().into());
self_.ctx.set_paint(paint(style, composition_options.alpha));
self_.ctx.fill_rect(&rect.cast().into());
})
}
fn get_size(&self) -> Size2D<i32> {
self.size().cast()
}
fn pop_clip(&mut self) {
if self.clips.pop().is_some() {
self.ctx.pop_layer();
}
}
fn push_clip(&mut self, path: &Path, fill_rule: FillRule, transform: Transform2D<f64>) {
self.ctx.set_transform(transform.cast().into());
let mut path = path.clone();
path.transform(transform.cast());
self.ctx.set_fill_rule(fill_rule.convert());
self.ctx.push_clip_layer(&path.0);
self.clips.push(path);
self.ctx.set_fill_rule(peniko::Fill::NonZero);
}
fn push_clip_rect(&mut self, rect: &Rect<i32>) {
let mut path = Path::new();
let rect = rect.cast();
path.rect(
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
);
self.push_clip(&path, FillRule::Nonzero, Transform2D::identity());
}
fn stroke(
&mut self,
path: &Path,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.with_composition(composition_options.composition_operation, |self_| {
self_.ctx.set_transform(transform.cast().into());
self_.ctx.set_paint(paint(style, composition_options.alpha));
self_.ctx.set_stroke(line_options.convert());
self_.ctx.stroke_path(&path.0);
})
}
fn stroke_rect(
&mut self,
rect: &Rect<f32>,
style: FillOrStrokeStyle,
line_options: LineOptions,
composition_options: CompositionOptions,
transform: Transform2D<f64>,
) {
self.ensure_drawing();
self.with_composition(composition_options.composition_operation, |self_| {
self_.ctx.set_transform(transform.cast().into());
self_.ctx.set_paint(paint(style, composition_options.alpha));
self_.ctx.set_stroke(line_options.convert());
self_.ctx.stroke_rect(&rect.cast().into());
})
}
fn image_descriptor_and_serializable_data(
&mut self,
) -> (ImageDescriptor, SerializableImageData) {
let image_desc = ImageDescriptor {
format: webrender_api::ImageFormat::RGBA8,
size: self.size().cast().cast_unit(),
stride: None,
offset: 0,
flags: ImageDescriptorFlags::empty(),
};
let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes(self.pixmap()));
(image_desc, data)
}
fn snapshot(&mut self) -> pixels::Snapshot {
Snapshot::from_vec(
self.size().cast(),
SnapshotPixelFormat::RGBA,
SnapshotAlphaMode::Transparent {
premultiplied: true,
},
self.pixmap().to_vec(),
)
}
fn surface(&mut self) -> Self::SourceSurface {
self.pixmap(); // sync pixmap
Arc::new(vello_cpu::Pixmap::from_parts(
self.pixmap.clone().take(),
self.pixmap.width(),
self.pixmap.height(),
))
}
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface> {
Some(snapshot_as_pixmap(data))
}
}
fn snapshot_as_pixmap(data: Snapshot) -> Arc<vello_cpu::Pixmap> {
let size = data.size().cast();
let (data, _, _) = data.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: true,
}),
Some(SnapshotPixelFormat::RGBA),
);
Arc::new(vello_cpu::Pixmap::from_parts(
bytemuck::cast_vec(data),
size.width,
size.height,
))
}
impl Convert<vello_cpu::PaintType> for FillOrStrokeStyle {
fn convert(self) -> vello_cpu::PaintType {
use canvas_traits::canvas::FillOrStrokeStyle::*;
match self {
Color(absolute_color) => vello_cpu::PaintType::Solid(absolute_color.convert()),
LinearGradient(style) => {
let start = kurbo::Point::new(style.x0, style.y0);
let end = kurbo::Point::new(style.x1, style.y1);
let mut gradient = peniko::Gradient::new_linear(start, end);
gradient.stops = style.stops.convert();
vello_cpu::PaintType::Gradient(gradient)
},
RadialGradient(style) => {
let center1 = kurbo::Point::new(style.x0, style.y0);
let center2 = kurbo::Point::new(style.x1, style.y1);
let mut gradient = peniko::Gradient::new_two_point_radial(
center1,
style.r0 as f32,
center2,
style.r1 as f32,
);
gradient.stops = style.stops.convert();
vello_cpu::PaintType::Gradient(gradient)
},
Surface(surface_style) => {
let pixmap = snapshot_as_pixmap(surface_style.surface_data.to_owned());
vello_cpu::PaintType::Image(vello_cpu::Image {
source: vello_cpu::ImageSource::Pixmap(pixmap),
x_extend: if surface_style.repeat_x {
peniko::Extend::Repeat
} else {
peniko::Extend::Pad
},
y_extend: if surface_style.repeat_y {
peniko::Extend::Repeat
} else {
peniko::Extend::Pad
},
quality: peniko::ImageQuality::Low,
})
},
}
}
}
fn paint(style: FillOrStrokeStyle, alpha: f64) -> vello_cpu::PaintType {
assert!((0.0..=1.0).contains(&alpha));
let paint = style.convert();
if alpha == 1.0 {
paint
} else {
match paint {
vello_cpu::PaintType::Solid(alpha_color) => {
vello_cpu::PaintType::Solid(alpha_color.multiply_alpha(alpha as f32))
},
vello_cpu::PaintType::Gradient(gradient) => {
vello_cpu::PaintType::Gradient(gradient.multiply_alpha(alpha as f32))
},
vello_cpu::PaintType::Image(mut image) => {
match &mut image.source {
vello_cpu::ImageSource::Pixmap(pixmap) => Arc::get_mut(pixmap)
.expect("pixmap should not be shared with anyone at this point")
.multiply_alpha((alpha * 255.0) as u8),
vello_cpu::ImageSource::OpaqueId(_) => unimplemented!(),
};
vello_cpu::PaintType::Image(image)
},
}
}
}

View File

@@ -16,6 +16,10 @@ default = []
tracing = ["dep:tracing"]
webxr = ["dep:webxr"]
[lints.clippy]
unwrap_used = "deny"
panic = "deny"
[dependencies]
base = { workspace = true }
bincode = { workspace = true }
@@ -31,12 +35,12 @@ gleam = { workspace = true }
ipc-channel = { workspace = true }
libc = { workspace = true }
log = { workspace = true }
net = { path = "../net" }
pixels = { path = "../pixels" }
profile_traits = { workspace = true }
servo_allocator = { path = "../allocator" }
servo_config = { path = "../config" }
servo_geometry = { path = "../geometry" }
servo-tracing = { workspace = true }
stylo_traits = { workspace = true }
timers = { path = "../timers" }
tracing = { workspace = true, optional = true }
@@ -44,7 +48,6 @@ webrender = { workspace = true }
webrender_api = { workspace = true }
webxr = { path = "../webxr", optional = true }
wr_malloc_size_of = { workspace = true }
servo-tracing = { workspace = true }
[dev-dependencies]
surfman = { workspace = true }

View File

@@ -11,13 +11,11 @@ use std::rc::Rc;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use base::Epoch;
use base::cross_process_instant::CrossProcessInstant;
use base::id::{PipelineId, WebViewId};
use base::{Epoch, WebRenderEpochToU16};
use bitflags::bitflags;
use compositing_traits::display_list::{
CompositorDisplayListInfo, HitTestInfo, ScrollTree, ScrollType,
};
use compositing_traits::display_list::{CompositorDisplayListInfo, ScrollTree, ScrollType};
use compositing_traits::rendering_context::RenderingContext;
use compositing_traits::{
CompositionPipeline, CompositorMsg, ImageUpdate, PipelineExitSource, SendableFrameTree,
@@ -26,13 +24,9 @@ use compositing_traits::{
use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
use crossbeam_channel::{Receiver, Sender};
use dpi::PhysicalSize;
use embedder_traits::{
CompositorHitTestResult, Cursor, InputEvent, ShutdownState, UntrustedNodeAddress,
ViewportDetails,
};
use embedder_traits::{CompositorHitTestResult, InputEvent, ShutdownState, ViewportDetails};
use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
use ipc_channel::ipc::{self, IpcSharedMemory};
use libc::c_void;
use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
use log::{debug, info, trace, warn};
use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
use profile_traits::mem::{ProcessReports, ProfilerRegistration, Report, ReportKind};
@@ -48,10 +42,10 @@ use webrender_api::units::{
};
use webrender_api::{
self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey, HitTestFlags,
PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, RenderReasons,
SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey,
TransformStyle,
ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey,
FontVariation, HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding,
ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo,
SpatialId, SpatialTreeItemKey, TransformStyle,
};
use crate::InitialCompositorState;
@@ -60,12 +54,12 @@ use crate::webview_manager::WebViewManager;
use crate::webview_renderer::{PinchZoomResult, UnknownWebView, WebViewRenderer};
#[derive(Debug, PartialEq)]
enum UnableToComposite {
pub enum UnableToComposite {
NotReadyToPaintImage(NotReadyToPaint),
}
#[derive(Debug, PartialEq)]
enum NotReadyToPaint {
pub enum NotReadyToPaint {
JustNotifiedConstellation,
WaitingOnConstellation,
}
@@ -91,11 +85,6 @@ pub struct ServoRenderer {
/// The [`RefreshDriver`] which manages the rythym of painting.
refresh_driver: RefreshDriver,
/// This is a temporary map between [`PipelineId`]s and their associated [`WebViewId`]. Once
/// all renderer operations become per-`WebView` this map can be removed, but we still sometimes
/// need to work backwards to figure out what `WebView` is associated with a `Pipeline`.
pub(crate) pipeline_to_webview_map: HashMap<PipelineId, WebViewId>,
/// Tracks whether we are in the process of shutting down, or have shut down and should close
/// the compositor. This is shared with the `Servo` instance.
shutdown_state: Rc<Cell<ShutdownState>>,
@@ -125,11 +114,9 @@ pub struct ServoRenderer {
/// True to translate mouse input into touch events.
pub(crate) convert_mouse_to_touch: bool,
/// Current mouse cursor.
cursor: Cursor,
/// Current cursor position.
cursor_pos: DevicePoint,
/// The last position in the rendered view that the mouse moved over. This becomes `None`
/// when the mouse leaves the rendered view.
pub(crate) last_mouse_move_position: Option<DevicePoint>,
}
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
@@ -150,7 +137,7 @@ pub struct IOCompositor {
/// The webrender renderer.
webrender: Option<webrender::Renderer>,
/// The surfman instance that webrender targets
/// The [`RenderingContext`] instance that webrender targets, which is the viewport.
rendering_context: Rc<dyn RenderingContext>,
/// The number of frames pending to receive from WebRender.
@@ -200,10 +187,6 @@ pub(crate) struct PipelineDetails {
/// The id of the parent pipeline, if any.
pub parent_pipeline_id: Option<PipelineId>,
/// The epoch of the most recent display list for this pipeline. Note that this display
/// list might not be displayed, as WebRender processes display lists asynchronously.
pub most_recent_display_list_epoch: Option<WebRenderEpoch>,
/// Whether animations are running
pub animations_running: bool,
@@ -213,10 +196,6 @@ pub(crate) struct PipelineDetails {
/// Whether to use less resources by stopping animations.
pub throttled: bool,
/// Hit test items for this pipeline. This is used to map WebRender hit test
/// information to the full information necessary for Servo.
pub hit_test_items: Vec<HitTestInfo>,
/// The compositor-side [ScrollTree]. This is used to allow finding and scrolling
/// nodes in the compositor before forwarding new offsets to WebRender.
pub scroll_tree: ScrollTree,
@@ -227,6 +206,11 @@ pub(crate) struct PipelineDetails {
/// The paint metric status of the first contentful paint.
pub first_contentful_paint_metric: PaintMetricState,
/// The CSS pixel to device pixel scale of the viewport of this pipeline, including
/// page zoom, but not including any pinch zoom amount. This is used to detect
/// situations where the current display list is for an old scale.
pub viewport_scale: Option<Scale<f32, CSSPixel, DevicePixel>>,
/// Which parts of Servo have reported that this `Pipeline` has exited. Only when all
/// have done so will it be discarded.
pub exited: PipelineExitSource,
@@ -247,11 +231,10 @@ impl PipelineDetails {
PipelineDetails {
pipeline: None,
parent_pipeline_id: None,
most_recent_display_list_epoch: None,
viewport_scale: None,
animations_running: false,
animation_callbacks_running: false,
throttled: false,
hit_test_items: Vec::new(),
scroll_tree: ScrollTree::default(),
first_paint_metric: PaintMetricState::Waiting,
first_contentful_paint_metric: PaintMetricState::Waiting,
@@ -266,146 +249,49 @@ impl PipelineDetails {
}
}
pub enum HitTestError {
EpochMismatch,
Others,
}
impl ServoRenderer {
pub fn shutdown_state(&self) -> ShutdownState {
self.shutdown_state.get()
}
pub(crate) fn hit_test_at_point<'a>(
&self,
point: DevicePoint,
details_for_pipeline: impl Fn(PipelineId) -> Option<&'a PipelineDetails>,
) -> Result<CompositorHitTestResult, HitTestError> {
match self.hit_test_at_point_with_flags_and_pipeline(
point,
HitTestFlags::empty(),
None,
details_for_pipeline,
) {
Ok(hit_test_results) => hit_test_results
.first()
.cloned()
.ok_or(HitTestError::Others),
Err(error) => Err(error),
}
pub(crate) fn hit_test_at_point(&self, point: DevicePoint) -> Vec<CompositorHitTestResult> {
self.hit_test_at_point_with_flags(point, HitTestFlags::empty())
}
// TODO: split this into first half (global) and second half (one for whole compositor, one for webview)
pub(crate) fn hit_test_at_point_with_flags_and_pipeline<'a>(
pub(crate) fn hit_test_at_point_with_flags(
&self,
point: DevicePoint,
flags: HitTestFlags,
pipeline_id: Option<WebRenderPipelineId>,
details_for_pipeline: impl Fn(PipelineId) -> Option<&'a PipelineDetails>,
) -> Result<Vec<CompositorHitTestResult>, HitTestError> {
) -> Vec<CompositorHitTestResult> {
// DevicePoint and WorldPoint are the same for us.
let world_point = WorldPoint::from_untyped(point.to_untyped());
let results =
self.webrender_api
.hit_test(self.webrender_document, pipeline_id, world_point, flags);
let results = self.webrender_api.hit_test(
self.webrender_document,
None, /* pipeline_id */
world_point,
flags,
);
let mut epoch_mismatch = false;
let results = results
results
.items
.iter()
.filter_map(|item| {
.map(|item| {
let pipeline_id = item.pipeline.into();
let details = details_for_pipeline(pipeline_id)?;
// If the epoch in the tag does not match the current epoch of the pipeline,
// then the hit test is against an old version of the display list.
match details.most_recent_display_list_epoch {
Some(epoch) => {
if epoch.as_u16() != item.tag.1 {
// It's too early to hit test for now.
// New scene building is in progress.
epoch_mismatch = true;
return None;
}
},
_ => return None,
}
let offset = details
.scroll_tree
.scroll_offset(pipeline_id.root_scroll_id())
.unwrap_or_default();
let point_in_initial_containing_block =
(item.point_in_viewport + offset).to_untyped();
let info = &details.hit_test_items[item.tag.0 as usize];
Some(CompositorHitTestResult {
let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
CompositorHitTestResult {
pipeline_id,
point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
point_relative_to_initial_containing_block: Point2D::from_untyped(
point_in_initial_containing_block,
),
point_relative_to_item: Point2D::from_untyped(
item.point_relative_to_item.to_untyped(),
),
node: UntrustedNodeAddress(info.node as *const c_void),
cursor: info.cursor,
scroll_tree_node: info.scroll_tree_node,
})
external_scroll_id,
}
})
.collect();
if epoch_mismatch {
return Err(HitTestError::EpochMismatch);
}
Ok(results)
.collect()
}
pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
self.webrender_api
.send_transaction(self.webrender_document, transaction);
}
pub(crate) fn update_cursor_from_hittest(
&mut self,
pos: DevicePoint,
result: &CompositorHitTestResult,
) {
if let Some(webview_id) = self
.pipeline_to_webview_map
.get(&result.pipeline_id)
.copied()
{
self.update_cursor(pos, webview_id, result.cursor);
} else {
warn!("Couldn't update cursor for non-WebView-associated pipeline");
};
}
pub(crate) fn update_cursor(
&mut self,
pos: DevicePoint,
webview_id: WebViewId,
cursor: Option<Cursor>,
) {
self.cursor_pos = pos;
let cursor = match cursor {
Some(cursor) if cursor != self.cursor => cursor,
_ => return,
};
self.cursor = cursor;
if let Err(e) = self
.constellation_sender
.send(EmbedderToConstellationMessage::SetCursor(
webview_id, cursor,
))
{
warn!("Sending event to constellation failed ({:?}).", e);
}
}
}
impl IOCompositor {
@@ -422,7 +308,6 @@ impl IOCompositor {
state.event_loop_waker,
),
shutdown_state: state.shutdown_state,
pipeline_to_webview_map: Default::default(),
compositor_receiver: state.receiver,
constellation_sender: state.constellation_chan,
time_profiler_chan: state.time_profiler_chan,
@@ -432,8 +317,7 @@ impl IOCompositor {
#[cfg(feature = "webxr")]
webxr_main_thread: state.webxr_main_thread,
convert_mouse_to_touch,
cursor: Cursor::None,
cursor_pos: DevicePoint::new(0.0, 0.0),
last_mouse_move_position: None,
})),
webview_renderers: WebViewManager::default(),
needs_repaint: Cell::default(),
@@ -591,18 +475,6 @@ impl IOCompositor {
};
webview_renderer.on_touch_event_processed(result);
},
CompositorMsg::CreatePng(webview_id, page_rect, reply) => {
let res = self.render_to_shared_memory(webview_id, page_rect);
if let Err(ref e) = res {
info!("Error retrieving PNG: {:?}", e);
}
let img = res.unwrap_or(None);
if let Err(e) = reply.send(img) {
warn!("Sending reply to create png failed ({:?}).", e);
}
},
CompositorMsg::IsReadyToSaveImageReply(is_ready) => {
assert_eq!(
self.ready_to_save_state,
@@ -640,25 +512,7 @@ impl IOCompositor {
},
CompositorMsg::NewWebRenderFrameReady(_document_id, recomposite_needed) => {
self.pending_frames -= 1;
let point: DevicePoint = self.global.borrow().cursor_pos;
if recomposite_needed {
let details_for_pipeline = |pipeline_id| self.details_for_pipeline(pipeline_id);
let result = self
.global
.borrow()
.hit_test_at_point(point, details_for_pipeline);
if let Ok(result) = result {
self.global
.borrow_mut()
.update_cursor_from_hittest(point, &result);
}
}
if recomposite_needed || self.animation_callbacks_running() {
self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
}
self.handle_new_webrender_frame_ready(recomposite_needed);
},
CompositorMsg::LoadComplete(_) => {
@@ -773,14 +627,14 @@ impl IOCompositor {
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
return warn!("Could not find WebView for incoming display list");
};
// WebRender is not ready until we receive "NewWebRenderFrameReady"
webview_renderer.webrender_frame_ready.set(false);
let old_scale = webview_renderer.device_pixels_per_page_pixel();
let pipeline_id = display_list_info.pipeline_id;
let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
details.most_recent_display_list_epoch = Some(display_list_info.epoch);
details.hit_test_items = display_list_info.hit_test_info;
details.install_new_scroll_tree(display_list_info.scroll_tree);
details.viewport_scale =
Some(display_list_info.viewport_details.hidpi_scale_factor);
let epoch = display_list_info.epoch;
let first_reflow = display_list_info.first_reflow;
@@ -795,6 +649,14 @@ impl IOCompositor {
}
let mut transaction = Transaction::new();
let is_root_pipeline =
Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel()
{
self.send_root_pipeline_display_list_in_transaction(&mut transaction);
}
transaction
.set_display_list(display_list_info.epoch, (pipeline_id, built_display_list));
self.update_transaction_with_all_scroll_offsets(&mut transaction);
@@ -802,33 +664,6 @@ impl IOCompositor {
self.global.borrow_mut().send_transaction(transaction);
},
CompositorMsg::HitTest(pipeline, point, flags, sender) => {
// When a display list is sent to WebRender, it starts scene building in a
// separate thread and then that display list is available for hit testing.
// Without flushing scene building, any hit test we do might be done against
// a previous scene, if the last one we sent hasn't finished building.
//
// TODO(mrobinson): Flushing all scene building is a big hammer here, because
// we might only be interested in a single pipeline. The only other option
// would be to listen to the TransactionNotifier for previous per-pipeline
// transactions, but that isn't easily compatible with the event loop wakeup
// mechanism from libserver.
self.global.borrow().webrender_api.flush_scene_builder();
let details_for_pipeline = |pipeline_id| self.details_for_pipeline(pipeline_id);
let result = self
.global
.borrow()
.hit_test_at_point_with_flags_and_pipeline(
point,
flags,
pipeline,
details_for_pipeline,
)
.unwrap_or_default();
let _ = sender.send(result);
},
CompositorMsg::GenerateImageKey(sender) => {
let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
},
@@ -872,8 +707,14 @@ impl IOCompositor {
self.global.borrow_mut().send_transaction(transaction);
},
CompositorMsg::AddFontInstance(font_instance_key, font_key, size, flags) => {
self.add_font_instance(font_instance_key, font_key, size, flags);
CompositorMsg::AddFontInstance(
font_instance_key,
font_key,
size,
flags,
variations,
) => {
self.add_font_instance(font_instance_key, font_key, size, flags, variations);
},
CompositorMsg::RemoveFonts(keys, instance_keys) => {
@@ -894,18 +735,11 @@ impl IOCompositor {
number_of_font_instance_keys,
result_sender,
) => {
let font_keys = (0..number_of_font_keys)
.map(|_| self.global.borrow().webrender_api.generate_font_key())
.collect();
let font_instance_keys = (0..number_of_font_instance_keys)
.map(|_| {
self.global
.borrow()
.webrender_api
.generate_font_instance_key()
})
.collect();
let _ = result_sender.send((font_keys, font_instance_keys));
self.handle_generate_font_keys(
number_of_font_keys,
number_of_font_instance_keys,
result_sender,
);
},
CompositorMsg::Viewport(webview_id, viewport_description) => {
if let Some(webview) = self.webview_renderers.get_mut(webview_id) {
@@ -943,18 +777,11 @@ impl IOCompositor {
number_of_font_instance_keys,
result_sender,
) => {
let font_keys = (0..number_of_font_keys)
.map(|_| self.global.borrow().webrender_api.generate_font_key())
.collect();
let font_instance_keys = (0..number_of_font_instance_keys)
.map(|_| {
self.global
.borrow()
.webrender_api
.generate_font_instance_key()
})
.collect();
let _ = result_sender.send((font_keys, font_instance_keys));
self.handle_generate_font_keys(
number_of_font_keys,
number_of_font_instance_keys,
result_sender,
);
},
CompositorMsg::NewWebRenderFrameReady(..) => {
// Subtract from the number of pending frames, but do not do any compositing.
@@ -966,6 +793,27 @@ impl IOCompositor {
}
}
/// Generate the font keys and send them to the `result_sender`.
fn handle_generate_font_keys(
&self,
number_of_font_keys: usize,
number_of_font_instance_keys: usize,
result_sender: IpcSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
) {
let font_keys = (0..number_of_font_keys)
.map(|_| self.global.borrow().webrender_api.generate_font_key())
.collect();
let font_instance_keys = (0..number_of_font_instance_keys)
.map(|_| {
self.global
.borrow()
.webrender_api
.generate_font_instance_key()
})
.collect();
let _ = result_sender.send((font_keys, font_instance_keys));
}
/// Queue a new frame in the transaction and increase the pending frames count.
pub(crate) fn generate_frame(&mut self, transaction: &mut Transaction, reason: RenderReasons) {
self.pending_frames += 1;
@@ -1239,9 +1087,8 @@ impl IOCompositor {
}
if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
webview_renderer.set_page_zoom(1.0);
webview_renderer.set_page_zoom(Scale::new(1.0));
}
self.send_root_pipeline_display_list();
}
pub fn on_zoom_window_event(&mut self, webview_id: WebViewId, magnification: f32) {
@@ -1250,22 +1097,9 @@ impl IOCompositor {
}
if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
webview_renderer.set_page_zoom(magnification);
let current_page_zoom = webview_renderer.page_zoom();
webview_renderer.set_page_zoom(current_page_zoom * Scale::new(magnification));
}
self.send_root_pipeline_display_list();
}
fn details_for_pipeline(&self, pipeline_id: PipelineId) -> Option<&PipelineDetails> {
let webview_id = self
.global
.borrow()
.pipeline_to_webview_map
.get(&pipeline_id)
.cloned()?;
self.webview_renderers
.get(webview_id)?
.pipelines
.get(&pipeline_id)
}
/// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
@@ -1351,7 +1185,7 @@ impl IOCompositor {
/// Render the WebRender scene to the shared memory, without updating other state of this
/// [`IOCompositor`]. If succesful return the output image in shared memory.
fn render_to_shared_memory(
pub fn render_to_shared_memory(
&mut self,
webview_id: WebViewId,
page_rect: Option<Rect<f32, CSSPixel>>,
@@ -1543,7 +1377,7 @@ impl IOCompositor {
}
/// Get the message receiver for this [`IOCompositor`].
pub fn receiver(&self) -> Ref<Receiver<CompositorMsg>> {
pub fn receiver(&self) -> Ref<'_, Receiver<CompositorMsg>> {
Ref::map(self.global.borrow(), |global| &global.compositor_receiver)
}
@@ -1561,15 +1395,6 @@ impl IOCompositor {
},
CompositorMsg::NewWebRenderFrameReady(..) => {
found_recomposite_msg = true;
// Process all pending events
// FIXME: Shouldn't `webview_frame_ready` be stored globally and why can't `pending_frames`
// be used here?
self.webview_renderers.iter().for_each(|webview| {
webview.dispatch_pending_point_input_events();
webview.webrender_frame_ready.set(true);
});
true
},
_ => true,
@@ -1688,7 +1513,14 @@ impl IOCompositor {
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) {
let variations = if pref!(layout_variable_fonts_enabled) {
variations
} else {
vec![]
};
let mut transaction = Transaction::new();
let font_instance_options = FontInstanceOptions {
@@ -1701,7 +1533,7 @@ impl IOCompositor {
size,
Some(font_instance_options),
None,
Vec::new(),
variations,
);
self.global.borrow_mut().send_transaction(transaction);
@@ -1749,4 +1581,39 @@ impl IOCompositor {
fn shutdown_state(&self) -> ShutdownState {
self.global.borrow().shutdown_state()
}
fn refresh_cursor(&self) {
let global = self.global.borrow();
let Some(last_mouse_move_position) = global.last_mouse_move_position else {
return;
};
let Some(hit_test_result) = global
.hit_test_at_point(last_mouse_move_position)
.first()
.cloned()
else {
return;
};
if let Err(error) =
global
.constellation_sender
.send(EmbedderToConstellationMessage::RefreshCursor(
hit_test_result.pipeline_id,
))
{
warn!("Sending event to constellation failed ({:?}).", error);
}
}
fn handle_new_webrender_frame_ready(&mut self, recomposite_needed: bool) {
self.pending_frames -= 1;
if recomposite_needed {
self.refresh_cursor();
}
if recomposite_needed || self.animation_callbacks_running() {
self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
}
}
}

View File

@@ -34,7 +34,6 @@ mod from_constellation {
Self::CreateOrUpdateWebView(..) => target!("CreateOrUpdateWebView"),
Self::RemoveWebView(..) => target!("RemoveWebView"),
Self::TouchEventProcessed(..) => target!("TouchEventProcessed"),
Self::CreatePng(..) => target!("CreatePng"),
Self::IsReadyToSaveImageReply(..) => target!("IsReadyToSaveImageReply"),
Self::SetThrottled(..) => target!("SetThrottled"),
Self::NewWebRenderFrameReady(..) => target!("NewWebRenderFrameReady"),
@@ -43,7 +42,6 @@ mod from_constellation {
Self::SendInitialTransaction(..) => target!("SendInitialTransaction"),
Self::SendScrollNode(..) => target!("SendScrollNode"),
Self::SendDisplayList { .. } => target!("SendDisplayList"),
Self::HitTest(..) => target!("HitTest"),
Self::GenerateImageKey(..) => target!("GenerateImageKey"),
Self::UpdateImages(..) => target!("UpdateImages"),
Self::GenerateFontKeys(..) => target!("GenerateFontKeys"),

View File

@@ -127,7 +127,7 @@ mod test {
webviews: &WebViewManager<WebView>,
) -> Vec<(WebViewId, WebView)> {
let mut keys = webviews.webviews.keys().collect::<Vec<_>>();
keys.sort();
keys.sort_unstable();
keys.iter()
.map(|&id| (*id, webviews.webviews.get(id).cloned().unwrap()))
.collect()

View File

@@ -2,15 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::{Cell, RefCell};
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::{Entry, Keys};
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use base::id::{PipelineId, WebViewId};
use compositing_traits::display_list::ScrollType;
use compositing_traits::viewport_description::{
DEFAULT_ZOOM, MAX_ZOOM, MIN_ZOOM, ViewportDescription,
DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
};
use compositing_traits::{PipelineExitSource, SendableFrameTree, WebViewTrait};
use constellation_traits::{EmbedderToConstellationMessage, WindowSizeType};
@@ -27,7 +27,7 @@ use style_traits::{CSSPixel, PinchZoomFactor};
use webrender_api::units::{DeviceIntPoint, DevicePixel, DevicePoint, DeviceRect, LayoutVector2D};
use webrender_api::{ExternalScrollId, HitTestFlags, ScrollLocation};
use crate::compositor::{HitTestError, PipelineDetails, ServoRenderer};
use crate::compositor::{PipelineDetails, ServoRenderer};
use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState};
#[derive(Clone, Copy)]
@@ -76,6 +76,7 @@ pub(crate) struct WebViewRenderer {
pub webview: Box<dyn WebViewTrait>,
/// The root [`PipelineId`] of the currently displayed page in this WebView.
pub root_pipeline_id: Option<PipelineId>,
/// The rectangle of the [`WebView`] in device pixels, which is the viewport.
pub rect: DeviceRect,
/// Tracks details about each active pipeline that the compositor knows about.
pub pipelines: HashMap<PipelineId, PipelineDetails>,
@@ -88,30 +89,18 @@ pub(crate) struct WebViewRenderer {
/// "Desktop-style" zoom that resizes the viewport to fit the window.
pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
/// "Mobile-style" zoom that does not reflow the page.
viewport_zoom: PinchZoomFactor,
pinch_zoom: PinchZoomFactor,
/// The HiDPI scale factor for the `WebView` associated with this renderer. This is controlled
/// by the embedding layer.
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
/// Whether or not this [`WebViewRenderer`] isn't throttled and has a pipeline with
/// active animations or animation frame callbacks.
animating: bool,
/// Pending input events queue. Priavte and only this thread pushes events to it.
pending_point_input_events: RefCell<VecDeque<InputEvent>>,
/// WebRender is not ready between `SendDisplayList` and `WebRenderFrameReady` messages.
pub webrender_frame_ready: Cell<bool>,
/// Viewport Description
/// A [`ViewportDescription`] for this [`WebViewRenderer`], which contains the limitations
/// and initial values for zoom derived from the `viewport` meta tag in web content.
viewport_description: Option<ViewportDescription>,
}
impl Drop for WebViewRenderer {
fn drop(&mut self) {
self.global
.borrow_mut()
.pipeline_to_webview_map
.retain(|_, webview_id| self.id != *webview_id);
}
}
impl WebViewRenderer {
pub(crate) fn new(
global: Rc<RefCell<ServoRenderer>>,
@@ -129,12 +118,10 @@ impl WebViewRenderer {
touch_handler: TouchHandler::new(),
global,
pending_scroll_zoom_events: Default::default(),
page_zoom: Scale::new(1.0),
viewport_zoom: PinchZoomFactor::new(DEFAULT_ZOOM),
page_zoom: DEFAULT_PAGE_ZOOM,
pinch_zoom: PinchZoomFactor::new(1.0),
hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
animating: false,
pending_point_input_events: Default::default(),
webrender_frame_ready: Cell::default(),
viewport_description: None,
}
}
@@ -158,13 +145,9 @@ impl WebViewRenderer {
&mut self,
pipeline_id: PipelineId,
) -> &mut PipelineDetails {
self.pipelines.entry(pipeline_id).or_insert_with(|| {
self.global
.borrow_mut()
.pipeline_to_webview_map
.insert(pipeline_id, self.id);
PipelineDetails::new()
})
self.pipelines
.entry(pipeline_id)
.or_insert_with(PipelineDetails::new)
}
pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
@@ -183,15 +166,11 @@ impl WebViewRenderer {
}
pipeline.remove_entry();
self.global
.borrow_mut()
.pipeline_to_webview_map
.remove(&pipeline_id);
}
pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
let pipeline_id = frame_tree.pipeline.id;
let old_pipeline_id = std::mem::replace(&mut self.root_pipeline_id, Some(pipeline_id));
let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
if old_pipeline_id != self.root_pipeline_id {
debug!(
@@ -263,11 +242,7 @@ impl WebViewRenderer {
self.pipelines
.iter_mut()
.filter(|(id, _)| !attached_pipelines.contains(id))
.for_each(|(_, details)| {
details.scroll_tree.nodes.iter_mut().for_each(|node| {
node.set_offset(LayoutVector2D::zero());
})
})
.for_each(|(_, details)| details.scroll_tree.reset_all_scroll_offsets());
}
/// Sets or unsets the animations-running flag for the given pipeline. Returns
@@ -337,78 +312,41 @@ impl WebViewRenderer {
}
}
pub(crate) fn dispatch_point_input_event(&self, event: InputEvent) -> bool {
self.dispatch_point_input_event_internal(event, true)
}
pub(crate) fn dispatch_pending_point_input_events(&self) {
while let Some(event) = self.pending_point_input_events.borrow_mut().pop_front() {
// TODO: Add multiple retry later if needed.
self.dispatch_point_input_event_internal(event, false);
}
}
pub(crate) fn dispatch_point_input_event_internal(
&self,
mut event: InputEvent,
retry_on_error: bool,
) -> bool {
// Events that do not need to do hit testing are sent directly to the
// constellation to filter down.
let Some(point) = event.point() else {
return false;
};
// Delay the event if the epoch is not synchronized yet (new frame is not ready),
// or hit test result would fail and the event is rejected anyway.
if retry_on_error &&
(!self.webrender_frame_ready.get() ||
!self.pending_point_input_events.borrow().is_empty())
{
self.pending_point_input_events
.borrow_mut()
.push_back(event);
return false;
}
// If we can't find a pipeline to send this event to, we cannot continue.
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
let result = match self
.global
.borrow()
.hit_test_at_point(point, get_pipeline_details)
{
Ok(hit_test_results) => Some(hit_test_results),
Err(HitTestError::EpochMismatch) if retry_on_error => {
self.pending_point_input_events
.borrow_mut()
.push_back(event.clone());
return false;
pub(crate) fn dispatch_input_event_with_hit_testing(&self, mut event: InputEvent) -> bool {
let event_point = event.point();
let hit_test_result = match event_point {
Some(point) => {
let hit_test_result = self
.global
.borrow()
.hit_test_at_point(point)
.into_iter()
.nth(0);
if hit_test_result.is_none() {
warn!("Empty hit test result for input event, ignoring.");
return false;
}
hit_test_result
},
_ => None,
None => None,
};
match event {
InputEvent::Touch(ref mut touch_event) => {
touch_event.init_sequence_id(self.touch_handler.current_sequence_id);
},
InputEvent::MouseButton(_) |
InputEvent::MouseLeave(_) |
InputEvent::MouseMove(_) |
InputEvent::Wheel(_) => {
if let Some(ref result) = result {
self.global
.borrow_mut()
.update_cursor_from_hittest(point, result);
} else {
warn!("Not hit test result.");
}
InputEvent::MouseMove(_) => {
self.global.borrow_mut().last_mouse_move_position = event_point;
},
InputEvent::MouseLeftViewport(_) => {
self.global.borrow_mut().last_mouse_move_position = None;
},
InputEvent::MouseButton(_) | InputEvent::Wheel(_) => {},
_ => unreachable!("Unexpected input event type: {event:?}"),
}
if let Err(error) = self.global.borrow().constellation_sender.send(
EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, result),
EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
) {
warn!("Sending event to constellation failed ({error:?}).");
false
@@ -483,11 +421,11 @@ impl WebViewRenderer {
}
}
self.dispatch_point_input_event(event);
self.dispatch_input_event_with_hit_testing(event);
}
fn send_touch_event(&mut self, event: TouchEvent) -> bool {
self.dispatch_point_input_event(InputEvent::Touch(event))
self.dispatch_input_event_with_hit_testing(InputEvent::Touch(event))
}
pub(crate) fn on_touch_event(&mut self, event: TouchEvent) {
@@ -751,13 +689,15 @@ impl WebViewRenderer {
/// <http://w3c.github.io/touch-events/#mouse-events>
fn simulate_mouse_click(&mut self, point: DevicePoint) {
let button = MouseButton::Left;
self.dispatch_point_input_event(InputEvent::MouseMove(MouseMoveEvent::new(point)));
self.dispatch_point_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
self.dispatch_input_event_with_hit_testing(InputEvent::MouseMove(MouseMoveEvent::new(
point,
)));
self.dispatch_input_event_with_hit_testing(InputEvent::MouseButton(MouseButtonEvent::new(
MouseButtonAction::Down,
button,
point,
)));
self.dispatch_point_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
self.dispatch_input_event_with_hit_testing(InputEvent::MouseButton(MouseButtonEvent::new(
MouseButtonAction::Up,
button,
point,
@@ -896,17 +836,10 @@ impl WebViewRenderer {
ScrollLocation::Start | ScrollLocation::End => scroll_location,
};
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
let hit_test_results = self
.global
.borrow()
.hit_test_at_point_with_flags_and_pipeline(
cursor,
HitTestFlags::FIND_ALL,
None,
get_pipeline_details,
)
.unwrap_or_default();
.hit_test_at_point_with_flags(cursor, HitTestFlags::FIND_ALL);
// Iterate through all hit test results, processing only the first node of each pipeline.
// This is needed to propagate the scroll events from a pipeline representing an iframe to
@@ -918,7 +851,7 @@ impl WebViewRenderer {
Some(&hit_test_result.pipeline_id)
{
let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
&hit_test_result.scroll_tree_node,
&hit_test_result.external_scroll_id,
scroll_location,
ScrollType::InputEvents,
);
@@ -951,7 +884,7 @@ impl WebViewRenderer {
}
pub(crate) fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
Scale::new(self.viewport_zoom.get())
Scale::new(self.pinch_zoom.get())
}
fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool {
@@ -959,19 +892,43 @@ impl WebViewRenderer {
zoom = viewport.clamp_zoom(zoom);
}
let old_zoom = std::mem::replace(&mut self.viewport_zoom, PinchZoomFactor::new(zoom));
old_zoom != self.viewport_zoom
let old_zoom = std::mem::replace(&mut self.pinch_zoom, PinchZoomFactor::new(zoom));
old_zoom != self.pinch_zoom
}
pub(crate) fn set_page_zoom(&mut self, magnification: f32) {
self.page_zoom =
Scale::new((self.page_zoom.get() * magnification).clamp(MIN_ZOOM, MAX_ZOOM));
pub(crate) fn page_zoom(&mut self) -> Scale<f32, CSSPixel, DeviceIndependentPixel> {
self.page_zoom
}
pub(crate) fn set_page_zoom(
&mut self,
new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
) {
let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
if old_zoom != self.page_zoom {
self.send_window_size_message();
}
}
/// The scale to use when displaying this [`WebViewRenderer`] in WebRender
/// including both viewport scale (page zoom and hidpi scale) as well as any
/// pinch zoom applied. This is based on the latest display list received,
/// as page zoom changes are applied asynchronously and the rendered view
/// should reflect the latest display list.
pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
self.page_zoom * self.hidpi_scale_factor * self.pinch_zoom_level()
let viewport_scale = self
.root_pipeline_id
.and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
.and_then(|pipeline| pipeline.viewport_scale)
.unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
viewport_scale * self.pinch_zoom_level()
}
/// The current viewport scale (hidpi scale and page zoom and not pinch
/// zoom) based on the current setting of the WebView. Note that this may
/// not be the rendered viewport zoom as that is based on the latest display
/// list and zoom changes are applied asynchronously.
pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
&self,
) -> Scale<f32, CSSPixel, DevicePixel> {

View File

@@ -34,6 +34,12 @@ fn servo_preferences_derive(input: synstructure::Structure) -> TokenStream {
set_match_cases.extend(quote!(stringify!(#name) => self.#name = value.try_into().unwrap(),))
}
let mut comparisons = quote!();
for field in named_fields.named.iter() {
let name = field.ident.as_ref().unwrap();
comparisons.extend(quote!(if self.#name != other.#name { changes.push((stringify!(#name), self.#name.clone().into(),)) }))
}
let structure_name = &ast.ident;
quote! {
impl #structure_name {
@@ -50,6 +56,12 @@ fn servo_preferences_derive(input: synstructure::Structure) -> TokenStream {
_ => { panic!("Unknown preference: {:?}", name); }
}
}
pub fn diff(&self, other: &Self) -> Vec<(&'static str, PrefValue)> {
let mut changes = vec![];
#comparisons
changes
}
}
}
}

View File

@@ -7,7 +7,7 @@
use std::default::Default;
use std::path::PathBuf;
use std::sync::{LazyLock, RwLock, RwLockReadGuard};
use std::sync::OnceLock;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
@@ -47,6 +47,10 @@ pub struct Opts {
/// Whether we're running in multiprocess mode.
pub multiprocess: bool,
/// Whether to force using ipc_channel instead of crossbeam_channel in singleprocess mode. Does
/// nothing in multiprocess mode.
pub force_ipc: bool,
/// Whether we want background hang monitor enabled or not
pub background_hang_monitor: bool,
@@ -189,6 +193,7 @@ impl Default for Opts {
user_stylesheets: Vec::new(),
hard_fail: true,
multiprocess: false,
force_ipc: false,
background_hang_monitor: false,
random_pipeline_closure_probability: None,
random_pipeline_closure_seed: None,
@@ -209,13 +214,27 @@ impl Default for Opts {
// Make Opts available globally. This saves having to clone and pass
// opts everywhere it is used, which gets particularly cumbersome
// when passing through the DOM structures.
static OPTIONS: LazyLock<RwLock<Opts>> = LazyLock::new(|| RwLock::new(Opts::default()));
static OPTIONS: OnceLock<Opts> = OnceLock::new();
pub fn set_options(opts: Opts) {
*OPTIONS.write().unwrap() = opts;
/// Initialize options.
///
/// Should only be called once at process startup.
/// Must be called before the first call to [get].
pub fn initialize_options(opts: Opts) {
OPTIONS.set(opts).expect("Already initialized");
}
/// Get the servo options
///
/// If the servo options have not been initialized by calling [initialize_options], then the
/// options will be initialized to default values. Outside of tests the options should
/// be explicitly initialized.
#[inline]
pub fn get() -> RwLockReadGuard<'static, Opts> {
OPTIONS.read().unwrap()
pub fn get() -> &'static Opts {
// In unit-tests using default options reduces boilerplate.
// We can't use `cfg(test)` since that only is enabled when this crate
// is compiled in test mode.
// We rely on the `expect` in `initialize_options` to inform us if refactoring
// causes a `get` call to move before `initialize_options`.
OPTIONS.get_or_init(Default::default)
}

View File

@@ -11,12 +11,22 @@ pub use crate::pref_util::PrefValue;
static PREFERENCES: RwLock<Preferences> = RwLock::new(Preferences::const_default());
pub trait Observer: Send + Sync {
fn prefs_changed(&self, _changes: &[(&'static str, PrefValue)]) {}
}
static OBSERVERS: RwLock<Vec<Box<dyn Observer>>> = RwLock::new(Vec::new());
#[inline]
/// Get the current set of global preferences for Servo.
pub fn get() -> RwLockReadGuard<'static, Preferences> {
PREFERENCES.read().unwrap()
}
pub fn add_observer(observer: Box<dyn Observer>) {
OBSERVERS.write().unwrap().push(observer);
}
pub fn set(preferences: Preferences) {
// Map between Stylo preference names and Servo preference names as the This should be
// kept in sync with components/script/dom/bindings/codegen/run.py which generates the
@@ -38,8 +48,18 @@ pub fn set(preferences: Preferences) {
"layout.container-queries.enabled",
preferences.layout_container_queries_enabled,
);
stylo_config::set_bool(
"layout.variable_fonts.enabled",
preferences.layout_variable_fonts_enabled,
);
let changed = preferences.diff(&PREFERENCES.read().unwrap());
*PREFERENCES.write().unwrap() = preferences;
for observer in &*OBSERVERS.read().unwrap() {
observer.prefs_changed(&changed);
}
}
/// A convenience macro for accessing a preference value using its static path.
@@ -69,14 +89,24 @@ pub struct Preferences {
/// List of comma-separated backends to be used by wgpu.
pub dom_webgpu_wgpu_backend: String,
pub dom_abort_controller_enabled: bool,
pub dom_adoptedstylesheet_enabled: bool,
pub dom_async_clipboard_enabled: bool,
pub dom_bluetooth_enabled: bool,
pub dom_bluetooth_testing_enabled: bool,
pub dom_allow_scripts_to_close_windows: bool,
pub dom_canvas_capture_enabled: bool,
pub dom_canvas_text_enabled: bool,
/// Selects canvas backend
///
/// Available values:
/// - ` `/`auto`
/// - raqote
/// - vello
/// - vello_cpu
pub dom_canvas_backend: String,
pub dom_clipboardevent_enabled: bool,
pub dom_composition_event_enabled: bool,
pub dom_cookiestore_enabled: bool,
pub dom_crypto_subtle_enabled: bool,
pub dom_customelements_enabled: bool,
pub dom_document_dblclick_timeout: i64,
@@ -89,6 +119,7 @@ pub struct Preferences {
pub dom_microdata_testing_enabled: bool,
pub dom_mouse_event_which_enabled: bool,
pub dom_mutation_observer_enabled: bool,
pub dom_navigator_sendbeacon_enabled: bool,
pub dom_notification_enabled: bool,
pub dom_offscreen_canvas_enabled: bool,
pub dom_permissions_enabled: bool,
@@ -99,7 +130,6 @@ pub struct Preferences {
pub dom_serviceworker_timeout_seconds: i64,
pub dom_servo_helpers_enabled: bool,
pub dom_servoparser_async_html_tokenizer_enabled: bool,
pub dom_svg_enabled: bool,
pub dom_testable_crash_enabled: bool,
pub dom_testbinding_enabled: bool,
pub dom_testbinding_prefcontrolled_enabled: bool,
@@ -171,7 +201,6 @@ pub struct Preferences {
pub js_mem_gc_decommit_threshold_mb: i64,
pub js_mem_gc_dynamic_heap_growth_enabled: bool,
pub js_mem_gc_dynamic_mark_slice_enabled: bool,
pub js_mem_gc_empty_chunk_count_max: i64,
pub js_mem_gc_empty_chunk_count_min: i64,
pub js_mem_gc_high_frequency_heap_growth_max: i64,
pub js_mem_gc_high_frequency_heap_growth_min: i64,
@@ -187,7 +216,6 @@ pub struct Preferences {
pub js_mem_max: i64,
pub js_native_regex_enabled: bool,
pub js_offthread_compilation_enabled: bool,
pub js_parallel_parsing_enabled: bool,
pub js_shared_memory: bool,
pub js_throw_on_asmjs_validation_failure: bool,
pub js_throw_on_debuggee_would_run: bool,
@@ -204,6 +232,7 @@ pub struct Preferences {
pub layout_flexbox_enabled: bool,
pub layout_threads: i64,
pub layout_unimplemented: bool,
pub layout_variable_fonts_enabled: bool,
pub layout_writing_mode_enabled: bool,
/// Enable hardware acceleration for video playback.
pub media_glvideo_enabled: bool,
@@ -246,14 +275,17 @@ impl Preferences {
devtools_server_enabled: false,
devtools_server_port: 0,
dom_abort_controller_enabled: false,
dom_adoptedstylesheet_enabled: false,
dom_allow_scripts_to_close_windows: false,
dom_async_clipboard_enabled: false,
dom_bluetooth_enabled: false,
dom_bluetooth_testing_enabled: false,
dom_canvas_capture_enabled: false,
dom_canvas_text_enabled: true,
dom_canvas_backend: String::new(),
dom_clipboardevent_enabled: true,
dom_composition_event_enabled: false,
dom_cookiestore_enabled: false,
dom_crypto_subtle_enabled: true,
dom_customelements_enabled: true,
dom_document_dblclick_dist: 1,
@@ -266,6 +298,7 @@ impl Preferences {
dom_microdata_testing_enabled: false,
dom_mouse_event_which_enabled: false,
dom_mutation_observer_enabled: true,
dom_navigator_sendbeacon_enabled: false,
dom_notification_enabled: false,
dom_offscreen_canvas_enabled: false,
dom_permissions_enabled: false,
@@ -276,7 +309,6 @@ impl Preferences {
dom_serviceworker_timeout_seconds: 60,
dom_servo_helpers_enabled: false,
dom_servoparser_async_html_tokenizer_enabled: false,
dom_svg_enabled: false,
dom_testable_crash_enabled: false,
dom_testbinding_enabled: false,
dom_testbinding_prefcontrolled2_enabled: false,
@@ -346,7 +378,6 @@ impl Preferences {
js_mem_gc_decommit_threshold_mb: 32,
js_mem_gc_dynamic_heap_growth_enabled: true,
js_mem_gc_dynamic_mark_slice_enabled: true,
js_mem_gc_empty_chunk_count_max: 30,
js_mem_gc_empty_chunk_count_min: 1,
js_mem_gc_high_frequency_heap_growth_max: 300,
js_mem_gc_high_frequency_heap_growth_min: 150,
@@ -362,7 +393,6 @@ impl Preferences {
js_mem_max: -1,
js_native_regex_enabled: true,
js_offthread_compilation_enabled: true,
js_parallel_parsing_enabled: true,
js_shared_memory: true,
js_throw_on_asmjs_validation_failure: false,
js_throw_on_debuggee_would_run: false,
@@ -380,6 +410,7 @@ impl Preferences {
// TODO(mrobinson): This should likely be based on the number of processors.
layout_threads: 3,
layout_unimplemented: false,
layout_variable_fonts_enabled: false,
layout_writing_mode_enabled: false,
media_glvideo_enabled: false,
media_testing_enabled: false,
@@ -450,12 +481,12 @@ impl UserAgentPlatform {
const ARCHITECTURE: &str = "";
format!(
"Mozilla/5.0 (Windows NT 10.0; Win64; {ARCHITECTURE}rv:128.0) Servo/{SERVO_VERSION} Firefox/128.0"
"Mozilla/5.0 (Windows NT 10.0; Win64; {ARCHITECTURE}rv:140.0) Servo/{SERVO_VERSION} Firefox/140.0"
)
},
UserAgentPlatform::Desktop if cfg!(target_os = "macos") => {
format!(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:128.0) Servo/{SERVO_VERSION} Firefox/128.0"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:140.0) Servo/{SERVO_VERSION} Firefox/140.0"
)
},
UserAgentPlatform::Desktop => {
@@ -466,19 +497,19 @@ impl UserAgentPlatform {
const ARCHITECTURE: &str = "i686";
format!(
"Mozilla/5.0 (X11; Linux {ARCHITECTURE}; rv:128.0) Servo/{SERVO_VERSION} Firefox/128.0"
"Mozilla/5.0 (X11; Linux {ARCHITECTURE}; rv:140.0) Servo/{SERVO_VERSION} Firefox/140.0"
)
},
UserAgentPlatform::Android => {
format!(
"Mozilla/5.0 (Android 10; Mobile; rv:128.0) Servo/{SERVO_VERSION} Firefox/128.0"
"Mozilla/5.0 (Android 10; Mobile; rv:140.0) Servo/{SERVO_VERSION} Firefox/140.0"
)
},
UserAgentPlatform::OpenHarmony => format!(
"Mozilla/5.0 (OpenHarmony; Mobile; rv:128.0) Servo/{SERVO_VERSION} Firefox/128.0"
"Mozilla/5.0 (OpenHarmony; Mobile; rv:140.0) Servo/{SERVO_VERSION} Firefox/140.0"
),
UserAgentPlatform::Ios => format!(
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X; rv:128.0) Servo/{SERVO_VERSION} Firefox/128.0"
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X; rv:140.0) Servo/{SERVO_VERSION} Firefox/140.0"
),
}
}

View File

@@ -14,8 +14,15 @@ path = "lib.rs"
[features]
bluetooth = ["bluetooth_traits"]
default = []
tracing = ["dep:tracing"]
tracing = ["dep:tracing", "canvas/tracing"]
webgpu = ["script_traits/webgpu"]
vello = ["canvas/vello"]
vello_cpu = ["canvas/vello_cpu"]
raqote = ["canvas/raqote"]
[lints.clippy]
unwrap_used = "deny"
panic = "deny"
[dependencies]
background_hang_monitor = { path = "../background_hang_monitor" }
@@ -31,7 +38,7 @@ crossbeam-channel = { workspace = true }
devtools_traits = { workspace = true }
embedder_traits = { workspace = true }
euclid = { workspace = true }
fonts = { path = "../fonts" }
fonts = { path = "../fonts", default-features = false }
ipc-channel = { workspace = true }
keyboard-types = { workspace = true }
layout_api = { workspace = true }

View File

@@ -92,6 +92,7 @@ use std::marker::PhantomData;
use std::mem::replace;
use std::rc::{Rc, Weak};
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use std::{process, thread};
use background_hang_monitor::HangMonitorRegister;
@@ -129,11 +130,11 @@ use devtools_traits::{
use embedder_traits::resources::{self, Resource};
use embedder_traits::user_content_manager::UserContentManager;
use embedder_traits::{
AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy,
AnimationState, CompositorHitTestResult, EmbedderMsg, EmbedderProxy, FocusId,
FocusSequenceNumber, InputEvent, JSValue, JavaScriptEvaluationError, JavaScriptEvaluationId,
KeyboardEvent, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState,
MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg,
WebDriverCommandResponse, WebDriverLoadStatus,
WebDriverCommandResponse, WebDriverLoadStatus, WebDriverScriptCommand,
};
use euclid::Size2D;
use euclid::default::Size2D as UntypedSize2D;
@@ -141,14 +142,17 @@ use fonts::SystemFontServiceProxy;
use ipc_channel::Error as IpcError;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use keyboard_types::{Key, KeyState, Modifiers};
use keyboard_types::{Key, KeyState, Modifiers, NamedKey};
use layout_api::{LayoutFactory, ScriptThreadFactory};
use log::{debug, error, info, trace, warn};
use media::WindowGLContext;
use net_traits::pub_domains::reg_host;
use net_traits::request::Referrer;
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
use net_traits::{self, IpcSend, ReferrerPolicy, ResourceThreads};
use net_traits::{
self, AsyncRuntime, IpcSend, ReferrerPolicy, ResourceThreads, exit_fetch_thread,
start_fetch_thread,
};
use profile_traits::mem::ProfilerMsg;
use profile_traits::{mem, time};
use script_traits::{
@@ -156,10 +160,10 @@ use script_traits::{
ScriptThreadMessage, UpdatePipelineIdReason,
};
use serde::{Deserialize, Serialize};
use servo_config::prefs::{self, PrefValue};
use servo_config::{opts, pref};
use servo_rand::{Rng, ServoRng, SliceRandom, random};
use servo_url::{Host, ImmutableOrigin, ServoUrl};
use style_traits::CSSPixel;
#[cfg(feature = "webgpu")]
use webgpu::swapchain::WGPUImageMap;
#[cfg(feature = "webgpu")]
@@ -250,6 +254,18 @@ struct BrowsingContextGroup {
webgpus: HashMap<Host, WebGPU>,
}
struct PreferenceForwarder(Sender<EmbedderToConstellationMessage>);
impl prefs::Observer for PreferenceForwarder {
fn prefs_changed(&self, changes: &[(&'static str, PrefValue)]) {
let _ = self
.0
.send(EmbedderToConstellationMessage::PreferencesUpdated(
changes.to_owned(),
));
}
}
/// The `Constellation` itself. In the servo browser, there is one
/// constellation, which maintains all of the browser global data.
/// In embedded applications, there may be more than one constellation,
@@ -282,6 +298,9 @@ pub struct Constellation<STF, SWF> {
/// None when in multiprocess mode.
background_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
/// In single process mode, a join handle on the BHM worker thread.
background_monitor_register_join_handle: Option<JoinHandle<()>>,
/// Channels to control all background-hang monitors.
/// TODO: store them on the relevant BrowsingContextGroup,
/// so that they could be controlled on a "per-tab/event-loop" basis.
@@ -397,8 +416,11 @@ pub struct Constellation<STF, SWF> {
/// and the namespaces are allocated by the constellation.
next_pipeline_namespace_id: PipelineNamespaceId,
/// Bits of state used to interact with the webdriver implementation
webdriver: WebDriverData,
/// An [`IpcSender`] to notify navigation events to webdriver.
webdriver_load_status_sender: Option<(IpcSender<WebDriverLoadStatus>, PipelineId)>,
/// An [`IpcSender`] to forward responses from the `ScriptThread` to the WebDriver server.
webdriver_input_command_reponse_sender: Option<IpcSender<WebDriverCommandResponse>>,
/// Document states for loaded pipelines (used only when writing screenshots).
document_states: HashMap<PipelineId, DocumentState>,
@@ -452,6 +474,12 @@ pub struct Constellation<STF, SWF> {
/// The process manager.
process_manager: ProcessManager,
/// The async runtime.
async_runtime: Box<dyn AsyncRuntime>,
/// When in single-process mode, join handles for script-threads.
script_join_handles: HashMap<WebViewId, JoinHandle<()>>,
}
/// State needed to construct a constellation.
@@ -504,24 +532,9 @@ pub struct InitialConstellationState {
/// User content manager
pub user_content_manager: UserContentManager,
}
/// Data needed for webdriver
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 {
fn new() -> WebDriverData {
WebDriverData {
load_channel: None,
resize_channel: None,
input_command_response_sender: None,
}
}
/// The async runtime.
pub async_runtime: Box<dyn AsyncRuntime>,
}
/// When we are running reftests, we save an image to compare against a reference.
@@ -582,6 +595,7 @@ where
hard_fail: bool,
) -> Sender<EmbedderToConstellationMessage> {
let (compositor_sender, compositor_receiver) = unbounded();
let compositor_sender_self = compositor_sender.clone();
// service worker manager to communicate with constellation
let (swmanager_ipc_sender, swmanager_ipc_receiver) =
@@ -614,23 +628,28 @@ where
// If we are in multiprocess mode,
// a dedicated per-process hang monitor will be initialized later inside the content process.
// See run_content_process in servo/lib.rs
let (background_monitor_register, background_hang_monitor_control_ipc_senders) =
if opts::get().multiprocess {
(None, vec![])
} else {
let (
background_hang_monitor_control_ipc_sender,
background_hang_monitor_control_ipc_receiver,
) = ipc::channel().expect("ipc channel failure");
(
Some(HangMonitorRegister::init(
background_hang_monitor_ipc_sender.clone(),
background_hang_monitor_control_ipc_receiver,
opts::get().background_hang_monitor,
)),
vec![background_hang_monitor_control_ipc_sender],
)
};
let (
background_monitor_register,
background_monitor_register_join_handle,
background_hang_monitor_control_ipc_senders,
) = if opts::get().multiprocess {
(None, None, vec![])
} else {
let (
background_hang_monitor_control_ipc_sender,
background_hang_monitor_control_ipc_receiver,
) = ipc::channel().expect("ipc channel failure");
let (register, join_handle) = HangMonitorRegister::init(
background_hang_monitor_ipc_sender.clone(),
background_hang_monitor_control_ipc_receiver,
opts::get().background_hang_monitor,
);
(
Some(register),
Some(join_handle),
vec![background_hang_monitor_control_ipc_sender],
)
};
let swmanager_receiver =
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
@@ -648,6 +667,10 @@ where
let rippy_data = resources::read_bytes(Resource::RippyPNG);
if opts::get().multiprocess {
prefs::add_observer(Box::new(PreferenceForwarder(compositor_sender_self)));
}
let mut constellation: Constellation<STF, SWF> = Constellation {
namespace_receiver,
namespace_ipc_sender,
@@ -655,6 +678,7 @@ where
background_hang_monitor_sender: background_hang_monitor_ipc_sender,
background_hang_monitor_receiver,
background_monitor_register,
background_monitor_register_join_handle,
background_monitor_control_senders: background_hang_monitor_control_ipc_senders,
script_receiver,
compositor_receiver,
@@ -685,7 +709,8 @@ where
time_profiler_chan: state.time_profiler_chan,
mem_profiler_chan: state.mem_profiler_chan.clone(),
phantom: PhantomData,
webdriver: WebDriverData::new(),
webdriver_load_status_sender: None,
webdriver_input_command_reponse_sender: None,
document_states: HashMap::new(),
#[cfg(feature = "webgpu")]
webrender_wgpu,
@@ -709,6 +734,8 @@ where
rippy_data,
user_content_manager: state.user_content_manager,
process_manager: ProcessManager::new(state.mem_profiler_chan),
async_runtime: state.async_runtime,
script_join_handles: Default::default(),
};
constellation.run();
@@ -720,6 +747,11 @@ where
/// The main event loop for the constellation.
fn run(&mut self) {
// Start a fetch thread.
// In single-process mode this will be the global fetch thread;
// in multi-process mode this will be used only by the canvas paint thread.
let join_handle = start_fetch_thread();
while !self.shutting_down || !self.pipelines.is_empty() {
// Randomly close a pipeline if --random-pipeline-closure-probability is set
// This is for testing the hardening of the constellation.
@@ -727,6 +759,12 @@ where
self.handle_request();
}
self.handle_shutdown();
// Shut down the fetch thread started above.
exit_fetch_thread();
join_handle
.join()
.expect("Failed to join on the fetch thread in the constellation");
}
/// Generate a new pipeline id namespace.
@@ -970,6 +1008,10 @@ where
self.background_monitor_control_senders.push(chan);
}
if let Some(join_handle) = pipeline.join_handle {
self.script_join_handles.insert(webview_id, join_handle);
}
if let Some(host) = host {
debug!("{}: Adding new host entry {}", webview_id, host,);
self.set_event_loop(
@@ -994,7 +1036,7 @@ where
fn fully_active_descendant_browsing_contexts_iter(
&self,
browsing_context_id: BrowsingContextId,
) -> FullyActiveBrowsingContextsIterator {
) -> FullyActiveBrowsingContextsIterator<'_> {
FullyActiveBrowsingContextsIterator {
stack: vec![browsing_context_id],
pipelines: &self.pipelines,
@@ -1006,7 +1048,7 @@ where
fn fully_active_browsing_contexts_iter(
&self,
webview_id: WebViewId,
) -> FullyActiveBrowsingContextsIterator {
) -> FullyActiveBrowsingContextsIterator<'_> {
self.fully_active_descendant_browsing_contexts_iter(BrowsingContextId::from(webview_id))
}
@@ -1014,7 +1056,7 @@ where
fn all_descendant_browsing_contexts_iter(
&self,
browsing_context_id: BrowsingContextId,
) -> AllBrowsingContextsIterator {
) -> AllBrowsingContextsIterator<'_> {
AllBrowsingContextsIterator {
stack: vec![browsing_context_id],
pipelines: &self.pipelines,
@@ -1132,6 +1174,7 @@ where
/// Handles loading pages, navigation, and granting access to the compositor
#[servo_tracing::instrument(skip_all)]
fn handle_request(&mut self) {
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
enum Request {
PipelineNamespace(PipelineNamespaceRequest),
@@ -1273,6 +1316,12 @@ where
if allowed {
self.load_url(webview_id, pipeline_id, load_data, history_handling);
} else {
if let Some((sender, id)) = &self.webdriver_load_status_sender {
if pipeline_id == *id {
let _ = sender.send(WebDriverLoadStatus::NavigationStop);
}
}
let pipeline_is_top_level_pipeline = self
.browsing_contexts
.get(&BrowsingContextId::from(webview_id))
@@ -1352,7 +1401,7 @@ where
// Create a new top level browsing context. Will use response_chan to return
// the browsing context id.
EmbedderToConstellationMessage::NewWebView(url, webview_id, viewport_details) => {
self.handle_new_top_level_browsing_context(url, webview_id, viewport_details, None);
self.handle_new_top_level_browsing_context(url, webview_id, viewport_details);
},
// Close a top level browsing context.
EmbedderToConstellationMessage::CloseWebView(webview_id) => {
@@ -1366,8 +1415,8 @@ where
}
self.handle_panic(webview_id, error, None);
},
EmbedderToConstellationMessage::FocusWebView(webview_id) => {
self.handle_focus_web_view(webview_id);
EmbedderToConstellationMessage::FocusWebView(webview_id, focus_id) => {
self.handle_focus_web_view(webview_id, focus_id);
},
EmbedderToConstellationMessage::BlurWebView => {
self.webviews.unfocus();
@@ -1415,8 +1464,8 @@ where
EmbedderToConstellationMessage::ForwardInputEvent(webview_id, event, hit_test) => {
self.forward_input_event(webview_id, event, hit_test);
},
EmbedderToConstellationMessage::SetCursor(webview_id, cursor) => {
self.handle_set_cursor_msg(webview_id, cursor)
EmbedderToConstellationMessage::RefreshCursor(pipeline_id) => {
self.handle_refresh_cursor(pipeline_id)
},
EmbedderToConstellationMessage::ToggleProfiler(rate, max_duration) => {
for background_monitor_control_sender in &self.background_monitor_control_senders {
@@ -1473,7 +1522,21 @@ where
}
},
EmbedderToConstellationMessage::SetWebDriverResponseSender(sender) => {
self.webdriver.input_command_response_sender = Some(sender);
self.webdriver_input_command_reponse_sender = Some(sender);
},
EmbedderToConstellationMessage::PreferencesUpdated(updates) => {
let event_loops = self
.pipelines
.values()
.map(|pipeline| pipeline.event_loop.clone());
for event_loop in event_loops {
let _ = event_loop.send(ScriptThreadMessage::PreferencesUpdated(
updates
.iter()
.map(|(name, value)| (String::from(*name), value.clone()))
.collect(),
));
}
},
}
}
@@ -1874,14 +1937,14 @@ where
self.handle_finish_javascript_evaluation(evaluation_id, result)
},
ScriptToConstellationMessage::WebDriverInputComplete(msg_id) => {
if let Some(ref reply_sender) = self.webdriver.input_command_response_sender {
if let Some(ref reply_sender) = self.webdriver_input_command_reponse_sender {
reply_sender
.send(WebDriverCommandResponse { id: msg_id })
.unwrap_or_else(|_| {
warn!("Failed to send WebDriverInputComplete {:?}", msg_id);
});
} else {
warn!("No WebDriver input_command_response_sender");
warn!("No webdriver_input_command_reponse_sender");
}
},
}
@@ -2449,15 +2512,13 @@ where
// even when currently hanging(on JS or sync XHR).
// This must be done before starting the process of closing all pipelines.
for chan in &self.background_monitor_control_senders {
let (exit_ipc_sender, exit_ipc_receiver) =
ipc::channel().expect("Failed to create IPC channel!");
if let Err(e) = chan.send(BackgroundHangMonitorControlMsg::Exit(exit_ipc_sender)) {
// Note: the bhm worker thread will continue to run
// until all monitored components have exited,
// at which point we can join on the thread(done in `handle_shutdown`).
if let Err(e) = chan.send(BackgroundHangMonitorControlMsg::Exit) {
warn!("error communicating with bhm: {}", e);
continue;
}
if exit_ipc_receiver.recv().is_err() {
warn!("Failed to receive exit confirmation from BHM.");
}
}
// Close the top-level browsing contexts
@@ -2517,6 +2578,22 @@ where
fn handle_shutdown(&mut self) {
debug!("Handling shutdown.");
// In single process mode, join on script-threads
// from webview which haven't been manually closed before.
for (_, join_handle) in self.script_join_handles.drain() {
if join_handle.join().is_err() {
error!("Failed to join on a script-thread.");
}
}
// In single process mode, join on the background hang monitor worker thread.
drop(self.background_monitor_register.take());
if let Some(join_handle) = self.background_monitor_register_join_handle.take() {
if join_handle.join().is_err() {
error!("Failed to join on the bhm background thread.");
}
}
// At this point, there are no active pipelines,
// so we can safely block on other threads, without worrying about deadlock.
// Channels to receive signals when threads are done exiting.
@@ -2644,6 +2721,9 @@ where
debug!("Shutting-down IPC router thread in constellation.");
ROUTER.shutdown();
debug!("Shutting-down the async runtime in constellation.");
self.async_runtime.shutdown();
}
fn handle_pipeline_exited(&mut self, pipeline_id: PipelineId) {
@@ -2774,13 +2854,13 @@ where
}
#[servo_tracing::instrument(skip_all)]
fn handle_focus_web_view(&mut self, webview_id: WebViewId) {
if self.webviews.get(webview_id).is_none() {
return warn!("{webview_id}: FocusWebView on unknown top-level browsing context");
fn handle_focus_web_view(&mut self, webview_id: WebViewId, focus_id: FocusId) {
let focused = self.webviews.focus(webview_id).is_ok();
if !focused {
warn!("{webview_id}: FocusWebView on unknown top-level browsing context");
}
self.webviews.focus(webview_id);
self.embedder_proxy
.send(EmbedderMsg::WebViewFocused(webview_id));
.send(EmbedderMsg::WebViewFocused(webview_id, focus_id, focused));
}
#[servo_tracing::instrument(skip_all)]
@@ -2828,6 +2908,7 @@ where
}
}
#[allow(deprecated)]
fn update_active_keybord_modifiers(&mut self, event: &KeyboardEvent) {
self.active_keyboard_modifiers = event.event.modifiers;
@@ -2835,23 +2916,27 @@ where
// either pressed or released, but `active_keyboard_modifiers` should track the subsequent
// state. If this event will update that state, we need to ensure that we are tracking what
// the event changes.
let modified_modifier = match event.event.key {
Key::Alt => Modifiers::ALT,
Key::AltGraph => Modifiers::ALT_GRAPH,
Key::CapsLock => Modifiers::CAPS_LOCK,
Key::Control => Modifiers::CONTROL,
Key::Fn => Modifiers::FN,
Key::FnLock => Modifiers::FN_LOCK,
Key::Meta => Modifiers::META,
Key::NumLock => Modifiers::NUM_LOCK,
Key::ScrollLock => Modifiers::SCROLL_LOCK,
Key::Shift => Modifiers::SHIFT,
Key::Symbol => Modifiers::SYMBOL,
Key::SymbolLock => Modifiers::SYMBOL_LOCK,
Key::Hyper => Modifiers::HYPER,
let Key::Named(named_key) = event.event.key else {
return;
};
let modified_modifier = match named_key {
NamedKey::Alt => Modifiers::ALT,
NamedKey::AltGraph => Modifiers::ALT_GRAPH,
NamedKey::CapsLock => Modifiers::CAPS_LOCK,
NamedKey::Control => Modifiers::CONTROL,
NamedKey::Fn => Modifiers::FN,
NamedKey::FnLock => Modifiers::FN_LOCK,
NamedKey::Meta => Modifiers::META,
NamedKey::NumLock => Modifiers::NUM_LOCK,
NamedKey::ScrollLock => Modifiers::SCROLL_LOCK,
NamedKey::Shift => Modifiers::SHIFT,
NamedKey::Symbol => Modifiers::SYMBOL,
NamedKey::SymbolLock => Modifiers::SYMBOL_LOCK,
NamedKey::Hyper => Modifiers::HYPER,
// The web doesn't make a distinction between these keys (there is only
// "meta") so map "super" to "meta".
Key::Super => Modifiers::META,
NamedKey::Super => Modifiers::META,
_ => return,
};
match event.event.state {
@@ -2886,34 +2971,8 @@ where
}
}
let pipeline_id = match &hit_test_result {
Some(hit_test) => hit_test.pipeline_id,
None => {
// If there's no hit test, send to the focused browsing context of the given webview.
let Some(browsing_context_id) = self
.webviews
.get(webview_id)
.map(|webview| webview.focused_browsing_context_id)
else {
warn!("Handling InputEvent for an unknown webview: {webview_id}");
return;
};
let Some(pipeline_id) = self
.browsing_contexts
.get(&browsing_context_id)
.map(|context| context.pipeline_id)
else {
warn!("{browsing_context_id}: Got InputEvent for nonexistent browsing context");
return;
};
pipeline_id
},
};
let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
debug!("Got event for pipeline ({pipeline_id}) after closure");
let Some(webview) = self.webviews.get_mut(webview_id) else {
warn!("Got input event for unknown WebViewId: {webview_id:?}");
return;
};
@@ -2923,13 +2982,7 @@ where
active_keyboard_modifiers,
event,
};
if let Err(error) = pipeline
.event_loop
.send(ScriptThreadMessage::SendInputEvent(pipeline_id, event))
{
self.handle_send_error(pipeline_id, error);
}
webview.forward_input_event(event, &self.pipelines, &self.browsing_contexts);
}
#[servo_tracing::instrument(skip_all)]
@@ -2938,7 +2991,6 @@ where
url: ServoUrl,
webview_id: WebViewId,
viewport_details: ViewportDetails,
response_sender: Option<IpcSender<WebDriverLoadStatus>>,
) {
let pipeline_id = PipelineId::new();
let browsing_context_id = BrowsingContextId::from(webview_id);
@@ -2995,10 +3047,6 @@ where
}),
viewport_details,
});
if let Some(response_sender) = response_sender {
self.webdriver.load_channel = Some((pipeline_id, response_sender));
}
}
#[servo_tracing::instrument(skip_all)]
@@ -3036,6 +3084,11 @@ where
.remove(&browsing_context.bc_group_id);
}
// Note: In single-process mode,
// if the webview is manually closed, we drop the join handle without joining on it.
// It is unlikely the thread will still run when the constellation shuts-down.
self.script_join_handles.remove(&webview_id);
debug!("{webview_id}: Closed");
}
@@ -3153,7 +3206,8 @@ where
);
},
};
let is_parent_private = match self.browsing_contexts.get(&parent_browsing_context_id) {
match self.browsing_contexts.get(&parent_browsing_context_id) {
Some(ctx) => ctx.is_private,
None => {
return warn!(
@@ -3161,8 +3215,7 @@ where
parent_browsing_context_id, browsing_context_id,
);
},
};
is_parent_private
}
};
let is_private = is_private || is_parent_private;
@@ -3386,9 +3439,17 @@ where
}
#[servo_tracing::instrument(skip_all)]
fn handle_set_cursor_msg(&mut self, webview_id: WebViewId, cursor: Cursor) {
self.embedder_proxy
.send(EmbedderMsg::SetCursor(webview_id, cursor));
fn handle_refresh_cursor(&self, pipeline_id: PipelineId) {
let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
return;
};
if let Err(error) = pipeline
.event_loop
.send(ScriptThreadMessage::RefreshCursor(pipeline_id))
{
warn!("Could not send RefreshCursor message to pipeline: {error:?}");
}
}
#[servo_tracing::instrument(skip_all)]
@@ -3542,7 +3603,12 @@ where
};
if let Err(e) = result {
self.handle_send_error(parent_pipeline_id, e);
} else if let Some((sender, id)) = &self.webdriver_load_status_sender {
if source_id == *id {
let _ = sender.send(WebDriverLoadStatus::NavigationStop);
}
}
None
},
None => {
@@ -3621,18 +3687,6 @@ where
#[servo_tracing::instrument(skip_all)]
fn handle_load_complete_msg(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) {
let mut webdriver_reset = false;
if let Some((expected_pipeline_id, ref reply_chan)) = self.webdriver.load_channel {
if expected_pipeline_id == pipeline_id {
debug!("Sending load for {:?} to WebDriver", expected_pipeline_id);
let _ = reply_chan.send(WebDriverLoadStatus::Complete);
webdriver_reset = true;
}
}
if webdriver_reset {
self.webdriver.load_channel = None;
}
if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
debug!("{}: Marking as loaded", pipeline_id);
pipeline.completely_loaded = true;
@@ -4113,7 +4167,7 @@ where
source_browsing_context,
target_origin: origin,
source_origin,
data,
data: Box::new(data),
};
let result = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.event_loop.send(msg),
@@ -4150,9 +4204,12 @@ where
}
// Focus the top-level browsing context.
self.webviews.focus(webview_id);
self.embedder_proxy
.send(EmbedderMsg::WebViewFocused(webview_id));
let focused = self.webviews.focus(webview_id);
self.embedder_proxy.send(EmbedderMsg::WebViewFocused(
webview_id,
FocusId::new(),
focused.is_ok(),
));
// If a container with a non-null nested browsing context is focused,
// the nested browsing context's active document becomes the focused
@@ -4401,24 +4458,32 @@ where
fn handle_create_canvas_paint_thread_msg(
&mut self,
size: UntypedSize2D<u64>,
response_sender: IpcSender<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>,
response_sender: IpcSender<Option<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>>,
) {
let (canvas_data_sender, canvas_data_receiver) = unbounded();
let (canvas_sender, canvas_ipc_sender) = self
.canvas
.get_or_init(|| self.create_canvas_paint_thread());
if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create {
let response = if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create {
sender: canvas_data_sender,
size,
}) {
return warn!("Create canvas paint thread failed ({})", e);
}
let (canvas_id, image_key) = match canvas_data_receiver.recv() {
Ok(canvas_data) => canvas_data,
Err(e) => return warn!("Create canvas paint thread id response failed ({})", e),
warn!("Create canvas paint thread failed ({})", e);
None
} else {
match canvas_data_receiver.recv() {
Ok(Some((canvas_id, image_key))) => {
Some((canvas_ipc_sender.clone(), canvas_id, image_key))
},
Ok(None) => None,
Err(e) => {
warn!("Create canvas paint thread id response failed ({})", e);
None
},
}
};
if let Err(e) = response_sender.send((canvas_ipc_sender.clone(), canvas_id, image_key)) {
if let Err(e) = response_sender.send(response) {
warn!("Create canvas paint thread response failed ({})", e);
}
}
@@ -4434,11 +4499,24 @@ where
},
// TODO: This should use the ScriptThreadMessage::EvaluateJavaScript command
WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => {
let pipeline_id = self
.browsing_contexts
.get(&browsing_context_id)
.expect("ScriptCommand: Browsing context must exist at this point")
.pipeline_id;
let pipeline_id = if let Some(browsing_context) =
self.browsing_contexts.get(&browsing_context_id)
{
browsing_context.pipeline_id
} else {
return warn!("{}: Browsing context is not ready", browsing_context_id);
};
match &cmd {
WebDriverScriptCommand::AddLoadStatusSender(_, sender) => {
self.webdriver_load_status_sender = Some((sender.clone(), pipeline_id));
},
WebDriverScriptCommand::RemoveLoadStatusSender(_) => {
self.webdriver_load_status_sender = None;
},
_ => {},
};
let control_msg = ScriptThreadMessage::WebDriverScriptCommand(pipeline_id, cmd);
let result = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.event_loop.send(control_msg),
@@ -4448,27 +4526,22 @@ where
self.handle_send_error(pipeline_id, e);
}
},
WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => {
self.compositor_proxy.send(CompositorMsg::CreatePng(
webview_id,
rect,
response_sender,
));
},
WebDriverCommandMsg::CloseWebView(..) |
WebDriverCommandMsg::NewWebView(..) |
WebDriverCommandMsg::FocusWebView(..) |
WebDriverCommandMsg::IsWebViewOpen(..) |
WebDriverCommandMsg::GetWindowRect(..) |
WebDriverCommandMsg::GetViewportSize(..) |
WebDriverCommandMsg::SetWindowSize(..) |
WebDriverCommandMsg::SetWindowRect(..) |
WebDriverCommandMsg::MaximizeWebView(..) |
WebDriverCommandMsg::LoadUrl(..) |
WebDriverCommandMsg::Refresh(..) |
WebDriverCommandMsg::SendKeys(..) |
WebDriverCommandMsg::DispatchComposition(..) |
WebDriverCommandMsg::KeyboardAction(..) |
WebDriverCommandMsg::MouseButtonAction(..) |
WebDriverCommandMsg::MouseMoveAction(..) |
WebDriverCommandMsg::WheelScrollAction(..) => {
WebDriverCommandMsg::WheelScrollAction(..) |
WebDriverCommandMsg::TakeScreenshot(..) => {
unreachable!("This command should be send directly to the embedder.");
},
_ => {
@@ -4929,10 +5002,6 @@ where
let browsing_context_id = BrowsingContextId::from(webview_id);
self.resize_browsing_context(new_viewport_details, size_type, browsing_context_id);
if let Some(response_sender) = self.webdriver.resize_channel.take() {
let _ = response_sender.send(new_viewport_details.size);
}
}
/// Called when the window exits from fullscreen mode
@@ -5388,7 +5457,7 @@ where
};
// In order to get repeatability, we sort the pipeline ids.
let mut pipeline_ids: Vec<&PipelineId> = self.pipelines.keys().collect();
pipeline_ids.sort();
pipeline_ids.sort_unstable();
if let Some((ref mut rng, probability)) = self.random_pipeline_closure {
if let Some(pipeline_id) = pipeline_ids.choose(rng) {
if let Some(pipeline) = self.pipelines.get(pipeline_id) {

View File

@@ -2,9 +2,17 @@
* 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 base::id::BrowsingContextId;
use embedder_traits::Theme;
use std::collections::HashMap;
use base::id::{BrowsingContextId, PipelineId};
use embedder_traits::{InputEvent, MouseLeftViewportEvent, Theme};
use euclid::Point2D;
use log::warn;
use script_traits::{ConstellationInputEvent, ScriptThreadMessage};
use style_traits::CSSPixel;
use crate::browsingcontext::BrowsingContext;
use crate::pipeline::Pipeline;
use crate::session_history::JointSessionHistory;
/// The `Constellation`'s view of a `WebView` in the embedding layer. This tracks all of the
@@ -15,6 +23,14 @@ pub(crate) struct ConstellationWebView {
/// context.
pub focused_browsing_context_id: BrowsingContextId,
/// The [`BrowsingContextId`] of the currently hovered browsing context, to use for
/// knowing which frame is currently receiving cursor events.
pub hovered_browsing_context_id: Option<BrowsingContextId>,
/// The last mouse move point in the coordinate space of the Pipeline that it
/// happened int.
pub last_mouse_move_point: Point2D<f32, CSSPixel>,
/// The joint session history for this webview.
pub session_history: JointSessionHistory,
@@ -27,6 +43,8 @@ impl ConstellationWebView {
pub(crate) fn new(focused_browsing_context_id: BrowsingContextId) -> Self {
Self {
focused_browsing_context_id,
hovered_browsing_context_id: None,
last_mouse_move_point: Default::default(),
session_history: JointSessionHistory::new(),
theme: Theme::Light,
}
@@ -42,4 +60,93 @@ impl ConstellationWebView {
pub(crate) fn theme(&self) -> Theme {
self.theme
}
fn target_pipeline_id_for_input_event(
&self,
event: &ConstellationInputEvent,
browsing_contexts: &HashMap<BrowsingContextId, BrowsingContext>,
) -> Option<PipelineId> {
if let Some(hit_test_result) = &event.hit_test_result {
return Some(hit_test_result.pipeline_id);
}
// If there's no hit test, send the event to either the hovered or focused browsing context,
// depending on the event type.
let browsing_context_id = if matches!(event.event, InputEvent::MouseLeftViewport(_)) {
self.hovered_browsing_context_id
.unwrap_or(self.focused_browsing_context_id)
} else {
self.focused_browsing_context_id
};
Some(browsing_contexts.get(&browsing_context_id)?.pipeline_id)
}
pub(crate) fn forward_input_event(
&mut self,
event: ConstellationInputEvent,
pipelines: &HashMap<PipelineId, Pipeline>,
browsing_contexts: &HashMap<BrowsingContextId, BrowsingContext>,
) {
let Some(pipeline_id) = self.target_pipeline_id_for_input_event(&event, browsing_contexts)
else {
warn!("Unknown pipeline for input event. Ignoring.");
return;
};
let Some(pipeline) = pipelines.get(&pipeline_id) else {
warn!("Unknown pipeline id {pipeline_id:?} for input event. Ignoring.");
return;
};
let mut update_hovered_browsing_context =
|newly_hovered_browsing_context_id, focus_moving_to_another_iframe: bool| {
let old_hovered_context_id = std::mem::replace(
&mut self.hovered_browsing_context_id,
newly_hovered_browsing_context_id,
);
if old_hovered_context_id == newly_hovered_browsing_context_id {
return;
}
let Some(old_hovered_context_id) = old_hovered_context_id else {
return;
};
let Some(pipeline) = browsing_contexts
.get(&old_hovered_context_id)
.and_then(|browsing_context| pipelines.get(&browsing_context.pipeline_id))
else {
return;
};
let mut synthetic_mouse_leave_event = event.clone();
synthetic_mouse_leave_event.event =
InputEvent::MouseLeftViewport(MouseLeftViewportEvent {
focus_moving_to_another_iframe,
});
let _ = pipeline
.event_loop
.send(ScriptThreadMessage::SendInputEvent(
pipeline.id,
synthetic_mouse_leave_event,
));
};
if let InputEvent::MouseLeftViewport(_) = &event.event {
update_hovered_browsing_context(None, false);
return;
}
if let InputEvent::MouseMove(_) = &event.event {
update_hovered_browsing_context(Some(pipeline.browsing_context_id), true);
self.last_mouse_move_point = event
.hit_test_result
.as_ref()
.expect("MouseMove events should always have hit tests.")
.point_in_viewport;
}
let _ = pipeline
.event_loop
.send(ScriptThreadMessage::SendInputEvent(pipeline.id, event));
}
}

View File

@@ -11,15 +11,15 @@ use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
use base::generic_channel::GenericSender;
use ipc_channel::Error;
use ipc_channel::ipc::IpcSender;
use script_traits::ScriptThreadMessage;
static CURRENT_EVENT_LOOP_ID: AtomicUsize = AtomicUsize::new(0);
/// <https://html.spec.whatwg.org/multipage/#event-loop>
pub struct EventLoop {
script_chan: IpcSender<ScriptThreadMessage>,
script_chan: GenericSender<ScriptThreadMessage>,
dont_send_or_sync: PhantomData<Rc<()>>,
id: usize,
}
@@ -46,7 +46,7 @@ impl Drop for EventLoop {
impl EventLoop {
/// Create a new event loop from the channel to its script thread.
pub fn new(script_chan: IpcSender<ScriptThreadMessage>) -> Rc<EventLoop> {
pub fn new(script_chan: GenericSender<ScriptThreadMessage>) -> Rc<EventLoop> {
let id = CURRENT_EVENT_LOOP_ID.fetch_add(1, Ordering::Relaxed);
Rc::new(EventLoop {
script_chan,
@@ -57,6 +57,8 @@ impl EventLoop {
/// Send a message to the event loop.
pub fn send(&self, msg: ScriptThreadMessage) -> Result<(), Error> {
self.script_chan.send(msg)
self.script_chan
.send(msg)
.map_err(|_err| Box::new(ipc_channel::ErrorKind::Custom("SendError".into())))
}
}

View File

@@ -5,12 +5,14 @@
use std::collections::HashSet;
use std::rc::Rc;
use std::sync::Arc;
use std::thread::JoinHandle;
use background_hang_monitor::HangMonitorRegister;
use background_hang_monitor_api::{
BackgroundHangMonitorControlMsg, BackgroundHangMonitorRegister, HangMonitorAlert,
};
use base::Epoch;
use base::generic_channel::{GenericReceiver, GenericSender};
use base::id::{
BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespace, PipelineNamespaceId,
PipelineNamespaceRequest, WebViewId,
@@ -34,8 +36,8 @@ use layout_api::{LayoutFactory, ScriptThreadFactory};
use log::{debug, error, warn};
use media::WindowGLContext;
use net::image_cache::ImageCacheImpl;
use net_traits::ResourceThreads;
use net_traits::image_cache::ImageCache;
use net_traits::{CoreResourceThread, ResourceThreads};
use profile::system_reporter;
use profile_traits::mem::{ProfilerMsg, Reporter};
use profile_traits::{mem as profile_mem, time};
@@ -208,6 +210,7 @@ pub struct NewPipeline {
pub pipeline: Pipeline,
pub bhm_control_chan: Option<IpcSender<BackgroundHangMonitorControlMsg>>,
pub lifeline: Option<(IpcReceiver<()>, Process)>,
pub join_handle: Option<JoinHandle<()>>,
}
impl Pipeline {
@@ -217,7 +220,7 @@ impl Pipeline {
) -> Result<NewPipeline, Error> {
// Note: we allow channel creation to panic, since recovering from this
// probably requires a general low-memory strategy.
let (script_chan, (bhm_control_chan, lifeline)) = match state.event_loop {
let (script_chan, (bhm_control_chan, lifeline, join_handle)) = match state.event_loop {
Some(script_chan) => {
let new_layout_info = NewLayoutInfo {
parent_info: state.parent_pipeline_id,
@@ -234,10 +237,11 @@ impl Pipeline {
{
warn!("Sending to script during pipeline creation failed ({})", e);
}
(script_chan, (None, None))
(script_chan, (None, None, None))
},
None => {
let (script_chan, script_port) = ipc::channel().expect("Pipeline script chan");
let (script_chan, script_port) =
base::generic_channel::channel().expect("Pipeline script chan");
// Route messages coming from content to devtools as appropriate.
let script_to_devtools_ipc_sender =
@@ -314,18 +318,18 @@ impl Pipeline {
ipc::channel().expect("Failed to create lifeline channel");
unprivileged_pipeline_content.lifeline_sender = Some(sender);
let process = unprivileged_pipeline_content.spawn_multiprocess()?;
(Some(bhm_control_chan), Some((receiver, process)))
(Some(bhm_control_chan), Some((receiver, process)), None)
} else {
// Should not be None in single-process mode.
let register = state
.background_monitor_register
.expect("Couldn't start content, no background monitor has been initiated");
unprivileged_pipeline_content.start_all::<STF>(
let join_handle = unprivileged_pipeline_content.start_all::<STF>(
false,
state.layout_factory,
register,
);
(None, None)
(None, None, Some(join_handle))
};
(EventLoop::new(script_chan), multiprocess_data)
@@ -346,6 +350,7 @@ impl Pipeline {
pipeline,
bhm_control_chan,
lifeline,
join_handle,
})
}
@@ -479,9 +484,9 @@ pub struct UnprivilegedPipelineContent {
mem_profiler_chan: profile_mem::ProfilerChan,
viewport_details: ViewportDetails,
theme: Theme,
script_chan: IpcSender<ScriptThreadMessage>,
script_chan: GenericSender<ScriptThreadMessage>,
load_data: LoadData,
script_port: IpcReceiver<ScriptThreadMessage>,
script_port: GenericReceiver<ScriptThreadMessage>,
opts: Opts,
prefs: Box<Preferences>,
pipeline_namespace_id: PipelineNamespaceId,
@@ -500,7 +505,7 @@ impl UnprivilegedPipelineContent {
wait_for_completion: bool,
layout_factory: Arc<dyn LayoutFactory>,
background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
) {
) -> JoinHandle<()> {
// Setup pipeline-namespace-installing for all threads in this process.
// Idempotent in single-process mode.
PipelineNamespace::set_installer_sender(self.namespace_request_sender);
@@ -510,7 +515,7 @@ impl UnprivilegedPipelineContent {
self.rippy_data,
));
let (content_process_shutdown_chan, content_process_shutdown_port) = unbounded();
STF::create(
let join_handle = STF::create(
InitialScriptState {
id: self.id,
browsing_context_id: self.browsing_context_id,
@@ -550,6 +555,8 @@ impl UnprivilegedPipelineContent {
Err(_) => error!("Script-thread shut-down unexpectedly"),
}
}
join_handle
}
pub fn spawn_multiprocess(self) -> Result<Process, Error> {
@@ -558,7 +565,7 @@ impl UnprivilegedPipelineContent {
pub fn register_with_background_hang_monitor(
&mut self,
) -> Box<dyn BackgroundHangMonitorRegister> {
) -> (Box<dyn BackgroundHangMonitorRegister>, JoinHandle<()>) {
HangMonitorRegister::init(
self.background_hang_monitor_to_constellation_chan.clone(),
self.bhm_control_port.take().expect("no sampling profiler?"),
@@ -570,6 +577,10 @@ impl UnprivilegedPipelineContent {
&self.script_to_constellation_chan
}
pub fn core_resource_thread(&self) -> &CoreResourceThread {
&self.resource_threads.core_thread
}
pub fn opts(&self) -> Opts {
self.opts.clone()
}

View File

@@ -67,7 +67,7 @@ mod from_compositor {
Self::FocusWebView(..) => target!("FocusWebView"),
Self::BlurWebView => target!("BlurWebView"),
Self::ForwardInputEvent(_webview_id, event, ..) => event.log_target(),
Self::SetCursor(..) => target!("SetCursor"),
Self::RefreshCursor(..) => target!("RefreshCursor"),
Self::ToggleProfiler(..) => target!("EnableProfiler"),
Self::ExitFullScreen(_) => target!("ExitFullScreen"),
Self::MediaSessionAction(_) => target!("MediaSessionAction"),
@@ -78,6 +78,7 @@ mod from_compositor {
Self::CreateMemoryReport(..) => target!("CreateMemoryReport"),
Self::SendImageKeysForPipeline(..) => target!("SendImageKeysForPipeline"),
Self::SetWebDriverResponseSender(..) => target!("SetWebDriverResponseSender"),
Self::PreferencesUpdated(..) => target!("PreferencesUpdated"),
}
}
}
@@ -96,7 +97,7 @@ mod from_compositor {
InputEvent::Keyboard(..) => target_variant!("Keyboard"),
InputEvent::MouseButton(..) => target_variant!("MouseButton"),
InputEvent::MouseMove(..) => target_variant!("MouseMove"),
InputEvent::MouseLeave(..) => target_variant!("MouseLeave"),
InputEvent::MouseLeftViewport(..) => target_variant!("MouseLeftViewport"),
InputEvent::Touch(..) => target_variant!("Touch"),
InputEvent::Wheel(..) => target_variant!("Wheel"),
InputEvent::Scroll(..) => target_variant!("Scroll"),

View File

@@ -67,11 +67,14 @@ impl<WebView> WebViewManager<WebView> {
}
}
pub fn focus(&mut self, webview_id: WebViewId) {
debug_assert!(self.webviews.contains_key(&webview_id));
pub fn focus(&mut self, webview_id: WebViewId) -> Result<(), ()> {
if !self.webviews.contains_key(&webview_id) {
return Err(());
}
self.focus_order.retain(|b| *b != webview_id);
self.focus_order.push(webview_id);
self.is_focused = true;
Ok(())
}
pub fn unfocus(&mut self) {
@@ -96,7 +99,7 @@ mod test {
webviews: &WebViewManager<WebView>,
) -> Vec<(WebViewId, WebView)> {
let mut keys = webviews.webviews.keys().collect::<Vec<_>>();
keys.sort();
keys.sort_unstable();
keys.iter()
.map(|&id| {
(
@@ -128,13 +131,13 @@ mod test {
assert_eq!(webviews.is_focused, false);
// focus() makes the given webview the latest in focus order.
webviews.focus(id(0, 2));
let _ = webviews.focus(id(0, 2));
assert_eq!(webviews.focus_order, vec![id(0, 2)]);
assert_eq!(webviews.is_focused, true);
webviews.focus(id(0, 1));
let _ = webviews.focus(id(0, 1));
assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 1)]);
assert_eq!(webviews.is_focused, true);
webviews.focus(id(0, 3));
let _ = webviews.focus(id(0, 3));
assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 1), id(0, 3)]);
assert_eq!(webviews.is_focused, true);
@@ -144,7 +147,7 @@ mod test {
assert_eq!(webviews.is_focused, false);
// focus() avoids duplicates in focus order, when the given webview has been focused before.
webviews.focus(id(0, 1));
let _ = webviews.focus(id(0, 1));
assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 3), id(0, 1)]);
assert_eq!(webviews.is_focused, true);

View File

@@ -13,6 +13,7 @@ path = "lib.rs"
[dependencies]
base = { workspace = true }
base64 = { workspace = true }
chrono = { workspace = true }
crossbeam-channel = { workspace = true }
devtools_traits = { workspace = true }
@@ -21,6 +22,7 @@ headers = { workspace = true }
http = { workspace = true }
ipc-channel = { workspace = true }
log = { workspace = true }
net = { path = "../net" }
net_traits = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
@@ -29,5 +31,6 @@ servo_rand = { path = "../rand" }
servo_url = { path = "../url" }
uuid = { workspace = true }
[build-dependencies]
chrono = { workspace = true }

View File

@@ -11,7 +11,7 @@ use std::sync::{Arc, Mutex};
use base::cross_process_instant::CrossProcessInstant;
use base::id::PipelineId;
use log::debug;
use log::{debug, warn};
use serde_json::{Map, Value, json};
use crate::StreamId;
@@ -84,6 +84,8 @@ pub struct ActorRegistry {
/// Lookup table for SourceActor names associated with a given PipelineId.
source_actor_names: RefCell<HashMap<PipelineId, Vec<String>>>,
/// Lookup table for inline source content associated with a given PipelineId.
inline_source_content: RefCell<HashMap<PipelineId, String>>,
shareable: Option<Arc<Mutex<ActorRegistry>>>,
next: Cell<u32>,
@@ -99,6 +101,7 @@ impl ActorRegistry {
old_actors: RefCell::new(vec![]),
script_actors: RefCell::new(HashMap::new()),
source_actor_names: RefCell::new(HashMap::new()),
inline_source_content: RefCell::new(HashMap::new()),
shareable: None,
next: Cell::new(0),
start_stamp: CrossProcessInstant::now(),
@@ -220,9 +223,11 @@ impl ActorRegistry {
actor.handle_message(req, self, msg_type, msg, stream_id)
}) {
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#error-packets>
let _ = stream.write_json_packet(&json!({
let error = json!({
"from": actor.name(), "error": error.name()
}));
});
warn!("Sending devtools protocol error: error={error:?} request={msg:?}");
let _ = stream.write_json_packet(&error);
}
},
}
@@ -262,4 +267,20 @@ impl ActorRegistry {
vec![]
}
pub fn set_inline_source_content(&mut self, pipeline_id: PipelineId, content: String) {
assert!(
self.inline_source_content
.borrow_mut()
.insert(pipeline_id, content)
.is_none()
);
}
pub fn inline_source_content(&mut self, pipeline_id: PipelineId) -> Option<String> {
self.inline_source_content
.borrow()
.get(&pipeline_id)
.cloned()
}
}

View File

@@ -31,6 +31,9 @@ impl Actor for BreakpointListActor {
_stream_id: crate::StreamId,
) -> Result<(), ActorError> {
match msg_type {
// Client wants to set a breakpoint.
// Seems to be infallible, unlike the thread actors `setBreakpoint`.
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#breakpoints>
"setBreakpoint" => {
let msg = EmptyReplyMsg { from: self.name() };
request.reply_final(&msg)?
@@ -39,6 +42,10 @@ impl Actor for BreakpointListActor {
let msg = EmptyReplyMsg { from: self.name() };
request.reply_final(&msg)?
},
"removeBreakpoint" => {
let msg = EmptyReplyMsg { from: self.name() };
request.reply_final(&msg)?
},
_ => return Err(ActorError::UnrecognizedPacketType),
};
Ok(())
@@ -49,4 +56,8 @@ impl BreakpointListActor {
pub fn new(name: String) -> Self {
Self { name }
}
pub fn encodable(&self) -> BreakpointListActorMsg {
BreakpointListActorMsg { actor: self.name() }
}
}

View File

@@ -373,7 +373,7 @@ impl Actor for ConsoleActor {
},
"startListeners" => {
//TODO: actually implement listener filters that support starting/stopping
// TODO: actually implement listener filters that support starting/stopping
let listeners = msg.get("listeners").unwrap().as_array().unwrap().to_owned();
let msg = StartedListenersReply {
from: self.name(),
@@ -388,7 +388,7 @@ impl Actor for ConsoleActor {
},
"stopListeners" => {
//TODO: actually implement listener filters that support starting/stopping
// TODO: actually implement listener filters that support starting/stopping
let msg = StopListenersReply {
from: self.name(),
stopped_listeners: msg
@@ -403,7 +403,7 @@ impl Actor for ConsoleActor {
request.reply_final(&msg)?
},
//TODO: implement autocompletion like onAutocomplete in
// TODO: implement autocompletion like onAutocomplete in
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js
"autocomplete" => {
let msg = AutocompleteReply {

View File

@@ -92,13 +92,8 @@ impl Actor for InspectorActor {
.clone()
.unwrap_or_else(|| registry.new_name("walker"));
let root = root_info.encode(
registry,
false,
self.script_chan.clone(),
pipeline,
name.clone(),
);
let root =
root_info.encode(registry, self.script_chan.clone(), pipeline, name.clone());
if self.walker.borrow().is_none() {
let walker = WalkerActor {

View File

@@ -59,6 +59,9 @@ pub struct NodeActorMsg {
is_anonymous: bool,
is_before_pseudo_element: bool,
is_direct_shadow_host_child: Option<bool>,
/// Whether or not this node is displayed.
///
/// Setting this value to `false` will cause the devtools to render the node name in gray.
is_displayed: bool,
#[serde(rename = "isInHTMLDocument")]
is_in_html_document: Option<bool>,
@@ -158,7 +161,6 @@ impl Actor for NodeActor {
.ok_or(ActorError::Internal)?;
let node = doc_elem_info.encode(
registry,
true,
self.script_chan.clone(),
self.pipeline,
self.walker.clone(),
@@ -181,7 +183,6 @@ pub trait NodeInfoToProtocol {
fn encode(
self,
actors: &ActorRegistry,
display: bool,
script_chan: IpcSender<DevtoolScriptControlMsg>,
pipeline: PipelineId,
walker: String,
@@ -192,7 +193,6 @@ impl NodeInfoToProtocol for NodeInfo {
fn encode(
self,
actors: &ActorRegistry,
display: bool,
script_chan: IpcSender<DevtoolScriptControlMsg>,
pipeline: PipelineId,
walker: String,
@@ -239,7 +239,7 @@ impl NodeInfoToProtocol for NodeInfo {
let mut children = rx.recv().ok()??;
let child = children.pop()?;
let msg = child.encode(actors, true, script_chan.clone(), pipeline, walker);
let msg = child.encode(actors, script_chan.clone(), pipeline, walker);
// If the node child is not a text node, do not represent it inline.
if msg.node_type != TEXT_NODE {
@@ -267,7 +267,7 @@ impl NodeInfoToProtocol for NodeInfo {
is_anonymous: false,
is_before_pseudo_element: false,
is_direct_shadow_host_child: None,
is_displayed: display,
is_displayed: self.is_displayed,
is_in_html_document: Some(true),
is_marker_pseudo_element: false,
is_native_anonymous: false,

View File

@@ -175,42 +175,41 @@ impl PageStyleActor {
// For each selector (plus an empty one that represents the style attribute)
// get all of the rules associated with it.
let entries =
once(("".into(), usize::MAX))
.chain(selectors)
.filter_map(move |selector| {
let rule = match node_actor.style_rules.borrow_mut().entry(selector) {
Entry::Vacant(e) => {
let name = registry.new_name("style-rule");
let actor = StyleRuleActor::new(
name.clone(),
node_actor.name(),
(!e.key().0.is_empty()).then_some(e.key().clone()),
);
let rule = actor.applied(registry)?;
registry.register_later(Box::new(actor));
e.insert(name);
rule
},
Entry::Occupied(e) => {
let actor = registry.find::<StyleRuleActor>(e.get());
actor.applied(registry)?
},
};
if inherited.is_some() && rule.declarations.is_empty() {
return None;
}
once(("".into(), usize::MAX))
.chain(selectors)
.filter_map(move |selector| {
let rule = match node_actor.style_rules.borrow_mut().entry(selector) {
Entry::Vacant(e) => {
let name = registry.new_name("style-rule");
let actor = StyleRuleActor::new(
name.clone(),
node_actor.name(),
(!e.key().0.is_empty()).then_some(e.key().clone()),
);
let rule = actor.applied(registry)?;
Some(AppliedEntry {
rule,
// TODO: Handle pseudo elements
pseudo_element: None,
is_system: false,
inherited: inherited.clone(),
})
});
entries
registry.register_later(Box::new(actor));
e.insert(name);
rule
},
Entry::Occupied(e) => {
let actor = registry.find::<StyleRuleActor>(e.get());
actor.applied(registry)?
},
};
if inherited.is_some() && rule.declarations.is_empty() {
return None;
}
Some(AppliedEntry {
rule,
// TODO: Handle pseudo elements
pseudo_element: None,
is_system: false,
inherited: inherited.clone(),
})
})
})
.collect();
let msg = GetAppliedReply {

View File

@@ -156,7 +156,6 @@ impl Actor for WalkerActor {
.map(|child| {
child.encode(
registry,
true,
self.script_chan.clone(),
self.pipeline,
self.name(),
@@ -182,7 +181,6 @@ impl Actor for WalkerActor {
.ok_or(ActorError::Internal)?;
let node = doc_elem_info.encode(
registry,
true,
self.script_chan.clone(),
self.pipeline,
self.name(),
@@ -319,7 +317,7 @@ pub fn find_child(
let children = rx.recv().unwrap().ok_or(vec![])?;
for child in children {
let msg = child.encode(registry, true, script_chan.clone(), pipeline, name.into());
let msg = child.encode(registry, script_chan.clone(), pipeline, name.into());
if compare_fn(&msg) {
hierarchy.push(msg);
return Ok(hierarchy);

View File

@@ -0,0 +1,87 @@
/* 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 serde::Serialize;
use serde_json::{Map, Value};
use crate::StreamId;
use crate::actor::{Actor, ActorError, ActorRegistry};
use crate::protocol::ClientRequest;
const INITIAL_LENGTH: usize = 500;
pub struct LongStringActor {
name: String,
full_string: String,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LongStringObj {
#[serde(rename = "type")]
type_: String,
actor: String,
length: usize,
initial: String,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct SubstringReply {
from: String,
substring: String,
}
impl Actor for LongStringActor {
fn name(&self) -> String {
self.name.clone()
}
fn handle_message(
&self,
request: ClientRequest,
_registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
_id: StreamId,
) -> Result<(), ActorError> {
match msg_type {
"substring" => {
let start = msg.get("start").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
let end = msg
.get("end")
.and_then(|v| v.as_u64())
.unwrap_or(self.full_string.len() as u64) as usize;
let substring: String = self
.full_string
.chars()
.skip(start)
.take(end - start)
.collect();
let reply = SubstringReply {
from: self.name(),
substring,
};
request.reply_final(&reply)?
},
_ => return Err(ActorError::UnrecognizedPacketType),
}
Ok(())
}
}
impl LongStringActor {
pub fn new(registry: &ActorRegistry, full_string: String) -> Self {
let name = registry.new_name("longStringActor");
LongStringActor { name, full_string }
}
pub fn long_string_obj(&self) -> LongStringObj {
LongStringObj {
type_: "longString".to_string(),
actor: self.name.clone(),
length: self.full_string.len(),
initial: self.full_string.chars().take(INITIAL_LENGTH).collect(),
}
}
}

View File

@@ -7,15 +7,22 @@
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use base64::engine::Engine;
use base64::engine::general_purpose::STANDARD;
use chrono::{Local, LocalResult, TimeZone};
use devtools_traits::{HttpRequest as DevtoolsHttpRequest, HttpResponse as DevtoolsHttpResponse};
use headers::{ContentType, Cookie, HeaderMapExt};
use http::{HeaderMap, Method, header};
use headers::{ContentLength, ContentType, Cookie, HeaderMapExt};
use http::{HeaderMap, Method};
use net::cookie::ServoCookie;
use net_traits::CookieSource;
use net_traits::request::Destination as RequestDestination;
use serde::Serialize;
use serde_json::{Map, Value};
use servo_url::ServoUrl;
use crate::StreamId;
use crate::actor::{Actor, ActorError, ActorRegistry};
use crate::actors::long_string::LongStringActor;
use crate::network_handler::Cause;
use crate::protocol::ClientRequest;
@@ -27,6 +34,7 @@ pub struct NetworkEventActor {
pub request_method: Method,
pub request_started: SystemTime,
pub request_time_stamp: i64,
pub request_destination: RequestDestination,
pub request_headers_raw: Option<HeaderMap>,
pub request_body: Option<Vec<u8>>,
pub request_cookies: Option<RequestCookiesMsg>,
@@ -69,7 +77,7 @@ pub struct EventActor {
#[derive(Serialize)]
pub struct ResponseCookiesMsg {
pub cookies: usize,
pub cookies: Vec<ResponseCookieObj>,
}
#[derive(Serialize)]
@@ -102,7 +110,7 @@ pub struct ResponseHeadersMsg {
#[derive(Serialize)]
pub struct RequestCookiesMsg {
pub cookies: usize,
pub cookies: Vec<RequestCookieObj>,
}
#[derive(Serialize)]
@@ -140,7 +148,7 @@ struct GetResponseHeadersReply {
#[serde(rename_all = "camelCase")]
struct GetResponseContentReply {
from: String,
content: Option<Vec<u8>>,
content: Option<ResponseContentObj>,
content_discarded: bool,
}
@@ -155,13 +163,46 @@ struct GetRequestPostDataReply {
#[derive(Serialize)]
struct GetRequestCookiesReply {
from: String,
cookies: Vec<u8>,
cookies: Vec<RequestCookieObj>,
}
#[derive(Serialize)]
struct GetResponseCookiesReply {
from: String,
cookies: Vec<u8>,
cookies: Vec<ResponseCookieObj>,
}
#[derive(Clone, Serialize)]
pub struct ResponseCookieObj {
pub name: String,
pub value: String,
pub path: Option<String>,
pub domain: Option<String>,
pub expires: Option<String>,
#[serde(rename = "httpOnly")]
pub http_only: Option<bool>,
pub secure: Option<bool>,
#[serde(rename = "sameSite")]
pub same_site: Option<String>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ResponseContentObj {
mime_type: String,
text: Value,
body_size: usize,
decoded_body_size: usize,
size: usize,
headers_size: usize,
transferred_size: usize,
#[serde(skip_serializing_if = "Option::is_none")]
encoding: Option<String>,
}
#[derive(Clone, Serialize)]
pub struct RequestCookieObj {
pub name: String,
pub value: String,
}
#[derive(Clone, Default, Serialize)]
@@ -202,7 +243,7 @@ impl Actor for NetworkEventActor {
fn handle_message(
&self,
request: ClientRequest,
_registry: &ActorRegistry,
registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
_id: StreamId,
@@ -234,14 +275,11 @@ impl Actor for NetworkEventActor {
request.reply_final(&msg)?
},
"getRequestCookies" => {
let mut cookies = Vec::new();
if let Some(ref headers) = self.request_headers_raw {
for cookie in headers.get_all(header::COOKIE) {
if let Ok(cookie_value) = String::from_utf8(cookie.as_bytes().to_vec()) {
cookies = cookie_value.into_bytes();
}
}
}
let cookies = self
.request_cookies
.as_ref()
.map(|msg| msg.cookies.clone())
.unwrap_or_default();
let msg = GetRequestCookiesReply {
from: self.name(),
cookies,
@@ -285,15 +323,11 @@ impl Actor for NetworkEventActor {
}
},
"getResponseCookies" => {
let mut cookies = Vec::new();
// TODO: This seems quite broken
if let Some(ref headers) = self.response_headers_raw {
for cookie in headers.get_all(header::SET_COOKIE) {
if let Ok(cookie_value) = String::from_utf8(cookie.as_bytes().to_vec()) {
cookies = cookie_value.into_bytes();
}
}
}
let cookies = self
.response_cookies
.as_ref()
.map(|msg| msg.cookies.clone())
.unwrap_or_default();
let msg = GetResponseCookiesReply {
from: self.name(),
cookies,
@@ -301,9 +335,61 @@ impl Actor for NetworkEventActor {
request.reply_final(&msg)?
},
"getResponseContent" => {
let content_obj = self.response_body.as_ref().map(|body| {
let mime_type = self
.response_content
.as_ref()
.map(|c| c.mime_type.clone())
.unwrap_or_default();
let headers_size = self
.response_headers
.as_ref()
.map(|h| h.headers_size)
.unwrap_or(0);
let transferred_size = self
.response_content
.as_ref()
.map(|c| c.transferred_size as usize)
.unwrap_or(0);
let body_size = body.len();
let decoded_body_size = body.len();
let size = body.len();
if Self::is_text_mime(&mime_type) {
let full_str = String::from_utf8_lossy(body).to_string();
// Queue a LongStringActor for this body
let long_string_actor = LongStringActor::new(registry, full_str);
let long_string_obj = long_string_actor.long_string_obj();
registry.register_later(Box::new(long_string_actor));
ResponseContentObj {
mime_type,
text: serde_json::to_value(long_string_obj).unwrap(),
body_size,
decoded_body_size,
size,
headers_size,
transferred_size,
encoding: None,
}
} else {
let b64 = STANDARD.encode(body);
ResponseContentObj {
mime_type,
text: serde_json::to_value(b64).unwrap(),
body_size,
decoded_body_size,
size,
headers_size,
transferred_size,
encoding: Some("base64".to_string()),
}
}
});
let msg = GetResponseContentReply {
from: self.name(),
content: self.response_body.clone(),
content: content_obj,
content_discarded: self.response_body.is_none(),
};
request.reply_final(&msg)?
@@ -350,6 +436,7 @@ impl NetworkEventActor {
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64,
request_destination: RequestDestination::None,
request_headers_raw: None,
request_body: None,
request_cookies: None,
@@ -369,7 +456,7 @@ impl NetworkEventActor {
pub fn add_request(&mut self, request: DevtoolsHttpRequest) {
self.is_xhr = request.is_xhr;
self.request_cookies = Some(Self::request_cookies(&request));
self.request_cookies = Self::request_cookies(&request);
self.request_headers = Some(Self::request_headers(&request));
self.total_time = Self::total_time(&request);
self.event_timing = Some(Self::event_timing(&request));
@@ -377,16 +464,21 @@ impl NetworkEventActor {
self.request_method = request.method;
self.request_started = request.started_date_time;
self.request_time_stamp = request.time_stamp;
self.request_destination = request.destination;
self.request_body = request.body.clone();
self.request_headers_raw = Some(request.headers.clone());
}
pub fn add_response(&mut self, response: DevtoolsHttpResponse) {
self.response_headers = Some(Self::response_headers(&response));
self.response_cookies = Some(Self::response_cookies(&response));
self.response_cookies = ServoUrl::parse(&self.request_url)
.ok()
.as_ref()
.and_then(|url| Self::response_cookies(&response, url));
self.response_start = Some(Self::response_start(&response));
self.response_content = Some(Self::response_content(&response));
self.response_body = response.body.clone();
if let Some(response_content) = Self::response_content(self, &response) {
self.response_content = Some(response_content);
}
self.response_headers_raw = response.headers.clone();
}
@@ -404,14 +496,6 @@ impl NetworkEventActor {
LocalResult::Ambiguous(date_time, _) => date_time.to_rfc3339().to_string(),
};
let cause_type = match self.request_url.as_str() {
// Adjust based on request data
url if url.ends_with(".css") => "stylesheet",
url if url.ends_with(".js") => "script",
url if url.ends_with(".png") || url.ends_with(".jpg") => "img",
_ => "document",
};
EventActor {
actor: self.name(),
resource_id: self.resource_id,
@@ -422,7 +506,7 @@ impl NetworkEventActor {
is_xhr: self.is_xhr,
private: false,
cause: Cause {
type_: cause_type.to_string(),
type_: self.request_destination.as_str().to_string(),
loading_document_uri: None, // Set if available
},
}
@@ -445,7 +529,13 @@ impl NetworkEventActor {
}
}
pub fn response_content(response: &DevtoolsHttpResponse) -> ResponseContentMsg {
pub fn response_content(
&mut self,
response: &DevtoolsHttpResponse,
) -> Option<ResponseContentMsg> {
let body = response.body.as_ref()?;
self.response_body = Some(body.clone());
let mime_type = response
.headers
.as_ref()
@@ -453,24 +543,49 @@ impl NetworkEventActor {
.map(|ct| ct.to_string())
.unwrap_or_default();
// TODO: Set correct values when response's body is sent to the devtools in http_loader.
ResponseContentMsg {
mime_type,
content_size: 0,
transferred_size: 0,
discard_response_body: true,
}
}
pub fn response_cookies(response: &DevtoolsHttpResponse) -> ResponseCookiesMsg {
let cookies_size = response
let transferred_size = response
.headers
.as_ref()
.map(|headers| headers.get_all("set-cookie").iter().count())
.unwrap_or(0);
ResponseCookiesMsg {
cookies: cookies_size,
}
.and_then(|hdrs| hdrs.typed_get::<ContentLength>())
.map(|cl| cl.0);
let content_size = response.body.as_ref().map(|body| body.len() as u64);
Some(ResponseContentMsg {
mime_type,
content_size: content_size.unwrap_or(0) as u32,
transferred_size: transferred_size.unwrap_or(0) as u32,
discard_response_body: false,
})
}
pub fn response_cookies(
response: &DevtoolsHttpResponse,
url: &ServoUrl,
) -> Option<ResponseCookiesMsg> {
let headers = response.headers.as_ref()?;
let cookies = headers
.get_all("set-cookie")
.iter()
.filter_map(|cookie| {
let cookie_str = String::from_utf8(cookie.as_bytes().to_vec()).ok()?;
ServoCookie::from_cookie_string(cookie_str, url, CookieSource::HTTP)
})
.map(|servo_cookie| {
let c = &servo_cookie.cookie;
ResponseCookieObj {
name: c.name().to_string(),
value: c.value().to_string(),
path: c.path().map(|p| p.to_string()),
domain: c.domain().map(|d| d.to_string()),
expires: c.expires().map(|dt| format!("{:?}", dt)),
http_only: c.http_only(),
secure: c.secure(),
same_site: c.same_site().map(|s| s.to_string()),
}
})
.collect::<Vec<_>>();
Some(ResponseCookiesMsg { cookies })
}
pub fn response_headers(response: &DevtoolsHttpResponse) -> ResponseHeadersMsg {
@@ -498,15 +613,16 @@ impl NetworkEventActor {
}
}
pub fn request_cookies(request: &DevtoolsHttpRequest) -> RequestCookiesMsg {
let cookies_size = request
.headers
.typed_get::<Cookie>()
.map(|c| c.len())
.unwrap_or(0);
RequestCookiesMsg {
cookies: cookies_size,
}
pub fn request_cookies(request: &DevtoolsHttpRequest) -> Option<RequestCookiesMsg> {
let header_value = request.headers.typed_get::<Cookie>()?;
let cookies = header_value
.iter()
.map(|cookie| RequestCookieObj {
name: cookie.0.to_string(),
value: cookie.1.to_string(),
})
.collect::<Vec<_>>();
Some(RequestCookiesMsg { cookies })
}
pub fn total_time(request: &DevtoolsHttpRequest) -> Duration {
@@ -524,6 +640,16 @@ impl NetworkEventActor {
}
}
pub fn is_text_mime(mime: &str) -> bool {
let lower = mime.to_ascii_lowercase();
lower.starts_with("text/") ||
lower.contains("json") ||
lower.contains("javascript") ||
lower.contains("xml") ||
lower.contains("csv") ||
lower.contains("html")
}
fn insert_serialized_map<T: Serialize>(map: &mut Map<String, Value>, obj: &Option<T>) {
if let Some(value) = obj {
if let Ok(Value::Object(serialized)) = serde_json::to_value(value) {

View File

@@ -3,9 +3,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};
use base::id::PipelineId;
use devtools_traits::DevtoolScriptControlMsg;
use ipc_channel::ipc::{IpcSender, channel};
use serde::Serialize;
use serde_json::{Map, Value};
use servo_url::ServoUrl;
@@ -27,6 +29,8 @@ pub(crate) struct SourceForm {
/// URL of the script, or URL of the page for inline scripts.
pub url: String,
pub is_black_boxed: bool,
/// `introductionType` in SpiderMonkey `CompileOptionsWrapper`.
pub introduction_type: String,
}
#[derive(Serialize)]
@@ -52,17 +56,43 @@ pub struct SourceActor {
pub is_black_boxed: bool,
pub content: Option<String>,
pub content_type: String,
pub content_type: Option<String>,
// TODO: use it in #37667, then remove this allow
#[allow(unused)]
pub spidermonkey_id: u32,
/// `introductionType` in SpiderMonkey `CompileOptionsWrapper`.
pub introduction_type: String,
script_sender: IpcSender<DevtoolScriptControlMsg>,
}
#[derive(Serialize)]
struct SourceContentReply {
from: String,
#[serde(rename = "contentType")]
content_type: String,
content_type: Option<String>,
source: String,
}
#[derive(Serialize)]
struct GetBreakableLinesReply {
from: String,
// Line numbers are one-based.
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#source-locations>
lines: BTreeSet<u32>,
}
#[derive(Serialize)]
struct GetBreakpointPositionsCompressedReply {
from: String,
// Column numbers are in UTF-16 code units, not Unicode scalar values or grapheme clusters.
// Line number are one-based. Column numbers are zero-based.
// FIXME: the docs say column numbers are one-based, but this appears to be incorrect.
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#source-locations>
positions: BTreeMap<u32, BTreeSet<u32>>,
}
impl SourceManager {
pub fn new() -> Self {
Self {
@@ -90,7 +120,10 @@ impl SourceActor {
name: String,
url: ServoUrl,
content: Option<String>,
content_type: String,
content_type: Option<String>,
spidermonkey_id: u32,
introduction_type: String,
script_sender: IpcSender<DevtoolScriptControlMsg>,
) -> SourceActor {
SourceActor {
name,
@@ -98,19 +131,34 @@ impl SourceActor {
content,
content_type,
is_black_boxed: false,
spidermonkey_id,
introduction_type,
script_sender,
}
}
#[allow(clippy::too_many_arguments)]
pub fn new_registered(
actors: &mut ActorRegistry,
pipeline_id: PipelineId,
url: ServoUrl,
content: Option<String>,
content_type: String,
content_type: Option<String>,
spidermonkey_id: u32,
introduction_type: String,
script_sender: IpcSender<DevtoolScriptControlMsg>,
) -> &SourceActor {
let source_actor_name = actors.new_name("source");
let source_actor = SourceActor::new(source_actor_name.clone(), url, content, content_type);
let source_actor = SourceActor::new(
source_actor_name.clone(),
url,
content,
content_type,
spidermonkey_id,
introduction_type,
script_sender,
);
actors.register(Box::new(source_actor));
actors.register_source_actor(pipeline_id, &source_actor_name);
@@ -122,6 +170,7 @@ impl SourceActor {
actor: self.name.clone(),
url: self.url.to_string(),
is_black_boxed: self.is_black_boxed,
introduction_type: self.introduction_type.clone(),
}
}
}
@@ -145,6 +194,10 @@ impl Actor for SourceActor {
let reply = SourceContentReply {
from: self.name(),
content_type: self.content_type.clone(),
// TODO: if needed, fetch the page again, in the same way as in the original request.
// Fetch it from cache, even if the original request was non-idempotent (e.g. POST).
// If we cant fetch it from cache, we should probably give up, because with a real
// fetch, the server could return a different response.
// TODO: do we want to wait instead of giving up immediately, in cases where the content could
// become available later (e.g. after a fetch)?
source: self
@@ -155,6 +208,54 @@ impl Actor for SourceActor {
};
request.reply_final(&reply)?
},
// Client wants to know which lines can have breakpoints.
// Sent when opening a source in the Sources panel, and controls whether the line numbers can be clicked.
"getBreakableLines" => {
let (tx, rx) = channel().map_err(|_| ActorError::Internal)?;
self.script_sender
.send(DevtoolScriptControlMsg::GetPossibleBreakpoints(
self.spidermonkey_id,
tx,
))
.map_err(|_| ActorError::Internal)?;
let result = rx.recv().map_err(|_| ActorError::Internal)?;
let lines = result
.into_iter()
.map(|entry| entry.line_number)
.collect::<BTreeSet<_>>();
let reply = GetBreakableLinesReply {
from: self.name(),
lines,
};
request.reply_final(&reply)?
},
// Client wants to know which columns in the line can have breakpoints.
// Sent when the user tries to set a breakpoint by clicking a line number in a source.
"getBreakpointPositionsCompressed" => {
let (tx, rx) = channel().map_err(|_| ActorError::Internal)?;
self.script_sender
.send(DevtoolScriptControlMsg::GetPossibleBreakpoints(
self.spidermonkey_id,
tx,
))
.map_err(|_| ActorError::Internal)?;
let result = rx.recv().map_err(|_| ActorError::Internal)?;
let mut positions: BTreeMap<u32, BTreeSet<u32>> = BTreeMap::default();
for entry in result {
// Line number are one-based. Column numbers are zero-based.
// FIXME: the docs say column numbers are one-based, but this appears to be incorrect.
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#source-locations>
positions
.entry(entry.line_number)
.or_default()
.insert(entry.column_number - 1);
}
let reply = GetBreakpointPositionsCompressedReply {
from: self.name(),
positions,
};
request.reply_final(&reply)?
},
_ => return Err(ActorError::UnrecognizedPacketType),
};
Ok(())

View File

@@ -126,7 +126,7 @@ impl Serialize for HighResolutionStamp {
}
}
static DEFAULT_TIMELINE_DATA_PULL_TIMEOUT: u64 = 200; //ms
static DEFAULT_TIMELINE_DATA_PULL_TIMEOUT: u64 = 200; // ms
impl TimelineActor {
pub fn new(
@@ -209,7 +209,7 @@ impl Actor for TimelineActor {
))
.unwrap();
//TODO: support multiple connections by using root actor's streams instead.
// TODO: support multiple connections by using root actor's streams instead.
*self.stream.borrow_mut() = request.try_clone_stream().ok();
// init memory actor
@@ -268,7 +268,7 @@ impl Actor for TimelineActor {
))
.unwrap();
//TODO: move this to the cleanup method.
// TODO: move this to the cleanup method.
if let Some(ref actor_name) = *self.framerate_actor.borrow() {
registry.drop_actor_later(actor_name.clone());
}

View File

@@ -25,6 +25,7 @@ use super::breakpoint::BreakpointListActor;
use super::thread::ThreadActor;
use super::worker::WorkerMsg;
use crate::actor::{Actor, ActorError, ActorRegistry};
use crate::actors::breakpoint::BreakpointListActorMsg;
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
use crate::actors::root::RootActor;
use crate::actors::watcher::target_configuration::{
@@ -151,12 +152,7 @@ struct GetThreadConfigurationActorReply {
#[serde(rename_all = "camelCase")]
struct GetBreakpointListActorReply {
from: String,
breakpoint_list: GetBreakpointListActorReplyInner,
}
#[derive(Serialize)]
struct GetBreakpointListActorReplyInner {
actor: String,
breakpoint_list: BreakpointListActorMsg,
}
#[derive(Serialize)]
@@ -191,6 +187,7 @@ pub struct WatcherActor {
network_parent: String,
target_configuration: String,
thread_configuration: String,
breakpoint_list: String,
session_context: SessionContext,
}
@@ -376,15 +373,10 @@ impl Actor for WatcherActor {
request.reply_final(&msg)?
},
"getBreakpointListActor" => {
let breakpoint_list_name = registry.new_name("breakpoint-list");
let breakpoint_list = BreakpointListActor::new(breakpoint_list_name.clone());
registry.register_later(Box::new(breakpoint_list));
let breakpoint_list = registry.find::<BreakpointListActor>(&self.breakpoint_list);
request.reply_final(&GetBreakpointListActorReply {
from: self.name(),
breakpoint_list: GetBreakpointListActorReplyInner {
actor: breakpoint_list_name,
},
breakpoint_list: breakpoint_list.encodable(),
})?
},
_ => return Err(ActorError::UnrecognizedPacketType),
@@ -410,6 +402,7 @@ impl WatcherActor {
TargetConfigurationActor::new(actors.new_name("target-configuration"));
let thread_configuration =
ThreadConfigurationActor::new(actors.new_name("thread-configuration"));
let breakpoint_list = BreakpointListActor::new(actors.new_name("breakpoint-list"));
let watcher = Self {
name: actors.new_name("watcher"),
@@ -417,12 +410,14 @@ impl WatcherActor {
network_parent: network_parent.name(),
target_configuration: target_configuration.name(),
thread_configuration: thread_configuration.name(),
breakpoint_list: breakpoint_list.name(),
session_context,
};
actors.register(Box::new(network_parent));
actors.register(Box::new(target_configuration));
actors.register(Box::new(thread_configuration));
actors.register(Box::new(breakpoint_list));
watcher
}

View File

@@ -13,7 +13,6 @@
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::io::Read;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
@@ -60,6 +59,7 @@ mod actors {
pub mod device;
pub mod framerate;
pub mod inspector;
pub mod long_string;
pub mod memory;
pub mod network_event;
pub mod object;
@@ -256,9 +256,10 @@ impl DevtoolsInstance {
worker_id,
)) => self.handle_console_message(pipeline_id, worker_id, console_message),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::CreateSourceActor(
script_sender,
pipeline_id,
source_info,
)) => self.handle_create_source_actor(pipeline_id, source_info),
)) => self.handle_create_source_actor(script_sender, pipeline_id, source_info),
DevtoolsControlMsg::FromScript(
ScriptToDevtoolsControlMsg::UpdateSourceContent(pipeline_id, source_content),
) => self.handle_update_source_content(pipeline_id, source_content),
@@ -509,7 +510,10 @@ impl DevtoolsInstance {
.watcher
.clone();
let netevent_actor_name = self.find_network_event_actor(request_id, watcher_name);
let netevent_actor_name = match self.actor_requests.get(&request_id) {
Some(name) => name.clone(),
None => self.create_network_event_actor(request_id, watcher_name),
};
handle_network_event(
Arc::clone(&self.actors),
@@ -519,36 +523,41 @@ impl DevtoolsInstance {
)
}
// Find the name of NetworkEventActor corresponding to request_id
// Create a new one if it does not exist, add it to the actor_requests hashmap
fn find_network_event_actor(&mut self, request_id: String, watcher_name: String) -> String {
/// Create a new NetworkEventActor for a given request ID and watcher name.
fn create_network_event_actor(&mut self, request_id: String, watcher_name: String) -> String {
let mut actors = self.actors.lock().unwrap();
match self.actor_requests.entry(request_id) {
Occupied(name) => {
//TODO: Delete from map like Firefox does?
name.into_mut().clone()
},
Vacant(entry) => {
let resource_id = self.next_resource_id;
self.next_resource_id += 1;
let actor_name = actors.new_name("netevent");
let actor = NetworkEventActor::new(actor_name.clone(), resource_id, watcher_name);
entry.insert(actor_name.clone());
actors.register(Box::new(actor));
actor_name
},
}
let resource_id = self.next_resource_id;
self.next_resource_id += 1;
let actor_name = actors.new_name("netevent");
let actor = NetworkEventActor::new(actor_name.clone(), resource_id, watcher_name);
self.actor_requests.insert(request_id, actor_name.clone());
actors.register(Box::new(actor));
actor_name
}
fn handle_create_source_actor(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) {
fn handle_create_source_actor(
&mut self,
script_sender: IpcSender<DevtoolScriptControlMsg>,
pipeline_id: PipelineId,
source_info: SourceInfo,
) {
let mut actors = self.actors.lock().unwrap();
let source_content = source_info
.content
.or_else(|| actors.inline_source_content(pipeline_id));
let source_actor = SourceActor::new_registered(
&mut actors,
pipeline_id,
source_info.url,
source_info.content,
source_info.content_type.unwrap(),
source_content,
source_info.content_type,
source_info.spidermonkey_id,
source_info.introduction_type,
script_sender,
);
let source_actor_name = source_actor.name.clone();
let source_form = source_actor.source_form();
@@ -613,6 +622,10 @@ impl DevtoolsInstance {
source_actor.content = Some(source_content.clone());
}
}
// Store the source content separately for any future source actors that get created *after* we finish parsing
// the HTML. For example, adding an `import` to an inline module script can delay it until after parsing.
actors.set_inline_source_content(pipeline_id, source_content);
}
}

View File

@@ -14,6 +14,9 @@ test = true
doctest = false
[features]
default = ["harfbuzz"]
harfbuzz = ["dep:harfbuzz-sys"]
harfrust = ["dep:harfrust"]
tracing = ["dep:tracing"]
[dependencies]
@@ -27,7 +30,8 @@ fnv = { workspace = true }
fonts_traits = { workspace = true }
fontsan = { git = "https://github.com/servo/fontsan" }
# FIXME (#34517): macOS only needs this when building libservo without `--features media-gstreamer`
harfbuzz-sys = { workspace = true, features = ["bundled"] }
harfbuzz-sys = { workspace = true, optional = true, features = ["bundled"] }
harfrust = { workspace = true, optional = true }
ipc-channel = { workspace = true }
itertools = { workspace = true }
libc = { workspace = true }
@@ -45,6 +49,7 @@ serde = { workspace = true }
servo_arc = { workspace = true }
servo_config = { path = "../config" }
servo_url = { path = "../url" }
skrifa = { workspace = true }
smallvec = { workspace = true }
stylo = { workspace = true }
stylo_atoms = { workspace = true }
@@ -72,8 +77,8 @@ fontconfig_sys = { package = "yeslogic-fontconfig-sys", version = "6" }
xml-rs = "0.8"
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.11.2"
truetype = { version = "0.47.3", features = ["ignore-invalid-language-ids"] }
dwrote = "0.11.4"
winapi = { workspace = true }
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(ohos_mock)'] }

View File

@@ -4,6 +4,8 @@
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, OnceLock};
use std::time::Instant;
@@ -16,6 +18,7 @@ use euclid::num::Zero;
use log::debug;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::RwLock;
use read_fonts::tables::os2::{Os2, SelectionFlags};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use style::computed_values::font_variant_caps;
@@ -25,14 +28,14 @@ use style::values::computed::font::{
};
use style::values::computed::{FontStretch, FontStyle, FontWeight};
use unicode_script::Script;
use webrender_api::{FontInstanceFlags, FontInstanceKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontVariation};
use crate::platform::font::{FontTable, PlatformFont};
pub use crate::platform::font_list::fallback_font_families;
use crate::{
ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, FontContext, FontData,
FontIdentifier, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods, GlyphData,
GlyphId, GlyphStore, LocalFontIdentifier, Shaper,
FontDataAndIndex, FontDataError, FontIdentifier, FontTemplateDescriptor, FontTemplateRef,
FontTemplateRefMethods, GlyphData, GlyphId, GlyphStore, LocalFontIdentifier, Shaper,
};
#[macro_export]
@@ -42,13 +45,15 @@ macro_rules! ot_tag {
};
}
pub const GPOS: u32 = ot_tag!('G', 'P', 'O', 'S');
pub const GSUB: u32 = ot_tag!('G', 'S', 'U', 'B');
pub const KERN: u32 = ot_tag!('k', 'e', 'r', 'n');
pub const SBIX: u32 = ot_tag!('s', 'b', 'i', 'x');
pub const CBDT: u32 = ot_tag!('C', 'B', 'D', 'T');
pub const COLR: u32 = ot_tag!('C', 'O', 'L', 'R');
pub const BASE: u32 = ot_tag!('B', 'A', 'S', 'E');
pub type OpenTypeTableTag = u32;
pub const GPOS: OpenTypeTableTag = ot_tag!('G', 'P', 'O', 'S');
pub const GSUB: OpenTypeTableTag = ot_tag!('G', 'S', 'U', 'B');
pub const KERN: OpenTypeTableTag = ot_tag!('k', 'e', 'r', 'n');
pub const SBIX: OpenTypeTableTag = ot_tag!('s', 'b', 'i', 'x');
pub const CBDT: OpenTypeTableTag = ot_tag!('C', 'B', 'D', 'T');
pub const COLR: OpenTypeTableTag = ot_tag!('C', 'O', 'L', 'R');
pub const BASE: OpenTypeTableTag = ot_tag!('B', 'A', 'S', 'E');
pub const LIGA: OpenTypeTableTag = ot_tag!('l', 'i', 'g', 'a');
pub const LAST_RESORT_GLYPH_ADVANCE: FractionalPixel = 10.0;
@@ -65,6 +70,7 @@ pub trait PlatformFontMethods: Sized {
fn new_from_template(
template: FontTemplateRef,
pt_size: Option<Au>,
variations: &[FontVariation],
data: &Option<FontData>,
) -> Result<PlatformFont, &'static str> {
let template = template.borrow();
@@ -72,13 +78,14 @@ pub trait PlatformFontMethods: Sized {
match font_identifier {
FontIdentifier::Local(font_identifier) => {
Self::new_from_local_font_identifier(font_identifier, pt_size)
Self::new_from_local_font_identifier(font_identifier, pt_size, variations)
},
FontIdentifier::Web(_) => Self::new_from_data(
font_identifier,
data.as_ref()
.expect("Should never create a web font without data."),
pt_size,
variations,
),
}
}
@@ -86,12 +93,14 @@ pub trait PlatformFontMethods: Sized {
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str>;
fn new_from_data(
font_identifier: FontIdentifier,
data: &FontData,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str>;
/// Get a [`FontTemplateDescriptor`] from a [`PlatformFont`]. This is used to get
@@ -108,6 +117,32 @@ pub trait PlatformFontMethods: Sized {
/// Get the necessary [`FontInstanceFlags`]` for this font.
fn webrender_font_instance_flags(&self) -> FontInstanceFlags;
/// Return all the variation values that the font was instantiated with.
fn variations(&self) -> &[FontVariation];
fn descriptor_from_os2_table(os2: &Os2) -> FontTemplateDescriptor {
let mut style = FontStyle::NORMAL;
if os2.fs_selection().contains(SelectionFlags::ITALIC) {
style = FontStyle::ITALIC;
}
let weight = FontWeight::from_float(os2.us_weight_class() as f32);
let stretch = match os2.us_width_class() {
1 => FontStretch::ULTRA_CONDENSED,
2 => FontStretch::EXTRA_CONDENSED,
3 => FontStretch::CONDENSED,
4 => FontStretch::SEMI_CONDENSED,
5 => FontStretch::NORMAL,
6 => FontStretch::SEMI_EXPANDED,
7 => FontStretch::EXPANDED,
8 => FontStretch::EXTRA_EXPANDED,
9 => FontStretch::ULTRA_EXPANDED,
_ => FontStretch::NORMAL,
};
FontTemplateDescriptor::new(weight, stretch, style)
}
}
// Used to abstract over the shaper's choice of fixed int representation.
@@ -191,18 +226,29 @@ pub struct FontDescriptor {
pub style: FontStyle,
pub variant: font_variant_caps::T,
pub pt_size: Au,
pub variation_settings: Vec<FontVariation>,
}
impl Eq for FontDescriptor {}
impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
fn from(style: &'a FontStyleStruct) -> Self {
let variation_settings = style
.clone_font_variation_settings()
.0
.into_iter()
.map(|setting| FontVariation {
tag: setting.tag.0,
value: setting.value,
})
.collect();
FontDescriptor {
weight: style.font_weight,
stretch: style.font_stretch,
style: style.font_style,
variant: style.font_variant_caps,
pt_size: Au::from_f32_px(style.font_size.computed_size().px()),
variation_settings,
}
}
}
@@ -233,8 +279,9 @@ pub struct Font {
pub metrics: FontMetrics,
pub descriptor: FontDescriptor,
/// The data for this font. This might be uninitialized for system fonts.
data: OnceLock<FontData>,
/// The data for this font. And the index of the font within the data (in case it's a TTC)
/// This might be uninitialized for system fonts.
data_and_index: OnceLock<FontDataAndIndex>,
shaper: OnceLock<Shaper>,
cached_shape_data: RwLock<CachedShapeData>,
@@ -279,8 +326,12 @@ impl Font {
data: Option<FontData>,
synthesized_small_caps: Option<FontRef>,
) -> Result<Font, &'static str> {
let handle =
PlatformFont::new_from_template(template.clone(), Some(descriptor.pt_size), &data)?;
let handle = PlatformFont::new_from_template(
template.clone(),
Some(descriptor.pt_size),
&descriptor.variation_settings,
&data,
)?;
let metrics = handle.metrics();
Ok(Font {
@@ -288,7 +339,9 @@ impl Font {
template,
metrics,
descriptor,
data: data.map(OnceLock::from).unwrap_or_default(),
data_and_index: data
.map(|data| OnceLock::from(FontDataAndIndex { data, index: 0 }))
.unwrap_or_default(),
shaper: OnceLock::new(),
cached_shape_data: Default::default(),
font_instance_key: Default::default(),
@@ -323,17 +376,24 @@ impl Font {
/// Return the data for this `Font`. Note that this is currently highly inefficient for system
/// fonts and should not be used except in legacy canvas code.
pub fn data(&self) -> &FontData {
self.data.get_or_init(|| {
let FontIdentifier::Local(local_font_identifier) = self.identifier() else {
unreachable!("All web fonts should already have initialized data");
};
FontData::from_bytes(
&local_font_identifier
.read_data_from_file()
.unwrap_or_default(),
)
})
pub fn font_data_and_index(&self) -> Result<&FontDataAndIndex, FontDataError> {
if let Some(data_and_index) = self.data_and_index.get() {
return Ok(data_and_index);
}
let FontIdentifier::Local(local_font_identifier) = self.identifier() else {
unreachable!("All web fonts should already have initialized data");
};
let Some(data_and_index) = local_font_identifier.font_data_and_index() else {
return Err(FontDataError::FailedToLoad);
};
let data_and_index = self.data_and_index.get_or_init(move || data_and_index);
Ok(data_and_index)
}
pub fn variations(&self) -> &[FontVariation] {
self.handle.variations()
}
}
@@ -425,9 +485,8 @@ impl Font {
}
fn shape_text_harfbuzz(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
let this = self as *const Font;
self.shaper
.get_or_init(|| Shaper::new(this))
.get_or_init(|| Shaper::new(self))
.shape_text(text, options, glyphs);
}
@@ -456,14 +515,11 @@ impl Font {
None => continue,
};
let mut advance = Au::from_f64_px(self.glyph_h_advance(glyph_id));
if character == ' ' {
// https://drafts.csswg.org/css-text-3/#word-spacing-property
advance += options.word_spacing;
}
if let Some(letter_spacing) = options.letter_spacing {
advance += letter_spacing;
}
let mut advance = advance_for_shaped_glyph(
Au::from_f64_px(self.glyph_h_advance(glyph_id)),
character,
options,
);
let offset = prev_glyph_id.map(|prev| {
let h_kerning = Au::from_f64_px(self.glyph_h_kerning(prev, glyph_id));
advance += h_kerning;
@@ -546,12 +602,19 @@ impl Font {
/// Get the [`FontBaseline`] for this font.
pub fn baseline(&self) -> Option<FontBaseline> {
let this = self as *const Font;
self.shaper.get_or_init(|| Shaper::new(this)).baseline()
self.shaper.get_or_init(|| Shaper::new(self)).baseline()
}
}
pub type FontRef = Arc<Font>;
#[derive(Clone, MallocSizeOf)]
pub struct FontRef(#[conditional_malloc_size_of] pub(crate) Arc<Font>);
impl Deref for FontRef {
type Target = Arc<Font>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// A `FontGroup` is a prioritised list of fonts for a given set of font styles. It is used by
/// `TextRun` to decide which font to render a character with. If none of the fonts listed in the
@@ -914,7 +977,10 @@ pub struct FontBaseline {
/// ];
/// let mapped_weight = apply_font_config_to_style_mapping(&mapping, weight as f64);
/// ```
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
not(target_env = "ohos")
))]
pub(crate) fn map_platform_values_to_style_values(mapping: &[(f64, f64)], value: f64) -> f64 {
if value < mapping[0].0 {
return mapping[0].1;
@@ -932,3 +998,25 @@ pub(crate) fn map_platform_values_to_style_values(mapping: &[(f64, f64)], value:
mapping[mapping.len() - 1].1
}
/// Computes the total advance for a glyph, taking `letter-spacing` and `word-spacing` into account.
pub(super) fn advance_for_shaped_glyph(
mut advance: Au,
character: char,
options: &ShapingOptions,
) -> Au {
if let Some(letter_spacing) = options.letter_spacing {
advance += letter_spacing;
};
// CSS 2.1 § 16.4 states that "word spacing affects each space (U+0020) and non-breaking
// space (U+00A0) left in the text after the white space processing rules have been
// applied. The effect of the property on other word-separator characters is undefined."
// We elect to only space the two required code points.
if character == ' ' || character == '\u{a0}' {
// https://drafts.csswg.org/css-text-3/#word-spacing-property
advance += options.word_spacing;
}
advance
}

View File

@@ -5,6 +5,7 @@
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::ops::Deref;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -14,12 +15,12 @@ use compositing_traits::CrossProcessCompositorApi;
use fnv::FnvHasher;
use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use log::{debug, trace};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::request::{Destination, Referrer, RequestBuilder};
use net_traits::{CoreResourceThread, FetchResponseMsg, ResourceThreads, fetch_async};
use parking_lot::{Mutex, RwLock};
use servo_arc::Arc as ServoArc;
use servo_config::pref;
use servo_url::ServoUrl;
use style::Atom;
use style::computed_values::font_variant_caps::T as FontVariantCaps;
@@ -32,7 +33,7 @@ use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetInDocument};
use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
use url::Url;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope,
@@ -45,12 +46,27 @@ use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontSe
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
pub type FontParameters = (FontKey, Au, Vec<FontVariation>);
#[derive(MallocSizeOf)]
struct FontGroupRef(#[conditional_malloc_size_of] Arc<RwLock<FontGroup>>);
impl Deref for FontGroupRef {
type Target = Arc<RwLock<FontGroup>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the system font service where
/// required.
#[derive(MallocSizeOf)]
pub struct FontContext {
#[conditional_malloc_size_of]
system_font_service_proxy: Arc<SystemFontServiceProxy>,
resource_threads: Mutex<CoreResourceThread>,
/// A sender that can send messages and receive replies from the compositor.
@@ -64,7 +80,7 @@ pub struct FontContext {
/// resolved [`FontGroup`] which contains information about all fonts that
/// can be selected with that style.
resolved_font_groups:
RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
RwLock<HashMap<FontGroupCacheKey, FontGroupRef, BuildHasherDefault<FnvHasher>>>,
web_fonts: CrossThreadFontStore,
@@ -74,7 +90,7 @@ pub struct FontContext {
/// A collection of WebRender [`FontInstanceKey`]s generated for the web fonts that
/// this [`FontContext`] controls.
webrender_font_instance_keys: RwLock<HashMap<(FontKey, Au), FontInstanceKey>>,
webrender_font_instance_keys: RwLock<HashMap<FontParameters, FontInstanceKey>>,
/// The data for each web font [`FontIdentifier`]. This data might be used by more than one
/// [`FontTemplate`] as each identifier refers to a URL.
@@ -83,26 +99,6 @@ pub struct FontContext {
have_removed_web_fonts: AtomicBool,
}
impl MallocSizeOf for FontContext {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let font_cache_size = self
.fonts
.read()
.iter()
.map(|(key, font)| {
key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops))
})
.sum::<usize>();
let font_group_cache_size = self
.resolved_font_groups
.read()
.iter()
.map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops))
.sum::<usize>();
font_cache_size + font_group_cache_size
}
}
impl FontContext {
pub fn new(
system_font_service_proxy: Arc<SystemFontServiceProxy>,
@@ -116,7 +112,7 @@ impl FontContext {
compositor_api: Mutex::new(compositor_api),
fonts: Default::default(),
resolved_font_groups: Default::default(),
web_fonts: Arc::new(RwLock::default()),
web_fonts: Default::default(),
webrender_font_keys: RwLock::default(),
webrender_font_instance_keys: RwLock::default(),
have_removed_web_fonts: AtomicBool::new(false),
@@ -152,7 +148,7 @@ impl FontContext {
) -> Arc<RwLock<FontGroup>> {
let cache_key = FontGroupCacheKey { size, style };
if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) {
return font_group.clone();
return font_group.0.clone();
}
let mut descriptor = FontDescriptor::from(&*cache_key.style);
@@ -161,7 +157,7 @@ impl FontContext {
let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style, descriptor)));
self.resolved_font_groups
.write()
.insert(cache_key, font_group.clone());
.insert(cache_key, FontGroupRef(font_group.clone()));
font_group
}
@@ -275,12 +271,12 @@ impl FontContext {
font_descriptor: FontDescriptor,
synthesized_small_caps: Option<FontRef>,
) -> Result<FontRef, &'static str> {
Ok(Arc::new(Font::new(
Ok(FontRef(Arc::new(Font::new(
font_template.clone(),
font_descriptor.clone(),
font_descriptor,
self.get_font_data(&font_template.identifier()),
synthesized_small_caps,
)?))
)?)))
}
pub(crate) fn create_font_instance_key(&self, font: &Font) -> FontInstanceKey {
@@ -289,11 +285,13 @@ impl FontContext {
font.template.identifier(),
font.descriptor.pt_size,
font.webrender_font_instance_flags(),
font.variations().to_owned(),
),
FontIdentifier::Web(_) => self.create_web_font_instance(
font.template.clone(),
font.descriptor.pt_size,
font.webrender_font_instance_flags(),
font.variations().to_owned(),
),
}
}
@@ -303,6 +301,7 @@ impl FontContext {
font_template: FontTemplateRef,
pt_size: Au,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey {
let identifier = font_template.identifier().clone();
let font_data = self
@@ -322,10 +321,10 @@ impl FontContext {
font_key
});
let key = *self
*self
.webrender_font_instance_keys
.write()
.entry((font_key, pt_size))
.entry((font_key, pt_size, variations.clone()))
.or_insert_with(|| {
let font_instance_key = self.system_font_service_proxy.generate_font_instance_key();
self.compositor_api.lock().add_font_instance(
@@ -333,10 +332,10 @@ impl FontContext {
font_key,
pt_size.to_f32_px(),
flags,
variations,
);
font_instance_key
});
key
})
}
fn invalidate_font_groups_after_web_font_load(&self) {
@@ -366,10 +365,16 @@ impl FontContext {
}
if let FontFaceSourceFormat::String(string) = format_hint {
return string == "truetype" ||
string == "opentype" ||
string == "woff" ||
string == "woff2";
if string == "truetype" || string == "opentype" || string == "woff" || string == "woff2"
{
return true;
}
return pref!(layout_variable_fonts_enabled) &&
(string == "truetype-variations" ||
string == "opentype-variations" ||
string == "woff-variations" ||
string == "woff2-variations");
}
false
@@ -619,7 +624,7 @@ impl FontContextWebFontMethods for Arc<FontContext> {
});
let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
webrender_font_instance_keys.retain(|(font_key, _), instance_key| {
webrender_font_instance_keys.retain(|(font_key, _, _), instance_key| {
if removed_keys.contains(font_key) {
removed_instance_keys.insert(*instance_key);
false
@@ -864,11 +869,11 @@ impl RemoteWebFontDownloader {
let url: ServoUrl = self.url.clone().into();
let identifier = FontIdentifier::Web(url.clone());
let Ok(handle) = PlatformFont::new_from_data(identifier, &font_data, None) else {
let Ok(handle) = PlatformFont::new_from_data(identifier, &font_data, None, &[]) else {
return false;
};
let state = self.take_state();
let mut descriptor = handle.descriptor();
descriptor
.override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors);

View File

@@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use log::warn;
@@ -21,7 +22,16 @@ pub struct FontStore {
web_fonts_loading_for_stylesheets: Vec<(DocumentStyleSheet, usize)>,
web_fonts_loading_for_script: usize,
}
pub(crate) type CrossThreadFontStore = Arc<RwLock<FontStore>>;
#[derive(Default, MallocSizeOf)]
pub(crate) struct CrossThreadFontStore(#[conditional_malloc_size_of] Arc<RwLock<FontStore>>);
impl Deref for CrossThreadFontStore {
type Target = Arc<RwLock<FontStore>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl FontStore {
pub(crate) fn clear(&mut self) {

View File

@@ -315,7 +315,7 @@ impl<'a> DetailedGlyphStore {
// This struct is used by GlyphStore clients to provide new glyph data.
// It should be allocated on the stack and passed by reference to GlyphStore.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub struct GlyphData {
id: GlyphId,
advance: Au,
@@ -606,7 +606,7 @@ impl GlyphStore {
pub fn iter_glyphs_for_byte_range(
&self,
range: &Range<ByteIndex>,
) -> impl Iterator<Item = GlyphInfo> + use<'_> {
) -> impl Iterator<Item = GlyphInfo<'_>> + use<'_> {
if range.begin() >= self.len() {
panic!("iter_glyphs_for_range: range.begin beyond length!");
}

View File

@@ -11,44 +11,20 @@ mod font_template;
mod glyph;
#[allow(unsafe_code)]
pub mod platform;
mod shaper;
mod shapers;
mod system_font_service;
use std::sync::Arc;
pub use font::*;
pub use font_context::*;
pub use font_store::*;
pub use font_template::*;
pub use fonts_traits::*;
pub use glyph::*;
use ipc_channel::ipc::IpcSharedMemory;
pub use platform::LocalFontIdentifier;
pub use shaper::*;
pub use shapers::*;
pub use system_font_service::*;
use unicode_properties::{EmojiStatus, UnicodeEmoji, emoji};
/// A data structure to store data for fonts. Data is stored internally in an
/// [`IpcSharedMemory`] handle, so that it can be send without serialization
/// across IPC channels.
#[derive(Clone)]
pub struct FontData(pub(crate) Arc<IpcSharedMemory>);
impl FontData {
pub fn from_bytes(bytes: &[u8]) -> Self {
Self(Arc::new(IpcSharedMemory::from_bytes(bytes)))
}
pub(crate) fn as_ipc_shared_memory(&self) -> Arc<IpcSharedMemory> {
self.0.clone()
}
}
impl AsRef<[u8]> for FontData {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
/// Whether or not font fallback selection prefers the emoji or text representation
/// of a character. If `None` then either presentation is acceptable.
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -106,6 +82,7 @@ impl FallbackFontSelectionOptions {
}
}
#[cfg(feature = "harfbuzz")]
pub(crate) fn float_to_fixed(before: usize, f: f64) -> i32 {
((1i32 << before) as f64 * f) as i32
}

View File

@@ -4,28 +4,22 @@
use std::ffi::CString;
use std::fs::File;
use std::os::raw::c_long;
use std::{mem, ptr};
use app_units::Au;
use euclid::default::{Point2D, Rect, Size2D};
use freetype_sys::{
FT_Byte, FT_Done_Face, FT_Error, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES,
FT_FACE_FLAG_SCALABLE, FT_Face, FT_Get_Char_Index, FT_Get_Kerning, FT_GlyphSlot, FT_Int32,
FT_KERNING_DEFAULT, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Long,
FT_New_Face, FT_New_Memory_Face, FT_Pos, FT_Select_Size, FT_Set_Char_Size, FT_Size_Metrics,
FT_SizeRec, FT_UInt, FT_ULong, FT_Vector,
FT_F26Dot6, FT_Get_Char_Index, FT_Get_Kerning, FT_GlyphSlot, FT_KERNING_DEFAULT,
FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Size_Metrics, FT_SizeRec, FT_UInt,
FT_ULong, FT_Vector,
};
use log::debug;
use memmap2::Mmap;
use parking_lot::ReentrantMutex;
use read_fonts::tables::os2::SelectionFlags;
use read_fonts::types::Tag;
use read_fonts::{FontRef, ReadError, TableProvider};
use servo_arc::Arc;
use style::Zero;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight;
use style::values::computed::font::FontStyle;
use webrender_api::FontInstanceFlags;
use webrender_api::{FontInstanceFlags, FontVariation};
use super::LocalFontIdentifier;
use super::library_handle::FreeTypeLibraryHandle;
@@ -35,13 +29,9 @@ use crate::font::{
};
use crate::font_template::FontTemplateDescriptor;
use crate::glyph::GlyphId;
use crate::platform::freetype::freetype_face::FreeTypeFace;
use crate::system_font_service::FontIdentifier;
// This constant is not present in the freetype
// bindings due to bindgen not handling the way
// the macro is defined.
const FT_LOAD_TARGET_LIGHT: FT_UInt = 1 << 16;
/// Convert FreeType-style 26.6 fixed point to an [`f64`].
fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 {
fixed as f64 / 64.0
@@ -49,70 +39,44 @@ fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 {
#[derive(Debug)]
pub struct FontTable {
buffer: Vec<u8>,
data: FreeTypeFaceTableProviderData,
tag: Tag,
}
impl FontTableMethods for FontTable {
fn buffer(&self) -> &[u8] {
&self.buffer
let font_ref = self.data.font_ref().expect("Font checked before creating");
let table_data = font_ref
.table_data(self.tag)
.expect("Table existence checked before creating");
table_data.as_bytes()
}
}
#[derive(Debug)]
#[allow(unused)]
pub struct PlatformFont {
face: ReentrantMutex<FT_Face>,
face: ReentrantMutex<FreeTypeFace>,
requested_face_size: Au,
actual_face_size: Au,
variations: Vec<FontVariation>,
/// A member that allows using `skrifa` to read values from this font.
table_provider_data: FreeTypeFaceTableProviderData,
}
// FT_Face can be used in multiple threads, but from only one thread at a time.
// It's protected with a ReentrantMutex for PlatformFont.
// See https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_face.
unsafe impl Sync for PlatformFont {}
unsafe impl Send for PlatformFont {}
impl Drop for PlatformFont {
fn drop(&mut self) {
let face = self.face.lock();
assert!(!face.is_null());
unsafe {
// The FreeType documentation says that both `FT_New_Face` and `FT_Done_Face`
// should be protected by a mutex.
// See https://freetype.org/freetype2/docs/reference/ft2-library_setup.html.
let _guard = FreeTypeLibraryHandle::get().lock();
if FT_Done_Face(*face) != 0 {
panic!("FT_Done_Face failed");
}
}
}
}
impl PlatformFontMethods for PlatformFont {
fn new_from_data(
_font_identifier: FontIdentifier,
font_data: &FontData,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let library = FreeTypeLibraryHandle::get().lock();
let data: &[u8] = font_data.as_ref();
let mut face: FT_Face = ptr::null_mut();
let result = unsafe {
FT_New_Memory_Face(
library.freetype_library,
data.as_ptr(),
data.len() as FT_Long,
0, /* face_index */
&mut face,
)
};
let face = FreeTypeFace::new_from_memory(&library, data)?;
if 0 != result || face.is_null() {
return Err("Could not create FreeType face");
}
let normalized_variations = face.set_variations_for_font(variations, &library)?;
let (requested_face_size, actual_face_size) = match requested_size {
Some(requested_size) => (requested_size, face.set_size(requested_size)?),
@@ -124,29 +88,21 @@ impl PlatformFontMethods for PlatformFont {
requested_face_size,
actual_face_size,
table_provider_data: FreeTypeFaceTableProviderData::Web(font_data.clone()),
variations: normalized_variations,
})
}
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let mut face: FT_Face = ptr::null_mut();
let library = FreeTypeLibraryHandle::get().lock();
let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!");
let result = unsafe {
FT_New_Face(
library.freetype_library,
filename.as_ptr(),
font_identifier.index() as FT_Long,
&mut face,
)
};
let face = FreeTypeFace::new_from_file(&library, &filename, font_identifier.index())?;
if 0 != result || face.is_null() {
return Err("Could not create FreeType face");
}
let normalized_variations = face.set_variations_for_font(variations, &library)?;
let (requested_face_size, actual_face_size) = match requested_size {
Some(requested_size) => (requested_size, face.set_size(requested_size)?),
@@ -164,9 +120,10 @@ impl PlatformFontMethods for PlatformFont {
requested_face_size,
actual_face_size,
table_provider_data: FreeTypeFaceTableProviderData::Local(
memory_mapped_font_data,
Arc::new(memory_mapped_font_data),
font_identifier.index(),
),
variations: normalized_variations,
})
}
@@ -174,39 +131,17 @@ impl PlatformFontMethods for PlatformFont {
let Ok(font_ref) = self.table_provider_data.font_ref() else {
return FontTemplateDescriptor::default();
};
let Ok(os2) = font_ref.os2() else {
return FontTemplateDescriptor::default();
};
let mut style = FontStyle::NORMAL;
if os2.fs_selection().contains(SelectionFlags::ITALIC) {
style = FontStyle::ITALIC;
}
let weight = FontWeight::from_float(os2.us_weight_class() as f32);
let stretch = match os2.us_width_class() {
1 => FontStretch::ULTRA_CONDENSED,
2 => FontStretch::EXTRA_CONDENSED,
3 => FontStretch::CONDENSED,
4 => FontStretch::SEMI_CONDENSED,
5 => FontStretch::NORMAL,
6 => FontStretch::SEMI_EXPANDED,
7 => FontStretch::EXPANDED,
8 => FontStretch::EXTRA_EXPANDED,
9 => FontStretch::ULTRA_EXPANDED,
_ => FontStretch::NORMAL,
};
FontTemplateDescriptor::new(weight, stretch, style)
Self::descriptor_from_os2_table(&os2)
}
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
let face = self.face.lock();
assert!(!face.is_null());
unsafe {
let idx = FT_Get_Char_Index(*face, codepoint as FT_ULong);
let idx = FT_Get_Char_Index(face.as_ptr(), codepoint as FT_ULong);
if idx != 0 as FT_UInt {
Some(idx as GlyphId)
} else {
@@ -221,12 +156,11 @@ impl PlatformFontMethods for PlatformFont {
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
let face = self.face.lock();
assert!(!face.is_null());
let mut delta = FT_Vector { x: 0, y: 0 };
unsafe {
FT_Get_Kerning(
*face,
face.as_ptr(),
first_glyph,
second_glyph,
FT_KERNING_DEFAULT,
@@ -238,16 +172,15 @@ impl PlatformFontMethods for PlatformFont {
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
let face = self.face.lock();
assert!(!face.is_null());
let load_flags = face.glyph_load_flags();
let result = unsafe { FT_Load_Glyph(*face, glyph as FT_UInt, load_flags) };
let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph as FT_UInt, load_flags) };
if 0 != result {
debug!("Unable to load glyph {}. reason: {:?}", glyph, result);
return None;
}
let void_glyph = unsafe { (**face).glyph };
let void_glyph = face.as_ref().glyph;
let slot: FT_GlyphSlot = void_glyph;
assert!(!slot.is_null());
@@ -256,12 +189,11 @@ impl PlatformFontMethods for PlatformFont {
}
fn metrics(&self) -> FontMetrics {
let face_ptr = *self.face.lock();
let face = unsafe { &*face_ptr };
let face = self.face.lock();
let font_ref = self.table_provider_data.font_ref();
// face.size is a *c_void in the bindings, presumably to avoid recursive structural types
let freetype_size: &FT_SizeRec = unsafe { mem::transmute(&(*face.size)) };
let freetype_size: &FT_SizeRec = unsafe { &*face.as_ref().size };
let freetype_metrics: &FT_Size_Metrics = &(freetype_size).metrics;
let mut max_advance;
@@ -270,7 +202,7 @@ impl PlatformFontMethods for PlatformFont {
let mut line_height;
let mut y_scale = 0.0;
let mut em_height;
if face_ptr.scalable() {
if face.scalable() {
// Prefer FT_Size_Metrics::y_scale to y_ppem as y_ppem does not have subpixel accuracy.
//
// FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its (fractional) value is a
@@ -280,11 +212,11 @@ impl PlatformFontMethods for PlatformFont {
// This converts the value to a float without losing precision.
y_scale = freetype_metrics.y_scale as f64 / 65535.0 / 64.0;
max_advance = (face.max_advance_width as f64) * y_scale;
max_ascent = (face.ascender as f64) * y_scale;
max_descent = -(face.descender as f64) * y_scale;
line_height = (face.height as f64) * y_scale;
em_height = (face.units_per_EM as f64) * y_scale;
max_advance = (face.as_ref().max_advance_width as f64) * y_scale;
max_ascent = (face.as_ref().ascender as f64) * y_scale;
max_descent = -(face.as_ref().descender as f64) * y_scale;
line_height = (face.as_ref().height as f64) * y_scale;
em_height = (face.as_ref().units_per_EM as f64) * y_scale;
} else {
max_advance = fixed_26_dot_6_to_float(freetype_metrics.max_advance);
max_ascent = fixed_26_dot_6_to_float(freetype_metrics.ascender);
@@ -300,7 +232,7 @@ impl PlatformFontMethods for PlatformFont {
// Bug 1267909 - Even if the font is not explicitly scalable, if the face has color
// bitmaps, it should be treated as scalable and scaled to the desired size. Metrics
// based on y_ppem need to be rescaled for the adjusted size.
if face_ptr.color() {
if face.color() {
em_height = self.requested_face_size.to_f64_px();
let adjust_scale = em_height / (freetype_metrics.y_ppem as f64);
max_advance *= adjust_scale;
@@ -320,8 +252,8 @@ impl PlatformFontMethods for PlatformFont {
// Convert using a formula similar to what CTFont returns for consistency.
let leading = line_height - (max_ascent + max_descent);
let underline_size = face.underline_thickness as f64 * y_scale;
let underline_offset = face.underline_position as f64 * y_scale + 0.5;
let underline_size = face.as_ref().underline_thickness as f64 * y_scale;
let underline_offset = face.as_ref().underline_position as f64 * y_scale + 0.5;
// The default values for strikeout size and offset. Use OpenType spec's suggested position
// for Roman font as the default for offset.
@@ -393,36 +325,26 @@ impl PlatformFontMethods for PlatformFont {
}
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
let face = self.face.lock();
let tag = tag as FT_ULong;
unsafe {
// Get the length
let mut len = 0;
if 0 != FT_Load_Sfnt_Table(*face, tag, 0, ptr::null_mut(), &mut len) {
return None;
}
// Get the bytes
let mut buf = vec![0u8; len as usize];
if 0 != FT_Load_Sfnt_Table(*face, tag, 0, buf.as_mut_ptr(), &mut len) {
return None;
}
Some(FontTable { buffer: buf })
}
let tag = Tag::from_u32(tag);
let font_ref = self.table_provider_data.font_ref().ok()?;
let _table_data = font_ref.table_data(tag)?;
Some(FontTable {
data: self.table_provider_data.clone(),
tag,
})
}
fn typographic_bounds(&self, glyph_id: GlyphId) -> Rect<f32> {
let face = self.face.lock();
assert!(!face.is_null());
let load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
let result = unsafe { FT_Load_Glyph(*face, glyph_id as FT_UInt, load_flags) };
let result = unsafe { FT_Load_Glyph(face.as_ptr(), glyph_id as FT_UInt, load_flags) };
if 0 != result {
debug!("Unable to load glyph {}. reason: {:?}", glyph_id, result);
return Rect::default();
}
let metrics = unsafe { &(*(**face).glyph).metrics };
let metrics = unsafe { &(*face.as_ref().glyph).metrics };
Rect::new(
Point2D::new(
@@ -439,6 +361,10 @@ impl PlatformFontMethods for PlatformFont {
// loading bitmaps. There's no harm to always passing it.
FontInstanceFlags::EMBEDDED_BITMAPS
}
fn variations(&self) -> &[FontVariation] {
&self.variations
}
}
impl PlatformFont {
@@ -450,104 +376,16 @@ impl PlatformFont {
}
}
trait FreeTypeFaceHelpers {
fn scalable(self) -> bool;
fn color(self) -> bool;
fn set_size(self, pt_size: Au) -> Result<Au, &'static str>;
fn glyph_load_flags(self) -> FT_Int32;
}
impl FreeTypeFaceHelpers for FT_Face {
fn scalable(self) -> bool {
unsafe { (*self).face_flags & FT_FACE_FLAG_SCALABLE as c_long != 0 }
}
fn color(self) -> bool {
unsafe { (*self).face_flags & FT_FACE_FLAG_COLOR as c_long != 0 }
}
fn set_size(self, requested_size: Au) -> Result<Au, &'static str> {
if self.scalable() {
let size_in_fixed_point = (requested_size.to_f64_px() * 64.0 + 0.5) as FT_F26Dot6;
let result = unsafe { FT_Set_Char_Size(self, size_in_fixed_point, 0, 72, 72) };
if 0 != result {
return Err("FT_Set_Char_Size failed");
}
return Ok(requested_size);
}
let requested_size = (requested_size.to_f64_px() * 64.0) as FT_Pos;
let get_size_at_index = |index| unsafe {
(
(*(*self).available_sizes.offset(index as isize)).x_ppem,
(*(*self).available_sizes.offset(index as isize)).y_ppem,
)
};
let mut best_index = 0;
let mut best_size = get_size_at_index(0);
let mut best_dist = best_size.1 - requested_size;
for strike_index in 1..unsafe { (*self).num_fixed_sizes } {
let new_scale = get_size_at_index(strike_index);
let new_distance = new_scale.1 - requested_size;
// Distance is positive if strike is larger than desired size,
// or negative if smaller. If previously a found smaller strike,
// then prefer a larger strike. Otherwise, minimize distance.
if (best_dist < 0 && new_distance >= best_dist) || new_distance.abs() <= best_dist {
best_dist = new_distance;
best_size = new_scale;
best_index = strike_index;
}
}
if 0 == unsafe { FT_Select_Size(self, best_index) } {
Ok(Au::from_f64_px(best_size.1 as f64 / 64.0))
} else {
Err("FT_Select_Size failed")
}
}
fn glyph_load_flags(self) -> FT_Int32 {
let mut load_flags = FT_LOAD_DEFAULT;
// Default to slight hinting, which is what most
// Linux distros use by default, and is a better
// default than no hinting.
// TODO(gw): Make this configurable.
load_flags |= FT_LOAD_TARGET_LIGHT as i32;
let face_flags = unsafe { (*self).face_flags };
if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
// We only set FT_LOAD_COLOR if there are bitmap strikes; COLR (color-layer) fonts
// will be handled internally in Servo. In that case WebRender will just be asked to
// paint individual layers.
load_flags |= FT_LOAD_COLOR;
}
load_flags as FT_Int32
}
}
unsafe extern "C" {
fn FT_Load_Sfnt_Table(
face: FT_Face,
tag: FT_ULong,
offset: FT_Long,
buffer: *mut FT_Byte,
length: *mut FT_ULong,
) -> FT_Error;
}
#[derive(Clone)]
enum FreeTypeFaceTableProviderData {
Web(FontData),
Local(Mmap, u32),
Local(Arc<Mmap>, u32),
}
impl FreeTypeFaceTableProviderData {
fn font_ref(&self) -> Result<FontRef<'_>, ReadError> {
match self {
Self::Web(ipc_shared_memory) => FontRef::new(&ipc_shared_memory.0),
Self::Web(ipc_shared_memory) => FontRef::new(ipc_shared_memory.as_ref()),
Self::Local(mmap, index) => FontRef::from_index(mmap, *index),
}
}

View File

@@ -0,0 +1,254 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::ffi::{CStr, c_long};
use std::ptr;
use app_units::Au;
use freetype_sys::{
FT_Done_Face, FT_Done_MM_Var, FT_F26Dot6, FT_FACE_FLAG_COLOR, FT_FACE_FLAG_FIXED_SIZES,
FT_FACE_FLAG_SCALABLE, FT_Face, FT_FaceRec, FT_Fixed, FT_Get_MM_Var, FT_HAS_MULTIPLE_MASTERS,
FT_Int32, FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_Long, FT_MM_Var, FT_New_Face, FT_New_Memory_Face,
FT_Pos, FT_Select_Size, FT_Set_Char_Size, FT_Set_Var_Design_Coordinates, FT_UInt,
FTErrorMethods,
};
use webrender_api::FontVariation;
use crate::platform::freetype::library_handle::FreeTypeLibraryHandle;
// This constant is not present in the freetype
// bindings due to bindgen not handling the way
// the macro is defined.
const FT_LOAD_TARGET_LIGHT: FT_UInt = 1 << 16;
/// A safe wrapper around [FT_Face].
#[derive(Debug)]
pub(crate) struct FreeTypeFace {
/// ## Safety Invariant
/// The pointer must have been returned from [FT_New_Face] or [FT_New_Memory_Face]
/// and must not be freed before `FreetypeFace::drop` is called.
face: ptr::NonNull<FT_FaceRec>,
}
impl FreeTypeFace {
pub(crate) fn new_from_memory(
library: &FreeTypeLibraryHandle,
data: &[u8],
) -> Result<Self, &'static str> {
let mut face = ptr::null_mut();
let result = unsafe {
FT_New_Memory_Face(
library.freetype_library,
data.as_ptr(),
data.len() as FT_Long,
0,
&mut face,
)
};
if 0 != result {
return Err("Could not create FreeType face");
}
let Some(face) = ptr::NonNull::new(face) else {
return Err("Could not create FreeType face");
};
Ok(Self { face })
}
pub(crate) fn new_from_file(
library: &FreeTypeLibraryHandle,
filename: &CStr,
index: u32,
) -> Result<Self, &'static str> {
let mut face = ptr::null_mut();
let result = unsafe {
FT_New_Face(
library.freetype_library,
filename.as_ptr(),
index as FT_Long,
&mut face,
)
};
if 0 != result {
return Err("Could not create FreeType face");
}
let Some(face) = ptr::NonNull::new(face) else {
return Err("Could not create FreeType face");
};
Ok(Self { face })
}
pub(crate) fn as_ref(&self) -> &FT_FaceRec {
unsafe { self.face.as_ref() }
}
pub(crate) fn as_ptr(&self) -> FT_Face {
self.face.as_ptr()
}
/// Return true iff the font face flags contain [FT_FACE_FLAG_SCALABLE].
pub(crate) fn scalable(&self) -> bool {
self.as_ref().face_flags & FT_FACE_FLAG_SCALABLE as c_long != 0
}
/// Return true iff the font face flags contain [FT_FACE_FLAG_COLOR].
pub(crate) fn color(&self) -> bool {
self.as_ref().face_flags & FT_FACE_FLAG_COLOR as c_long != 0
}
/// Scale the font to the given size if it is scalable, or select the closest
/// available size if it is not, preferring larger sizes over smaller ones.
///
/// Returns the selected size on success and a error message on failure
pub(crate) fn set_size(&self, requested_size: Au) -> Result<Au, &'static str> {
if self.scalable() {
let size_in_fixed_point = (requested_size.to_f64_px() * 64.0 + 0.5) as FT_F26Dot6;
let result =
unsafe { FT_Set_Char_Size(self.face.as_ptr(), size_in_fixed_point, 0, 72, 72) };
if 0 != result {
return Err("FT_Set_Char_Size failed");
}
return Ok(requested_size);
}
let requested_size = (requested_size.to_f64_px() * 64.0) as FT_Pos;
let get_size_at_index = |index| unsafe {
(
(*self.as_ref().available_sizes.offset(index as isize)).x_ppem,
(*self.as_ref().available_sizes.offset(index as isize)).y_ppem,
)
};
let mut best_index = 0;
let mut best_size = get_size_at_index(0);
let mut best_dist = best_size.1 - requested_size;
for strike_index in 1..self.as_ref().num_fixed_sizes {
let new_scale = get_size_at_index(strike_index);
let new_distance = new_scale.1 - requested_size;
// Distance is positive if strike is larger than desired size,
// or negative if smaller. If previously a found smaller strike,
// then prefer a larger strike. Otherwise, minimize distance.
if (best_dist < 0 && new_distance >= best_dist) || new_distance.abs() <= best_dist {
best_dist = new_distance;
best_size = new_scale;
best_index = strike_index;
}
}
if 0 == unsafe { FT_Select_Size(self.face.as_ptr(), best_index) } {
Ok(Au::from_f64_px(best_size.1 as f64 / 64.0))
} else {
Err("FT_Select_Size failed")
}
}
/// Select a reasonable set of glyph loading flags for the font.
pub(crate) fn glyph_load_flags(&self) -> FT_Int32 {
let mut load_flags = FT_LOAD_DEFAULT;
// Default to slight hinting, which is what most
// Linux distros use by default, and is a better
// default than no hinting.
// TODO(gw): Make this configurable.
load_flags |= FT_LOAD_TARGET_LIGHT as i32;
let face_flags = self.as_ref().face_flags;
if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
// We only set FT_LOAD_COLOR if there are bitmap strikes; COLR (color-layer) fonts
// will be handled internally in Servo. In that case WebRender will just be asked to
// paint individual layers.
load_flags |= FT_LOAD_COLOR;
}
load_flags as FT_Int32
}
/// Applies to provided variations to the font face.
///
/// Returns the normalized font variations, which are clamped
/// to fit within the range of their respective axis. Variation
/// values for nonexistent axes are not included.
pub(crate) fn set_variations_for_font(
&self,
variations: &[FontVariation],
library: &FreeTypeLibraryHandle,
) -> Result<Vec<FontVariation>, &'static str> {
if !FT_HAS_MULTIPLE_MASTERS(self.as_ptr()) ||
variations.is_empty() ||
!servo_config::pref!(layout_variable_fonts_enabled)
{
// Nothing to do
return Ok(vec![]);
}
// Query variation axis of font
let mut mm_var: *mut FT_MM_Var = ptr::null_mut();
let result = unsafe { FT_Get_MM_Var(self.as_ptr(), &mut mm_var as *mut _) };
if !result.succeeded() {
return Err("Failed to query font variations");
}
// Prepare values for each axis. These are either the provided values (if any) or the default
// ones for the axis.
let num_axis = unsafe { (*mm_var).num_axis } as usize;
let mut normalized_axis_values = Vec::with_capacity(variations.len());
let mut coords = vec![0; num_axis];
for (index, coord) in coords.iter_mut().enumerate() {
let axis_data = unsafe { &*(*mm_var).axis.add(index) };
let Some(variation) = variations
.iter()
.find(|variation| variation.tag == axis_data.tag as u32)
else {
*coord = axis_data.def;
continue;
};
// Freetype uses a 16.16 fixed point format for variation values
let shift_factor = 16.0_f32.exp2();
let min_value = axis_data.minimum as f32 / shift_factor;
let max_value = axis_data.maximum as f32 / shift_factor;
normalized_axis_values.push(FontVariation {
tag: variation.tag,
value: variation.value.min(max_value).max(min_value),
});
*coord = (variation.value * shift_factor) as FT_Fixed;
}
// Free the MM_Var structure
unsafe {
FT_Done_MM_Var(library.freetype_library, mm_var);
}
// Set the values for each variation axis
let result = unsafe {
FT_Set_Var_Design_Coordinates(self.as_ptr(), coords.len() as u32, coords.as_ptr())
};
if !result.succeeded() {
return Err("Could not set variations for font face");
}
Ok(normalized_axis_values)
}
}
/// FT_Face can be used in multiple threads, but from only one thread at a time.
/// See <https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_face>.
unsafe impl Send for FreeTypeFace {}
impl Drop for FreeTypeFace {
fn drop(&mut self) {
// The FreeType documentation says that both `FT_New_Face` and `FT_Done_Face`
// should be protected by a mutex.
// See https://freetype.org/freetype2/docs/reference/ft2-library_setup.html.
let _guard = FreeTypeLibraryHandle::get().lock();
if unsafe { FT_Done_Face(self.face.as_ptr()) } != 0 {
panic!("FT_Done_Face failed");
}
}
}

View File

@@ -11,12 +11,12 @@ use freetype_sys::{
FT_Add_Default_Modules, FT_Done_Library, FT_Library, FT_Memory, FT_MemoryRec, FT_New_Library,
};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use parking_lot::Mutex;
use parking_lot::ReentrantMutex;
use servo_allocator::libc_compat::{free, malloc, realloc};
use servo_allocator::usable_size;
static FREETYPE_MEMORY_USAGE: AtomicUsize = AtomicUsize::new(0);
static FREETYPE_LIBRARY_HANDLE: OnceLock<Mutex<FreeTypeLibraryHandle>> = OnceLock::new();
static FREETYPE_LIBRARY_HANDLE: OnceLock<ReentrantMutex<FreeTypeLibraryHandle>> = OnceLock::new();
extern "C" fn ft_alloc(_: FT_Memory, req_size: c_long) -> *mut c_void {
unsafe {
@@ -90,7 +90,7 @@ impl FreeTypeLibraryHandle {
/// > also, as long as a mutex lock is used around FT_New_Face and FT_Done_Face.
///
/// See <https://freetype.org/freetype2/docs/reference/ft2-library_setup.html>.
pub(crate) fn get() -> &'static Mutex<FreeTypeLibraryHandle> {
pub(crate) fn get() -> &'static ReentrantMutex<FreeTypeLibraryHandle> {
FREETYPE_LIBRARY_HANDLE.get_or_init(|| {
let freetype_memory = Box::into_raw(Box::new(FT_MemoryRec {
user: ptr::null_mut(),
@@ -105,7 +105,7 @@ impl FreeTypeLibraryHandle {
panic!("Unable to initialize FreeType library");
}
FT_Add_Default_Modules(freetype_library);
Mutex::new(FreeTypeLibraryHandle {
ReentrantMutex::new(FreeTypeLibraryHandle {
freetype_library,
freetype_memory,
})

View File

@@ -13,7 +13,10 @@ use serde::{Deserialize, Serialize};
use style::Atom;
use webrender_api::NativeFontHandle;
use crate::{FontData, FontDataAndIndex};
pub mod font;
mod freetype_face;
#[cfg(all(target_os = "linux", not(target_env = "ohos"), not(ohos_mock)))]
pub mod font_list;
@@ -56,9 +59,14 @@ impl LocalFontIdentifier {
}
}
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
let file = File::open(Path::new(&*self.path)).ok()?;
let mmap = unsafe { Mmap::map(&file).ok()? };
Some(mmap[..].to_vec())
let data = FontData::from_bytes(&mmap);
Some(FontDataAndIndex {
data,
index: self.variation_index as u32,
})
}
}

View File

@@ -7,16 +7,17 @@ use std::sync::{Arc, OnceLock};
use app_units::Au;
use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use core_foundation::data::CFData;
use core_foundation::number::CFNumber;
use core_foundation::string::{CFString, CFStringRef};
use core_foundation::url::{CFURL, kCFURLPOSIXPathStyle};
use core_graphics::data_provider::CGDataProvider;
use core_graphics::display::CFDictionary;
use core_graphics::font::CGFont;
use core_text::font::CTFont;
use core_text::font_descriptor::kCTFontURLAttribute;
use core_text::font_descriptor::{kCTFontURLAttribute, kCTFontVariationAttribute};
use parking_lot::RwLock;
use webrender_api::FontVariation;
use crate::FontData;
use crate::platform::font::PlatformFont;
use crate::system_font_service::FontIdentifier;
/// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is
@@ -29,28 +30,62 @@ static CACHE: CoreTextFontCache = CoreTextFontCache(OnceLock::new());
/// A [`HashMap`] of cached [`CTFont`] for a single [`FontIdentifier`]. There is one [`CTFont`]
/// for each cached font size.
type CachedCTFont = HashMap<Au, CTFont>;
type CachedCTFont = HashMap<CoreTextFontCacheKey, PlatformFont>;
#[derive(Eq, Hash, PartialEq)]
struct CoreTextFontCacheKey {
size: Au,
variations: Vec<FontVariation>,
}
impl CoreTextFontCache {
pub(crate) fn core_text_font(
font_identifier: FontIdentifier,
data: Option<&FontData>,
pt_size: f64,
) -> Option<CTFont> {
variations: &[FontVariation],
) -> Option<PlatformFont> {
//// If you pass a zero font size to one of the Core Text APIs, it'll replace it with
//// 12.0. We don't want that! (Issue #10492.)
let clamped_pt_size = pt_size.max(0.01);
let au_size = Au::from_f64_px(clamped_pt_size);
let key = CoreTextFontCacheKey {
size: au_size,
variations: variations.to_owned(),
};
let cache = CACHE.0.get_or_init(Default::default);
{
let cache = cache.read();
if let Some(core_text_font) = cache
if let Some(platform_font) = cache
.get(&font_identifier)
.and_then(|identifier_cache| identifier_cache.get(&au_size))
.and_then(|identifier_cache| identifier_cache.get(&key))
{
return Some(platform_font.clone());
}
}
if !key.variations.is_empty() {
let core_text_font_no_variations =
Self::core_text_font(font_identifier.clone(), data, clamped_pt_size, &[])?;
let mut cache = cache.write();
let entry = cache.entry(font_identifier.clone()).or_default();
// It could be that between the time of the cache miss above and now, after the write lock
// on the cache has been acquired, the cache was populated with the data that we need. Thus
// check again and return the CTFont if it is is already cached.
if let Some(core_text_font) = entry.get(&key) {
return Some(core_text_font.clone());
}
let platform_font = Self::add_variations_to_font(
core_text_font_no_variations,
&key.variations,
clamped_pt_size,
);
entry.insert(key, platform_font.clone());
return Some(platform_font);
}
let mut cache = cache.write();
@@ -59,11 +94,22 @@ impl CoreTextFontCache {
// It could be that between the time of the cache miss above and now, after the write lock
// on the cache has been acquired, the cache was populated with the data that we need. Thus
// check again and return the CTFont if it is is already cached.
if let Some(core_text_font) = identifier_cache.get(&au_size) {
return Some(core_text_font.clone());
if let Some(platform_font) = identifier_cache.get(&key) {
return Some(platform_font.clone());
}
let core_text_font = match font_identifier {
let platform_font =
Self::create_font_without_variations(font_identifier, data, clamped_pt_size)?;
identifier_cache.insert(key, platform_font.clone());
Some(platform_font)
}
pub(crate) fn create_font_without_variations(
font_identifier: FontIdentifier,
data: Option<&FontData>,
clamped_pt_size: f64,
) -> Option<PlatformFont> {
let descriptor = match font_identifier {
FontIdentifier::Local(local_font_identifier) => {
// Other platforms can instantiate a platform font by loading the data
// from a file and passing an index in the case the file is a TTC bundle.
@@ -71,7 +117,7 @@ impl CoreTextFontCache {
// macOS is to create the font using a descriptor with both the PostScript
// name and path.
let cf_name = CFString::new(&local_font_identifier.postscript_name);
let mut descriptor = core_text::font_descriptor::new_from_postscript_name(&cf_name);
let descriptor = core_text::font_descriptor::new_from_postscript_name(&cf_name);
let cf_path = CFString::new(&local_font_identifier.path);
let url_attribute = unsafe { CFString::wrap_under_get_rule(kCTFontURLAttribute) };
@@ -79,25 +125,128 @@ impl CoreTextFontCache {
url_attribute,
CFURL::from_file_system_path(cf_path, kCFURLPOSIXPathStyle, false),
)]);
if let Ok(descriptor_with_path) =
descriptor.create_copy_with_attributes(attributes.to_untyped())
{
descriptor = descriptor_with_path;
}
core_text::font::new_from_descriptor(&descriptor, clamped_pt_size)
descriptor.create_copy_with_attributes(attributes.to_untyped())
},
FontIdentifier::Web(_) => {
let data = data
.expect("Should always have FontData for web fonts")
.clone();
let provider = CGDataProvider::from_buffer(Arc::new(data));
let cgfont = CGFont::from_data_provider(provider).ok()?;
core_text::font::new_from_CGFont(&cgfont, clamped_pt_size)
let cf_data = CFData::from_arc(Arc::new(data));
core_text::font_manager::create_font_descriptor_with_data(cf_data)
},
};
identifier_cache.insert(au_size, core_text_font.clone());
Some(core_text_font)
Some(PlatformFont::new_with_ctfont(
core_text::font::new_from_descriptor(&descriptor.ok()?, clamped_pt_size),
))
}
fn add_variations_to_font(
platform_font: PlatformFont,
specified_variations: &[FontVariation],
pt_size: f64,
) -> PlatformFont {
if specified_variations.is_empty() {
return platform_font;
}
let Some(variations) = Self::get_variation_axis_information(&platform_font) else {
return platform_font;
};
let mut modified_variations = false;
let variations: Vec<_> = variations
.iter()
.map(|variation| {
let value = specified_variations
.iter()
.find_map(|specified_variation| {
if variation.tag == specified_variation.tag as i64 {
Some(specified_variation.value as f64)
} else {
None
}
})
.unwrap_or(variation.default_value)
.clamp(variation.min_value, variation.max_value);
if value != variation.default_value {
modified_variations = true;
}
FontVariation {
tag: variation.tag as u32,
value: value as f32,
}
})
.collect();
if !modified_variations {
return platform_font;
}
let cftype_variations: Vec<_> = variations
.iter()
.map(|variation| {
(
CFNumber::from(variation.tag as i64),
CFNumber::from(variation.value as f64),
)
})
.collect();
let values_dict = CFDictionary::from_CFType_pairs(&cftype_variations);
let variation_attribute =
unsafe { CFString::wrap_under_get_rule(kCTFontVariationAttribute) };
let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute, values_dict)]);
let ct_var_font_desc = platform_font
.ctfont
.copy_descriptor()
.create_copy_with_attributes(attrs_dict.to_untyped())
.unwrap();
let ctfont = core_text::font::new_from_descriptor(&ct_var_font_desc, pt_size);
PlatformFont::new_with_ctfont_and_variations(ctfont, variations)
}
fn get_variation_axis_information(
platform_font: &PlatformFont,
) -> Option<Vec<VariationAxisInformation>> {
Some(
platform_font
.ctfont
.get_variation_axes()?
.iter()
.filter_map(|axes| {
let tag = unsafe { axes.find(kCTFontVariationAxisIdentifierKey) }
.and_then(|tag| tag.downcast::<CFNumber>())?;
let max_value = unsafe { axes.find(kCTFontVariationAxisMaximumValueKey) }
.and_then(|tag| tag.downcast::<CFNumber>())?;
let min_value = unsafe { axes.find(kCTFontVariationAxisMinimumValueKey) }
.and_then(|tag| tag.downcast::<CFNumber>())?;
let default_value = unsafe { axes.find(kCTFontVariationAxisDefaultValueKey) }
.and_then(|tag| tag.downcast::<CFNumber>())?;
Some(VariationAxisInformation {
tag: tag.to_i64()?,
max_value: max_value.to_f64()?,
min_value: min_value.to_f64()?,
default_value: default_value.to_f64()?,
})
})
.collect(),
)
}
}
#[derive(Clone, Default, Debug)]
struct VariationAxisInformation {
tag: i64,
max_value: f64,
min_value: f64,
default_value: f64,
}
unsafe extern "C" {
static kCTFontVariationAxisDefaultValueKey: CFStringRef;
static kCTFontVariationAxisIdentifierKey: CFStringRef;
static kCTFontVariationAxisMaximumValueKey: CFStringRef;
static kCTFontVariationAxisMinimumValueKey: CFStringRef;
}

View File

@@ -19,7 +19,7 @@ use core_text::font_descriptor::{
use euclid::default::{Point2D, Rect, Size2D};
use log::debug;
use style::values::computed::font::{FontStretch, FontStyle, FontWeight};
use webrender_api::FontInstanceFlags;
use webrender_api::{FontInstanceFlags, FontVariation};
use super::core_text_font_cache::CoreTextFontCache;
use super::font_list::LocalFontIdentifier;
@@ -31,6 +31,7 @@ use crate::{
const KERN_PAIR_LEN: usize = 6;
#[derive(Clone)]
pub struct FontTable {
data: CFData,
}
@@ -52,9 +53,10 @@ impl FontTableMethods for FontTable {
}
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct PlatformFont {
ctfont: CTFont,
pub(crate) ctfont: CTFont,
variations: Vec<FontVariation>,
h_kern_subtable: Option<CachedKernTable>,
}
@@ -70,10 +72,52 @@ unsafe impl Sync for PlatformFont {}
unsafe impl Send for PlatformFont {}
impl PlatformFont {
pub(crate) fn new_with_ctfont(ctfont: CTFont) -> Self {
Self::new_with_ctfont_and_variations(ctfont, vec![])
}
pub(crate) fn new_with_ctfont_and_variations(
ctfont: CTFont,
variations: Vec<FontVariation>,
) -> PlatformFont {
Self {
ctfont,
variations,
h_kern_subtable: None,
}
}
fn new(
font_identifier: FontIdentifier,
data: Option<&FontData>,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let size = match requested_size {
Some(s) => s.to_f64_px(),
None => 0.0,
};
let Some(mut platform_font) =
CoreTextFontCache::core_text_font(font_identifier, data, size, variations)
else {
return Err("Could not generate CTFont for FontTemplateData");
};
platform_font.load_h_kern_subtable();
Ok(platform_font)
}
/// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or
/// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case.
fn find_h_kern_subtable(&self) -> Option<CachedKernTable> {
let font_table = self.table_for_tag(KERN)?;
fn load_h_kern_subtable(&mut self) {
if self.h_kern_subtable.is_some() {
return;
}
let Some(font_table) = self.table_for_tag(KERN) else {
return;
};
let mut result = CachedKernTable {
font_table,
pair_data_range: 0..0,
@@ -89,7 +133,7 @@ impl PlatformFont {
let table = result.font_table.buffer();
let version = BigEndian::read_u16(table);
if version != 0 {
return None;
return;
}
let num_subtables = BigEndian::read_u16(&table[2..]);
let mut start = 4;
@@ -102,7 +146,7 @@ impl PlatformFont {
// Found a matching subtable.
if !result.pair_data_range.is_empty() {
debug!("Found multiple horizontal kern tables. Disable fast path.");
return None;
return;
}
// Read the subtable header.
let subtable_start = start + SUBTABLE_HEADER_LEN;
@@ -112,7 +156,7 @@ impl PlatformFont {
result.pair_data_range = pair_data_start..end;
if result.pair_data_range.len() != n_pairs * KERN_PAIR_LEN {
debug!("Bad data in kern header. Disable fast path.");
return None;
return;
}
let pt_per_font_unit =
@@ -122,14 +166,14 @@ impl PlatformFont {
start = end;
}
}
if !result.pair_data_range.is_empty() {
Some(result)
} else {
None
self.h_kern_subtable = Some(result);
}
}
}
#[derive(Clone)]
struct CachedKernTable {
font_table: FontTable,
pair_data_range: Range<usize>,
@@ -164,44 +208,27 @@ impl fmt::Debug for CachedKernTable {
}
}
impl PlatformFont {
fn new(
font_identifier: FontIdentifier,
data: Option<&FontData>,
requested_size: Option<Au>,
) -> Result<PlatformFont, &'static str> {
let size = match requested_size {
Some(s) => s.to_f64_px(),
None => 0.0,
};
let Some(core_text_font) = CoreTextFontCache::core_text_font(font_identifier, data, size)
else {
return Err("Could not generate CTFont for FontTemplateData");
};
let mut handle = PlatformFont {
ctfont: core_text_font.clone_with_font_size(size),
h_kern_subtable: None,
};
handle.h_kern_subtable = handle.find_h_kern_subtable();
Ok(handle)
}
}
impl PlatformFontMethods for PlatformFont {
fn new_from_data(
font_identifier: FontIdentifier,
data: &FontData,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
Self::new(font_identifier, Some(data), requested_size)
Self::new(font_identifier, Some(data), requested_size, variations)
}
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
requested_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
Self::new(FontIdentifier::Local(font_identifier), None, requested_size)
Self::new(
FontIdentifier::Local(font_identifier),
None,
requested_size,
variations,
)
}
fn descriptor(&self) -> FontTemplateDescriptor {
@@ -345,6 +372,10 @@ impl PlatformFontMethods for PlatformFont {
Size2D::new(rect.size.width as f32, rect.size.height as f32),
)
}
fn variations(&self) -> &[FontVariation] {
&self.variations
}
}
pub(super) trait CoreTextFontTraitsMapping {

View File

@@ -6,9 +6,11 @@ use std::fs::File;
use std::path::Path;
use base::text::{UnicodeBlock, UnicodeBlockMethod, unicode_plane};
use log::debug;
use log::{debug, warn};
use malloc_size_of_derive::MallocSizeOf;
use memmap2::Mmap;
use read_fonts::types::NameId;
use read_fonts::{FileRef, TableProvider as _};
use serde::{Deserialize, Serialize};
use style::Atom;
use style::values::computed::font::GenericFontFamily;
@@ -18,8 +20,8 @@ use webrender_api::NativeFontHandle;
use crate::platform::add_noto_fallback_families;
use crate::platform::font::CoreTextFontTraitsMapping;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex,
FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
};
/// An identifier for a local font on a MacOS system. These values comes from the CoreText
@@ -43,16 +45,61 @@ impl LocalFontIdentifier {
0
}
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
// TODO: This is incorrect, if the font file is a TTC (collection) with more than
// one font. In that case we either need to reconstruct the pertinent tables into
// a bundle of font data (expensive) or make sure that the value returned by
// `index()` above is correct. The latter is potentially tricky as macOS might not
// do an accurate mapping between the PostScript name that it gives us and what is
// listed in the font.
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
let file = File::open(Path::new(&*self.path)).ok()?;
let mmap = unsafe { Mmap::map(&file).ok()? };
Some(mmap[..].to_vec())
// Determine index
let file_ref = FileRef::new(mmap.as_ref()).ok()?;
let index = ttc_index_from_postscript_name(file_ref, &self.postscript_name);
Some(FontDataAndIndex {
data: FontData::from_bytes(&mmap),
index,
})
}
}
/// CoreText font enumeration gives us a Postscript name rather than an index.
/// This functions maps from a Postscript name to an index.
///
/// This mapping works for single-font files and for simple TTC files, but may not work in all cases.
/// We are not 100% sure which cases (if any) will not work. But we suspect that variable fonts may cause
/// issues due to the Postscript names corresponding to instances not being straightforward, and the possibility
/// that CoreText may return a non-standard in that scenerio.
fn ttc_index_from_postscript_name(font_file: FileRef<'_>, postscript_name: &str) -> u32 {
match font_file {
// File only contains one font: simply return 0
FileRef::Font(_) => 0,
// File is a collection: iterate through each font in the collection and check
// whether the name matches
FileRef::Collection(collection) => {
for i in 0..collection.len() {
let font = collection.get(i).unwrap();
let name_table = font.name().unwrap();
if name_table
.name_record()
.iter()
.filter(|record| record.name_id() == NameId::POSTSCRIPT_NAME)
.any(|record| {
record
.string(name_table.string_data())
.unwrap()
.chars()
.eq(postscript_name.chars())
})
{
return i;
}
}
// If we fail to find a font, just use the first font in the file.
warn!(
"Font with postscript_name {} not found in collection",
postscript_name
);
0
},
}
}

View File

@@ -6,27 +6,28 @@
// information for an approach that we'll likely need to take when the
// renderer moves to a sandboxed process.
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt;
use std::io::Cursor;
use std::ops::Deref;
use std::sync::Arc;
use app_units::Au;
use dwrote::{FontCollection, FontFace, FontFile};
use dwrote::{
DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile,
};
use euclid::default::{Point2D, Rect, Size2D};
use log::{debug, warn};
use log::debug;
use read_fonts::TableProvider;
use skrifa::Tag;
use style::Zero;
use style::computed_values::font_stretch::T as StyleFontStretch;
use style::computed_values::font_weight::T as StyleFontWeight;
use style::values::computed::font::FontStyle as StyleFontStyle;
use truetype::tables::WindowsMetrics;
use truetype::value::Read;
use webrender_api::FontInstanceFlags;
use webrender_api::{FontInstanceFlags, FontVariation};
use winapi::shared::minwindef::{BOOL, FALSE};
use super::font_list::LocalFontIdentifier;
use crate::{
FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTableTag, FontTemplateDescriptor,
FractionalPixel, GlyphId, PlatformFontMethods, ot_tag,
FractionalPixel, GlyphId, PlatformFontMethods,
};
// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
@@ -67,6 +68,7 @@ pub struct PlatformFont {
em_size: f32,
du_to_px: f32,
scaled_du_to_px: f32,
variations: Vec<FontVariation>,
}
// Based on information from the Skia codebase, it seems that DirectWrite APIs from
@@ -92,7 +94,11 @@ impl<T> Deref for Nondebug<T> {
}
impl PlatformFont {
fn new(font_face: FontFace, pt_size: Option<Au>) -> Result<Self, &'static str> {
fn new(
font_face: FontFace,
pt_size: Option<Au>,
variations: Vec<FontVariation>,
) -> Result<Self, &'static str> {
let pt_size = pt_size.unwrap_or(au_from_pt(12.));
let du_per_em = font_face.metrics().metrics0().designUnitsPerEm as f32;
@@ -107,8 +113,51 @@ impl PlatformFont {
em_size,
du_to_px: design_units_to_pixels,
scaled_du_to_px: scaled_design_units_to_pixels,
variations,
})
}
fn new_with_variations(
font_face: FontFace,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<Self, &'static str> {
if variations.is_empty() {
return Self::new(font_face, pt_size, vec![]);
}
// On FreeType and CoreText platforms, the platform layer is able to read the minimum, maxmimum,
// and default values of each axis. This doesn't seem possible here and it seems that Gecko
// also just sets the value of the axis based on the values from the style as well.
//
// dwrote (and presumably the Windows APIs) accept a reversed version of the table
// tag bytes, which means that `u32::swap_bytes` must be called here in order to
// use a byte order compatible with the rest of Servo.
let variations: Vec<_> = variations
.into_iter()
.map(|variation| DWRITE_FONT_AXIS_VALUE {
axisTag: variation.tag.swap_bytes(),
value: variation.value,
})
.collect();
let Some(font_face) =
font_face.create_font_face_with_variations(DWRITE_FONT_SIMULATIONS_NONE, &variations)
else {
return Err("Could not adapt FontFace to given variations");
};
let variations = font_face.variations().unwrap_or_default();
let variations = variations
.iter()
.map(|dwrote_variation| FontVariation {
tag: dwrote_variation.axisTag.swap_bytes(),
value: dwrote_variation.value,
})
.collect();
Self::new(font_face, pt_size, variations)
}
}
impl PlatformFontMethods for PlatformFont {
@@ -116,20 +165,19 @@ impl PlatformFontMethods for PlatformFont {
_font_identifier: FontIdentifier,
data: &FontData,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<Self, &'static str> {
let font_face = FontFile::new_from_buffer(Arc::new(data.clone()))
.ok_or("Could not create FontFile")?
.create_face(
0, /* face_index */
dwrote::DWRITE_FONT_SIMULATIONS_NONE,
)
.create_face(0 /* face_index */, DWRITE_FONT_SIMULATIONS_NONE)
.map_err(|_| "Could not create FontFace")?;
Self::new(font_face, pt_size)
Self::new_with_variations(font_face, pt_size, variations)
}
fn new_from_local_font_identifier(
font_identifier: LocalFontIdentifier,
pt_size: Option<Au>,
variations: &[FontVariation],
) -> Result<PlatformFont, &'static str> {
let font_face = FontCollection::system()
.font_from_descriptor(&font_identifier.font_descriptor)
@@ -137,100 +185,48 @@ impl PlatformFontMethods for PlatformFont {
.flatten()
.ok_or("Could not create Font from descriptor")?
.create_font_face();
Self::new(font_face, pt_size)
Self::new_with_variations(font_face, pt_size, variations)
}
fn descriptor(&self) -> FontTemplateDescriptor {
// We need the font (DWriteFont) in order to be able to query things like
// the family name, face name, weight, etc. On Windows 10, the
// DWriteFontFace3 interface provides this on the FontFace, but that's only
// available on Win10+.
//
// Instead, we do the parsing work using the truetype crate for raw fonts.
// We're just extracting basic info, so this is sufficient for now.
//
// The `dwrote` APIs take SFNT table tags in a reversed byte order, which
// is why `u32::swap_bytes()` is called here.
let windows_metrics_bytes = self
.face
.get_font_table(u32::swap_bytes(ot_tag!('O', 'S', '/', '2')));
if windows_metrics_bytes.is_none() {
warn!("Could not find OS/2 table in font.");
return FontTemplateDescriptor::default();
}
let mut cursor = Cursor::new(windows_metrics_bytes.as_ref().unwrap());
let Ok(table) = WindowsMetrics::read(&mut cursor) else {
warn!("Could not read OS/2 table in font.");
return FontTemplateDescriptor::default();
};
let (weight_val, width_val, italic_bool) = match table {
WindowsMetrics::Version0(ref m) => {
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
},
WindowsMetrics::Version1(ref m) => {
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
},
WindowsMetrics::Version2(ref m) |
WindowsMetrics::Version3(ref m) |
WindowsMetrics::Version4(ref m) => {
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
},
WindowsMetrics::Version5(ref m) => {
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
},
};
let weight = StyleFontWeight::from_float(weight_val as f32);
let stretch = match width_val.clamp(1, 9) {
1 => StyleFontStretch::ULTRA_CONDENSED,
2 => StyleFontStretch::EXTRA_CONDENSED,
3 => StyleFontStretch::CONDENSED,
4 => StyleFontStretch::SEMI_CONDENSED,
5 => StyleFontStretch::NORMAL,
6 => StyleFontStretch::SEMI_EXPANDED,
7 => StyleFontStretch::EXPANDED,
8 => StyleFontStretch::EXTRA_EXPANDED,
9 => StyleFontStretch::ULTRA_CONDENSED,
_ => {
warn!("Unknown stretch size.");
StyleFontStretch::NORMAL
},
};
let style = if italic_bool {
StyleFontStyle::ITALIC
} else {
StyleFontStyle::NORMAL
};
FontTemplateDescriptor::new(weight, stretch, style)
DirectWriteTableProvider::new(self)
.os2()
.as_ref()
.map(Self::descriptor_from_os2_table)
.unwrap_or_default()
}
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
let glyph = self.face.get_glyph_indices(&[codepoint as u32])[0];
if glyph == 0 {
let Ok(glyphs) = self.face.glyph_indices(&[codepoint as u32]) else {
return None;
};
let Some(glyph) = glyphs.first() else {
return None;
};
if *glyph == 0 {
return None;
}
Some(glyph as GlyphId)
Some(*glyph as GlyphId)
}
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
if glyph == 0 {
return None;
}
let gm = self.face.get_design_glyph_metrics(&[glyph as u16], false)[0];
let f = (gm.advanceWidth as f32 * self.scaled_du_to_px) as FractionalPixel;
Some(f)
let Ok(metrics) = self.face.design_glyph_metrics(&[glyph as u16], false) else {
return None;
};
let Some(glyph_metric) = metrics.first() else {
return None;
};
Some((glyph_metric.advanceWidth as f32 * self.scaled_du_to_px) as FractionalPixel)
}
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
let adjustment = self
.face
.get_glyph_pair_kerning_adjustment(first_glyph as u16, second_glyph as u16);
.glyph_pair_kerning_adjustment(first_glyph as u16, second_glyph as u16)
.unwrap_or_default();
(adjustment as f32 * self.scaled_du_to_px) as FractionalPixel
}
@@ -291,7 +287,9 @@ impl PlatformFontMethods for PlatformFont {
// tag bytes, which means that `u32::swap_bytes` must be called here in order to
// use a byte order compatible with the rest of Servo.
self.face
.get_font_table(u32::swap_bytes(tag))
.font_table(u32::swap_bytes(tag))
.ok()
.flatten()
.map(|bytes| FontTable { data: bytes })
}
@@ -300,10 +298,12 @@ impl PlatformFontMethods for PlatformFont {
}
fn typographic_bounds(&self, glyph_id: GlyphId) -> Rect<f32> {
let metrics = self
.face
.get_design_glyph_metrics(&[glyph_id as u16], false);
let metrics = &metrics[0];
let Ok(metrics) = self.face.design_glyph_metrics(&[glyph_id as u16], false) else {
return Rect::zero();
};
let Some(metrics) = metrics.first() else {
return Rect::zero();
};
let advance_width = metrics.advanceWidth as f32;
let advance_height = metrics.advanceHeight as f32;
let left_side_bearing = metrics.leftSideBearing as f32;
@@ -320,4 +320,76 @@ impl PlatformFontMethods for PlatformFont {
Size2D::new(width, height),
)
}
fn variations(&self) -> &[FontVariation] {
&self.variations
}
}
/// A wrapper struct around [`PlatformFont`] which is responsible for
/// implementing [`TableProvider`] and cleaning up any font table contexts from
/// DirectWrite when the struct is dropped.
struct DirectWriteTableProvider<'platform_font> {
platform_font: &'platform_font PlatformFont,
contexts: RefCell<Vec<*mut c_void>>,
}
impl<'platform_font> DirectWriteTableProvider<'platform_font> {
fn new(platform_font: &'platform_font PlatformFont) -> Self {
Self {
platform_font,
contexts: Default::default(),
}
}
}
impl Drop for DirectWriteTableProvider<'_> {
fn drop(&mut self) {
let direct_write_face = unsafe { self.platform_font.face.as_ptr() };
assert!(!direct_write_face.is_null());
let direct_write_face = unsafe { &*direct_write_face };
for context in self.contexts.borrow_mut().drain(..) {
unsafe { direct_write_face.ReleaseFontTable(context) };
}
}
}
impl<'platform_font> TableProvider<'platform_font> for DirectWriteTableProvider<'platform_font> {
fn data_for_tag(&self, tag: Tag) -> Option<read_fonts::FontData<'platform_font>> {
let direct_write_face = unsafe { self.platform_font.face.as_ptr() };
if direct_write_face.is_null() {
return None;
}
let direct_write_face = unsafe { &*direct_write_face };
let direct_write_tag = u32::from_be_bytes(tag.to_be_bytes()).swap_bytes();
let mut table_data_ptr: *const u8 = std::ptr::null_mut();
let mut table_size: u32 = 0;
let mut table_context: *mut c_void = std::ptr::null_mut();
let mut exists: BOOL = FALSE;
let hr = unsafe {
direct_write_face.TryGetFontTable(
direct_write_tag,
&mut table_data_ptr as *mut *const _ as *mut *const c_void,
&mut table_size,
&mut table_context,
&mut exists,
)
};
if hr != 0 || exists == 0 {
return None;
}
self.contexts.borrow_mut().push(table_context);
if table_data_ptr.is_null() || table_size == 0 {
return None;
}
let bytes = unsafe { std::slice::from_raw_parts(table_data_ptr, table_size as usize) };
Some(read_fonts::FontData::new(bytes))
}
}

View File

@@ -15,8 +15,8 @@ use style::values::specified::font::FontStretchKeyword;
use webrender_api::NativeFontHandle;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex,
FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
};
pub fn for_each_available_family<F>(mut callback: F)
@@ -56,10 +56,12 @@ impl LocalFontIdentifier {
.expect("Could not create Font from FontDescriptor")
.create_font_face();
let path = face
.get_files()
.first()
.files()
.ok()
.and_then(|files| files.first().cloned())
.expect("Could not get FontFace files")
.get_font_file_path()
.font_file_path()
.ok()
.expect("Could not get FontFace files path");
NativeFontHandle {
path,
@@ -67,14 +69,19 @@ impl LocalFontIdentifier {
}
}
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
let font = FontCollection::system()
.font_from_descriptor(&self.font_descriptor)
.ok()??;
let face = font.create_font_face();
let files = face.get_files();
let index = face.get_index();
let files = face.files().ok()?;
assert!(!files.is_empty());
Some(files[0].get_font_file_bytes())
let data = files[0].font_file_bytes().ok()?;
let data = FontData::from_bytes(&data);
Some(FontDataAndIndex { data, index })
}
}

View File

@@ -1,762 +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/. */
#![allow(unsafe_code)]
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::sync::LazyLock;
use std::{char, cmp, ptr};
use app_units::Au;
use base::text::is_bidi_control;
use euclid::default::Point2D;
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
// shapers. For now, however, HarfBuzz is a hard dependency.
use harfbuzz_sys::{
HB_DIRECTION_LTR, HB_DIRECTION_RTL, HB_MEMORY_MODE_READONLY, HB_OT_LAYOUT_BASELINE_TAG_HANGING,
HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
hb_blob_create, hb_blob_t, hb_bool_t, hb_buffer_add_utf8, hb_buffer_create, hb_buffer_destroy,
hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions, hb_buffer_get_length,
hb_buffer_set_direction, hb_buffer_set_script, hb_buffer_t, hb_codepoint_t,
hb_face_create_for_tables, hb_face_destroy, hb_face_t, hb_feature_t, hb_font_create,
hb_font_destroy, hb_font_funcs_create, hb_font_funcs_set_glyph_h_advance_func,
hb_font_funcs_set_nominal_glyph_func, hb_font_funcs_t, hb_font_set_funcs, hb_font_set_ppem,
hb_font_set_scale, hb_font_t, hb_glyph_info_t, hb_glyph_position_t, hb_ot_layout_get_baseline,
hb_position_t, hb_shape, hb_tag_t,
};
use log::debug;
use num_traits::Zero;
use crate::platform::font::FontTable;
use crate::{
BASE, ByteIndex, Font, FontBaseline, FontTableMethods, FontTableTag, GlyphData, GlyphId,
GlyphStore, KERN, ShapingFlags, ShapingOptions, fixed_to_float, float_to_fixed, ot_tag,
};
const NO_GLYPH: i32 = -1;
const LIGA: u32 = ot_tag!('l', 'i', 'g', 'a');
const HB_OT_TAG_DEFAULT_SCRIPT: u32 = ot_tag!('D', 'F', 'L', 'T');
const HB_OT_TAG_DEFAULT_LANGUAGE: u32 = ot_tag!('d', 'f', 'l', 't');
pub struct ShapedGlyphData {
count: usize,
glyph_infos: *mut hb_glyph_info_t,
pos_infos: *mut hb_glyph_position_t,
}
pub struct ShapedGlyphEntry {
codepoint: GlyphId,
advance: Au,
offset: Option<Point2D<Au>>,
}
impl ShapedGlyphData {
/// Create a new [`ShapedGlyphData`] from the given HarfBuzz buffer.
///
/// # Safety
///
/// Passing an invalid buffer pointer to this function results in undefined behavior.
pub unsafe fn new(buffer: *mut hb_buffer_t) -> ShapedGlyphData {
let mut glyph_count = 0;
let glyph_infos = unsafe { hb_buffer_get_glyph_infos(buffer, &mut glyph_count) };
assert!(!glyph_infos.is_null());
let mut pos_count = 0;
let pos_infos = unsafe { hb_buffer_get_glyph_positions(buffer, &mut pos_count) };
assert!(!pos_infos.is_null());
assert_eq!(glyph_count, pos_count);
ShapedGlyphData {
count: glyph_count as usize,
glyph_infos,
pos_infos,
}
}
#[inline(always)]
fn byte_offset_of_glyph(&self, i: usize) -> u32 {
assert!(i < self.count);
unsafe {
let glyph_info_i = self.glyph_infos.add(i);
(*glyph_info_i).cluster
}
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
/// Returns shaped glyph data for one glyph, and updates the y-position of the pen.
pub fn entry_for_glyph(&self, i: usize, y_pos: &mut Au) -> ShapedGlyphEntry {
assert!(i < self.count);
unsafe {
let glyph_info_i = self.glyph_infos.add(i);
let pos_info_i = self.pos_infos.add(i);
let x_offset = Shaper::fixed_to_float((*pos_info_i).x_offset);
let y_offset = Shaper::fixed_to_float((*pos_info_i).y_offset);
let x_advance = Shaper::fixed_to_float((*pos_info_i).x_advance);
let y_advance = Shaper::fixed_to_float((*pos_info_i).y_advance);
let x_offset = Au::from_f64_px(x_offset);
let y_offset = Au::from_f64_px(y_offset);
let x_advance = Au::from_f64_px(x_advance);
let y_advance = Au::from_f64_px(y_advance);
let offset = if x_offset.is_zero() && y_offset.is_zero() && y_advance.is_zero() {
None
} else {
// adjust the pen..
if y_advance > Au::zero() {
*y_pos -= y_advance;
}
Some(Point2D::new(x_offset, *y_pos - y_offset))
};
ShapedGlyphEntry {
codepoint: (*glyph_info_i).codepoint as GlyphId,
advance: x_advance,
offset,
}
}
}
}
#[derive(Debug)]
pub struct Shaper {
hb_face: *mut hb_face_t,
hb_font: *mut hb_font_t,
font: *const Font,
}
// The HarfBuzz API is thread safe as well as our `Font`, so we can make the data
// structures here as thread-safe as well. This doesn't seem to be documented,
// but was expressed as one of the original goals of the HarfBuzz API.
unsafe impl Sync for Shaper {}
unsafe impl Send for Shaper {}
impl Drop for Shaper {
fn drop(&mut self) {
unsafe {
assert!(!self.hb_face.is_null());
hb_face_destroy(self.hb_face);
assert!(!self.hb_font.is_null());
hb_font_destroy(self.hb_font);
}
}
}
impl Shaper {
#[allow(clippy::not_unsafe_ptr_arg_deref)] // Has an unsafe block inside
pub fn new(font: *const Font) -> Shaper {
unsafe {
let hb_face: *mut hb_face_t = hb_face_create_for_tables(
Some(font_table_func),
font as *const c_void as *mut c_void,
None,
);
let hb_font: *mut hb_font_t = hb_font_create(hb_face);
// Set points-per-em. if zero, performs no hinting in that direction.
let pt_size = (*font).descriptor.pt_size.to_f64_px();
hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
// Set scaling. Note that this takes 16.16 fixed point.
hb_font_set_scale(
hb_font,
Shaper::float_to_fixed(pt_size) as c_int,
Shaper::float_to_fixed(pt_size) as c_int,
);
// configure static function callbacks.
hb_font_set_funcs(
hb_font,
HB_FONT_FUNCS.0,
font as *mut Font as *mut c_void,
None,
);
Shaper {
hb_face,
hb_font,
font,
}
}
}
fn float_to_fixed(f: f64) -> i32 {
float_to_fixed(16, f)
}
fn fixed_to_float(i: hb_position_t) -> f64 {
fixed_to_float(16, i)
}
}
pub fn unicode_to_hb_script(script: unicode_script::Script) -> harfbuzz_sys::hb_script_t {
use harfbuzz_sys::*;
use unicode_script::Script::*;
match script {
Adlam => HB_SCRIPT_ADLAM,
Ahom => HB_SCRIPT_AHOM,
Anatolian_Hieroglyphs => HB_SCRIPT_ANATOLIAN_HIEROGLYPHS,
Arabic => HB_SCRIPT_ARABIC,
Armenian => HB_SCRIPT_ARMENIAN,
Avestan => HB_SCRIPT_AVESTAN,
Balinese => HB_SCRIPT_BALINESE,
Bamum => HB_SCRIPT_BAMUM,
Bassa_Vah => HB_SCRIPT_BASSA_VAH,
Batak => HB_SCRIPT_BATAK,
Bengali => HB_SCRIPT_BENGALI,
Bhaiksuki => HB_SCRIPT_BHAIKSUKI,
Bopomofo => HB_SCRIPT_BOPOMOFO,
Brahmi => HB_SCRIPT_BRAHMI,
Braille => HB_SCRIPT_BRAILLE,
Buginese => HB_SCRIPT_BUGINESE,
Buhid => HB_SCRIPT_BUHID,
Canadian_Aboriginal => HB_SCRIPT_CANADIAN_SYLLABICS,
Carian => HB_SCRIPT_CARIAN,
Caucasian_Albanian => HB_SCRIPT_CAUCASIAN_ALBANIAN,
Chakma => HB_SCRIPT_CHAKMA,
Cham => HB_SCRIPT_CHAM,
Cherokee => HB_SCRIPT_CHEROKEE,
Chorasmian => HB_SCRIPT_CHORASMIAN,
Common => HB_SCRIPT_COMMON,
Coptic => HB_SCRIPT_COPTIC,
Cuneiform => HB_SCRIPT_CUNEIFORM,
Cypriot => HB_SCRIPT_CYPRIOT,
Cyrillic => HB_SCRIPT_CYRILLIC,
Deseret => HB_SCRIPT_DESERET,
Devanagari => HB_SCRIPT_DEVANAGARI,
Dives_Akuru => HB_SCRIPT_DIVES_AKURU,
Dogra => HB_SCRIPT_DOGRA,
Duployan => HB_SCRIPT_DUPLOYAN,
Egyptian_Hieroglyphs => HB_SCRIPT_EGYPTIAN_HIEROGLYPHS,
Elbasan => HB_SCRIPT_ELBASAN,
Elymaic => HB_SCRIPT_ELYMAIC,
Ethiopic => HB_SCRIPT_ETHIOPIC,
Georgian => HB_SCRIPT_GEORGIAN,
Glagolitic => HB_SCRIPT_GLAGOLITIC,
Gothic => HB_SCRIPT_GOTHIC,
Grantha => HB_SCRIPT_GRANTHA,
Greek => HB_SCRIPT_GREEK,
Gujarati => HB_SCRIPT_GUJARATI,
Gunjala_Gondi => HB_SCRIPT_GUNJALA_GONDI,
Gurmukhi => HB_SCRIPT_GURMUKHI,
Han => HB_SCRIPT_HAN,
Hangul => HB_SCRIPT_HANGUL,
Hanifi_Rohingya => HB_SCRIPT_HANIFI_ROHINGYA,
Hanunoo => HB_SCRIPT_HANUNOO,
Hatran => HB_SCRIPT_HATRAN,
Hebrew => HB_SCRIPT_HEBREW,
Hiragana => HB_SCRIPT_HIRAGANA,
Imperial_Aramaic => HB_SCRIPT_IMPERIAL_ARAMAIC,
Inherited => HB_SCRIPT_INHERITED,
Inscriptional_Pahlavi => HB_SCRIPT_INSCRIPTIONAL_PAHLAVI,
Inscriptional_Parthian => HB_SCRIPT_INSCRIPTIONAL_PARTHIAN,
Javanese => HB_SCRIPT_JAVANESE,
Kaithi => HB_SCRIPT_KAITHI,
Kannada => HB_SCRIPT_KANNADA,
Katakana => HB_SCRIPT_KATAKANA,
Kayah_Li => HB_SCRIPT_KAYAH_LI,
Kharoshthi => HB_SCRIPT_KHAROSHTHI,
Khitan_Small_Script => HB_SCRIPT_KHITAN_SMALL_SCRIPT,
Khmer => HB_SCRIPT_KHMER,
Khojki => HB_SCRIPT_KHOJKI,
Khudawadi => HB_SCRIPT_KHUDAWADI,
Lao => HB_SCRIPT_LAO,
Latin => HB_SCRIPT_LATIN,
Lepcha => HB_SCRIPT_LEPCHA,
Limbu => HB_SCRIPT_LIMBU,
Linear_A => HB_SCRIPT_LINEAR_A,
Linear_B => HB_SCRIPT_LINEAR_B,
Lisu => HB_SCRIPT_LISU,
Lycian => HB_SCRIPT_LYCIAN,
Lydian => HB_SCRIPT_LYDIAN,
Mahajani => HB_SCRIPT_MAHAJANI,
Makasar => HB_SCRIPT_MAKASAR,
Malayalam => HB_SCRIPT_MALAYALAM,
Mandaic => HB_SCRIPT_MANDAIC,
Manichaean => HB_SCRIPT_MANICHAEAN,
Marchen => HB_SCRIPT_MARCHEN,
Masaram_Gondi => HB_SCRIPT_MASARAM_GONDI,
Medefaidrin => HB_SCRIPT_MEDEFAIDRIN,
Meetei_Mayek => HB_SCRIPT_MEETEI_MAYEK,
Mende_Kikakui => HB_SCRIPT_MENDE_KIKAKUI,
Meroitic_Cursive => HB_SCRIPT_MEROITIC_CURSIVE,
Meroitic_Hieroglyphs => HB_SCRIPT_MEROITIC_HIEROGLYPHS,
Miao => HB_SCRIPT_MIAO,
Modi => HB_SCRIPT_MODI,
Mongolian => HB_SCRIPT_MONGOLIAN,
Mro => HB_SCRIPT_MRO,
Multani => HB_SCRIPT_MULTANI,
Myanmar => HB_SCRIPT_MYANMAR,
Nabataean => HB_SCRIPT_NABATAEAN,
Nandinagari => HB_SCRIPT_NANDINAGARI,
New_Tai_Lue => HB_SCRIPT_NEW_TAI_LUE,
Newa => HB_SCRIPT_NEWA,
Nko => HB_SCRIPT_NKO,
Nushu => HB_SCRIPT_NUSHU,
Nyiakeng_Puachue_Hmong => HB_SCRIPT_NYIAKENG_PUACHUE_HMONG,
Ogham => HB_SCRIPT_OGHAM,
Ol_Chiki => HB_SCRIPT_OL_CHIKI,
Old_Hungarian => HB_SCRIPT_OLD_HUNGARIAN,
Old_Italic => HB_SCRIPT_OLD_ITALIC,
Old_North_Arabian => HB_SCRIPT_OLD_NORTH_ARABIAN,
Old_Permic => HB_SCRIPT_OLD_PERMIC,
Old_Persian => HB_SCRIPT_OLD_PERSIAN,
Old_Sogdian => HB_SCRIPT_OLD_SOGDIAN,
Old_South_Arabian => HB_SCRIPT_OLD_SOUTH_ARABIAN,
Old_Turkic => HB_SCRIPT_OLD_TURKIC,
Oriya => HB_SCRIPT_ORIYA,
Osage => HB_SCRIPT_OSAGE,
Osmanya => HB_SCRIPT_OSMANYA,
Pahawh_Hmong => HB_SCRIPT_PAHAWH_HMONG,
Palmyrene => HB_SCRIPT_PALMYRENE,
Pau_Cin_Hau => HB_SCRIPT_PAU_CIN_HAU,
Phags_Pa => HB_SCRIPT_PHAGS_PA,
Phoenician => HB_SCRIPT_PHOENICIAN,
Psalter_Pahlavi => HB_SCRIPT_PSALTER_PAHLAVI,
Rejang => HB_SCRIPT_REJANG,
Runic => HB_SCRIPT_RUNIC,
Samaritan => HB_SCRIPT_SAMARITAN,
Saurashtra => HB_SCRIPT_SAURASHTRA,
Sharada => HB_SCRIPT_SHARADA,
Shavian => HB_SCRIPT_SHAVIAN,
Siddham => HB_SCRIPT_SIDDHAM,
SignWriting => HB_SCRIPT_SIGNWRITING,
Sinhala => HB_SCRIPT_SINHALA,
Sogdian => HB_SCRIPT_SOGDIAN,
Sora_Sompeng => HB_SCRIPT_SORA_SOMPENG,
Soyombo => HB_SCRIPT_SOYOMBO,
Sundanese => HB_SCRIPT_SUNDANESE,
Syloti_Nagri => HB_SCRIPT_SYLOTI_NAGRI,
Syriac => HB_SCRIPT_SYRIAC,
Tagalog => HB_SCRIPT_TAGALOG,
Tagbanwa => HB_SCRIPT_TAGBANWA,
Tai_Le => HB_SCRIPT_TAI_LE,
Tai_Tham => HB_SCRIPT_TAI_THAM,
Tai_Viet => HB_SCRIPT_TAI_VIET,
Takri => HB_SCRIPT_TAKRI,
Tamil => HB_SCRIPT_TAMIL,
Tangut => HB_SCRIPT_TANGUT,
Telugu => HB_SCRIPT_TELUGU,
Thaana => HB_SCRIPT_THAANA,
Thai => HB_SCRIPT_THAI,
Tibetan => HB_SCRIPT_TIBETAN,
Tifinagh => HB_SCRIPT_TIFINAGH,
Tirhuta => HB_SCRIPT_TIRHUTA,
Ugaritic => HB_SCRIPT_UGARITIC,
Unknown => HB_SCRIPT_UNKNOWN,
Vai => HB_SCRIPT_VAI,
Warang_Citi => HB_SCRIPT_WARANG_CITI,
Wancho => HB_SCRIPT_WANCHO,
Yezidi => HB_SCRIPT_YEZIDI,
Yi => HB_SCRIPT_YI,
Zanabazar_Square => HB_SCRIPT_ZANABAZAR_SQUARE,
_ => HB_SCRIPT_UNKNOWN,
}
}
impl Shaper {
/// Calculate the layout metrics associated with the given text when painted in a specific
/// font.
pub(crate) fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
unsafe {
let hb_buffer: *mut hb_buffer_t = hb_buffer_create();
hb_buffer_set_direction(
hb_buffer,
if options.flags.contains(ShapingFlags::RTL_FLAG) {
HB_DIRECTION_RTL
} else {
HB_DIRECTION_LTR
},
);
hb_buffer_set_script(hb_buffer, unicode_to_hb_script(options.script));
hb_buffer_add_utf8(
hb_buffer,
text.as_ptr() as *const c_char,
text.len() as c_int,
0,
text.len() as c_int,
);
let mut features = Vec::new();
if options
.flags
.contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG)
{
features.push(hb_feature_t {
tag: LIGA,
value: 0,
start: 0,
end: hb_buffer_get_length(hb_buffer),
})
}
if options
.flags
.contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
{
features.push(hb_feature_t {
tag: KERN,
value: 0,
start: 0,
end: hb_buffer_get_length(hb_buffer),
})
}
hb_shape(
self.hb_font,
hb_buffer,
features.as_mut_ptr(),
features.len() as u32,
);
self.save_glyph_results(text, options, glyphs, hb_buffer);
hb_buffer_destroy(hb_buffer);
}
}
fn save_glyph_results(
&self,
text: &str,
options: &ShapingOptions,
glyphs: &mut GlyphStore,
buffer: *mut hb_buffer_t,
) {
let glyph_data = unsafe { ShapedGlyphData::new(buffer) };
let glyph_count = glyph_data.len();
let byte_max = text.len();
debug!(
"Shaped text[byte count={}], got back {} glyph info records.",
byte_max, glyph_count
);
// make map of what chars have glyphs
let mut byte_to_glyph = vec![NO_GLYPH; byte_max];
debug!("(glyph idx) -> (text byte offset)");
for i in 0..glyph_data.len() {
let loc = glyph_data.byte_offset_of_glyph(i) as usize;
if loc < byte_max {
byte_to_glyph[loc] = i as i32;
} else {
debug!(
"ERROR: tried to set out of range byte_to_glyph: idx={}, glyph idx={}",
loc, i
);
}
debug!("{} -> {}", i, loc);
}
debug!("text: {:?}", text);
debug!("(char idx): char->(glyph index):");
for (i, ch) in text.char_indices() {
debug!("{}: {:?} --> {}", i, ch, byte_to_glyph[i]);
}
let mut glyph_span = 0..0;
let mut byte_range = 0..0;
let mut y_pos = Au::zero();
// main loop over each glyph. each iteration usually processes 1 glyph and 1+ chars.
// in cases with complex glyph-character associations, 2+ glyphs and 1+ chars can be
// processed.
while glyph_span.start < glyph_count {
debug!("Processing glyph at idx={}", glyph_span.start);
glyph_span.end = glyph_span.start;
byte_range.end = glyph_data.byte_offset_of_glyph(glyph_span.start) as usize;
while byte_range.end < byte_max {
byte_range.end += 1;
// Extend the byte range to include any following byte without its own glyph.
while byte_range.end < byte_max && byte_to_glyph[byte_range.end] == NO_GLYPH {
byte_range.end += 1;
}
// Extend the glyph range to include all glyphs covered by bytes processed so far.
let mut max_glyph_idx = glyph_span.end;
for glyph_idx in &byte_to_glyph[byte_range.clone()] {
if *glyph_idx != NO_GLYPH {
max_glyph_idx = cmp::max(*glyph_idx as usize + 1, max_glyph_idx);
}
}
if max_glyph_idx > glyph_span.end {
glyph_span.end = max_glyph_idx;
debug!("Extended glyph span to {:?}", glyph_span);
}
// if there's just one glyph, then we don't need further checks.
if glyph_span.len() == 1 {
break;
}
// if no glyphs were found yet, extend the char byte range more.
if glyph_span.is_empty() {
continue;
}
// If byte_range now includes all the byte offsets found in glyph_span, then we
// have found a contiguous "cluster" and can stop extending it.
let mut all_glyphs_are_within_cluster: bool = true;
for j in glyph_span.clone() {
let loc = glyph_data.byte_offset_of_glyph(j) as usize;
if !(byte_range.start <= loc && loc < byte_range.end) {
all_glyphs_are_within_cluster = false;
break;
}
}
if all_glyphs_are_within_cluster {
break;
}
// Otherwise, the bytes we have seen so far correspond to a non-contiguous set of
// glyphs. Keep extending byte_range until we fill in all the holes in the glyph
// span or reach the end of the text.
}
assert!(!byte_range.is_empty());
assert!(!glyph_span.is_empty());
// Now byte_range is the ligature clump formed by the glyphs in glyph_span.
// We will save these glyphs to the glyph store at the index of the first byte.
let byte_idx = ByteIndex(byte_range.start as isize);
if glyph_span.len() == 1 {
// Fast path: 1-to-1 mapping of byte offset to single glyph.
//
// TODO(Issue #214): cluster ranges need to be computed before
// shaping, and then consulted here.
// for now, just pretend that every character is a cluster start.
// (i.e., pretend there are no combining character sequences).
// 1-to-1 mapping of character to glyph also treated as ligature start.
//
// NB: When we acquire the ability to handle ligatures that cross word boundaries,
// we'll need to do something special to handle `word-spacing` properly.
let character = text[byte_range.clone()].chars().next().unwrap();
if is_bidi_control(character) {
// Don't add any glyphs for bidi control chars
} else {
let (glyph_id, advance, offset) = if character == '\t' {
// Treat tabs in pre-formatted text as a fixed number of spaces. The glyph id does
// not matter here as Servo doesn't render any glyphs for whitespace.
//
// TODO: Proper tab stops. This should happen in layout and be based on the
// size of the space character of the inline formatting context.
let font = unsafe { &(*self.font) };
(
font.glyph_index(' ').unwrap_or(0) as hb_codepoint_t,
font.metrics.space_advance * 8,
Default::default(),
)
} else {
let shape = glyph_data.entry_for_glyph(glyph_span.start, &mut y_pos);
let advance =
self.advance_for_shaped_glyph(shape.advance, character, options);
(shape.codepoint, advance, shape.offset)
};
let data = GlyphData::new(glyph_id, advance, offset, true, true);
glyphs.add_glyph_for_byte_index(byte_idx, character, &data);
}
} else {
// collect all glyphs to be assigned to the first character.
let mut datas = vec![];
for glyph_i in glyph_span.clone() {
let shape = glyph_data.entry_for_glyph(glyph_i, &mut y_pos);
datas.push(GlyphData::new(
shape.codepoint,
shape.advance,
shape.offset,
true, // treat as cluster start
glyph_i > glyph_span.start,
));
// all but first are ligature continuations
}
// now add the detailed glyph entry.
glyphs.add_glyphs_for_byte_index(byte_idx, &datas);
}
glyph_span.start = glyph_span.end;
byte_range.start = byte_range.end;
}
// this must be called after adding all glyph data; it sorts the
// lookup table for finding detailed glyphs by associated char index.
glyphs.finalize_changes();
}
fn advance_for_shaped_glyph(
&self,
mut advance: Au,
character: char,
options: &ShapingOptions,
) -> Au {
if let Some(letter_spacing) = options.letter_spacing {
advance += letter_spacing;
};
// CSS 2.1 § 16.4 states that "word spacing affects each space (U+0020) and non-breaking
// space (U+00A0) left in the text after the white space processing rules have been
// applied. The effect of the property on other word-separator characters is undefined."
// We elect to only space the two required code points.
if character == ' ' || character == '\u{a0}' {
// https://drafts.csswg.org/css-text-3/#word-spacing-property
advance += options.word_spacing;
}
advance
}
pub fn baseline(&self) -> Option<FontBaseline> {
unsafe { (*self.font).table_for_tag(BASE)? };
let mut hanging_baseline = 0;
let mut alphabetic_baseline = 0;
let mut ideographic_baseline = 0;
unsafe {
hb_ot_layout_get_baseline(
self.hb_font,
HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
HB_DIRECTION_LTR,
HB_OT_TAG_DEFAULT_SCRIPT,
HB_OT_TAG_DEFAULT_LANGUAGE,
&mut alphabetic_baseline as *mut _,
);
hb_ot_layout_get_baseline(
self.hb_font,
HB_OT_LAYOUT_BASELINE_TAG_HANGING,
HB_DIRECTION_LTR,
HB_OT_TAG_DEFAULT_SCRIPT,
HB_OT_TAG_DEFAULT_LANGUAGE,
&mut hanging_baseline as *mut _,
);
hb_ot_layout_get_baseline(
self.hb_font,
HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
HB_DIRECTION_LTR,
HB_OT_TAG_DEFAULT_SCRIPT,
HB_OT_TAG_DEFAULT_LANGUAGE,
&mut ideographic_baseline as *mut _,
);
}
Some(FontBaseline {
ideographic_baseline: Shaper::fixed_to_float(ideographic_baseline) as f32,
alphabetic_baseline: Shaper::fixed_to_float(alphabetic_baseline) as f32,
hanging_baseline: Shaper::fixed_to_float(hanging_baseline) as f32,
})
}
}
/// Callbacks from Harfbuzz when font map and glyph advance lookup needed.
struct FontFuncs(*mut hb_font_funcs_t);
unsafe impl Sync for FontFuncs {}
unsafe impl Send for FontFuncs {}
static HB_FONT_FUNCS: LazyLock<FontFuncs> = LazyLock::new(|| unsafe {
let hb_funcs = hb_font_funcs_create();
hb_font_funcs_set_nominal_glyph_func(hb_funcs, Some(glyph_func), ptr::null_mut(), None);
hb_font_funcs_set_glyph_h_advance_func(
hb_funcs,
Some(glyph_h_advance_func),
ptr::null_mut(),
None,
);
FontFuncs(hb_funcs)
});
extern "C" fn glyph_func(
_: *mut hb_font_t,
font_data: *mut c_void,
unicode: hb_codepoint_t,
glyph: *mut hb_codepoint_t,
_: *mut c_void,
) -> hb_bool_t {
let font: *const Font = font_data as *const Font;
assert!(!font.is_null());
unsafe {
match (*font).glyph_index(char::from_u32(unicode).unwrap()) {
Some(g) => {
*glyph = g as hb_codepoint_t;
true as hb_bool_t
},
None => false as hb_bool_t,
}
}
}
extern "C" fn glyph_h_advance_func(
_: *mut hb_font_t,
font_data: *mut c_void,
glyph: hb_codepoint_t,
_: *mut c_void,
) -> hb_position_t {
let font: *mut Font = font_data as *mut Font;
assert!(!font.is_null());
unsafe {
let advance = (*font).glyph_h_advance(glyph as GlyphId);
Shaper::float_to_fixed(advance)
}
}
// Callback to get a font table out of a font.
extern "C" fn font_table_func(
_: *mut hb_face_t,
tag: hb_tag_t,
user_data: *mut c_void,
) -> *mut hb_blob_t {
unsafe {
// NB: These asserts have security implications.
let font = user_data as *const Font;
assert!(!font.is_null());
// TODO(Issue #197): reuse font table data, which will change the unsound trickery here.
match (*font).table_for_tag(tag as FontTableTag) {
None => ptr::null_mut(),
Some(font_table) => {
// `Box::into_raw` intentionally leaks the FontTable so we don't destroy the buffer
// while HarfBuzz is using it. When HarfBuzz is done with the buffer, it will pass
// this raw pointer back to `destroy_blob_func` which will deallocate the Box.
let font_table_ptr = Box::into_raw(Box::new(font_table));
let buf = (*font_table_ptr).buffer();
// HarfBuzz calls `destroy_blob_func` when the buffer is no longer needed.
let blob = hb_blob_create(
buf.as_ptr() as *const c_char,
buf.len() as c_uint,
HB_MEMORY_MODE_READONLY,
font_table_ptr as *mut c_void,
Some(destroy_blob_func),
);
assert!(!blob.is_null());
blob
},
}
}
}
extern "C" fn destroy_blob_func(font_table_ptr: *mut c_void) {
unsafe {
drop(Box::from_raw(font_table_ptr as *mut FontTable));
}
}

View File

@@ -0,0 +1,423 @@
/* 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/. */
#![allow(unsafe_code)]
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::sync::LazyLock;
use std::{char, ptr};
use app_units::Au;
use euclid::default::Point2D;
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
// shapers. For now, however, HarfBuzz is a hard dependency.
use harfbuzz_sys::{
HB_DIRECTION_LTR, HB_DIRECTION_RTL, HB_MEMORY_MODE_READONLY, HB_OT_LAYOUT_BASELINE_TAG_HANGING,
HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
hb_blob_create, hb_blob_t, hb_bool_t, hb_buffer_add_utf8, hb_buffer_create, hb_buffer_destroy,
hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions, hb_buffer_get_length,
hb_buffer_set_direction, hb_buffer_set_script, hb_buffer_t, hb_codepoint_t,
hb_face_create_for_tables, hb_face_destroy, hb_face_t, hb_feature_t, hb_font_create,
hb_font_destroy, hb_font_funcs_create, hb_font_funcs_set_glyph_h_advance_func,
hb_font_funcs_set_nominal_glyph_func, hb_font_funcs_t, hb_font_set_funcs, hb_font_set_ppem,
hb_font_set_scale, hb_font_set_variations, hb_font_t, hb_glyph_info_t, hb_glyph_position_t,
hb_ot_layout_get_baseline, hb_position_t, hb_script_from_iso15924_tag, hb_shape, hb_tag_t,
hb_variation_t,
};
use num_traits::Zero;
use super::{HarfBuzzShapedGlyphData, ShapedGlyphEntry, unicode_script_to_iso15924_tag};
use crate::platform::font::FontTable;
use crate::{
BASE, Font, FontBaseline, FontTableMethods, FontTableTag, GlyphId, GlyphStore, KERN, LIGA,
OpenTypeTableTag, ShapingFlags, ShapingOptions, fixed_to_float, float_to_fixed, ot_tag,
};
const HB_OT_TAG_DEFAULT_SCRIPT: OpenTypeTableTag = ot_tag!('D', 'F', 'L', 'T');
const HB_OT_TAG_DEFAULT_LANGUAGE: OpenTypeTableTag = ot_tag!('d', 'f', 'l', 't');
pub struct ShapedGlyphData {
count: usize,
buffer: *mut hb_buffer_t,
glyph_infos: *mut hb_glyph_info_t,
pos_infos: *mut hb_glyph_position_t,
}
impl ShapedGlyphData {
/// Create a new [`ShapedGlyphData`] from the given HarfBuzz buffer.
///
/// # Safety
///
/// - Passing an invalid buffer pointer to this function results in undefined behavior.
/// - This function takes ownership of the buffer and the ShapedGlyphData destroys the buffer when dropped
/// so the pointer must an owned pointer and must not be used after being passed to this function
unsafe fn new(buffer: *mut hb_buffer_t) -> ShapedGlyphData {
let mut glyph_count = 0;
let glyph_infos = unsafe { hb_buffer_get_glyph_infos(buffer, &mut glyph_count) };
assert!(!glyph_infos.is_null());
let mut pos_count = 0;
let pos_infos = unsafe { hb_buffer_get_glyph_positions(buffer, &mut pos_count) };
assert!(!pos_infos.is_null());
assert_eq!(glyph_count, pos_count);
ShapedGlyphData {
count: glyph_count as usize,
buffer,
glyph_infos,
pos_infos,
}
}
}
impl Drop for ShapedGlyphData {
fn drop(&mut self) {
unsafe { hb_buffer_destroy(self.buffer) }
}
}
impl HarfBuzzShapedGlyphData for ShapedGlyphData {
#[inline]
fn len(&self) -> usize {
self.count
}
#[inline(always)]
fn byte_offset_of_glyph(&self, i: usize) -> u32 {
assert!(i < self.count);
unsafe {
let glyph_info_i = self.glyph_infos.add(i);
(*glyph_info_i).cluster
}
}
/// Returns shaped glyph data for one glyph, and updates the y-position of the pen.
fn entry_for_glyph(&self, i: usize, y_pos: &mut Au) -> ShapedGlyphEntry {
assert!(i < self.count);
unsafe {
let glyph_info_i = self.glyph_infos.add(i);
let pos_info_i = self.pos_infos.add(i);
let x_offset = Shaper::fixed_to_float((*pos_info_i).x_offset);
let y_offset = Shaper::fixed_to_float((*pos_info_i).y_offset);
let x_advance = Shaper::fixed_to_float((*pos_info_i).x_advance);
let y_advance = Shaper::fixed_to_float((*pos_info_i).y_advance);
let x_offset = Au::from_f64_px(x_offset);
let y_offset = Au::from_f64_px(y_offset);
let x_advance = Au::from_f64_px(x_advance);
let y_advance = Au::from_f64_px(y_advance);
let offset = if x_offset.is_zero() && y_offset.is_zero() && y_advance.is_zero() {
None
} else {
// adjust the pen..
if y_advance > Au::zero() {
*y_pos -= y_advance;
}
Some(Point2D::new(x_offset, *y_pos - y_offset))
};
ShapedGlyphEntry {
codepoint: (*glyph_info_i).codepoint as GlyphId,
advance: x_advance,
offset,
}
}
}
}
#[derive(Debug)]
pub struct Shaper {
hb_face: *mut hb_face_t,
hb_font: *mut hb_font_t,
font: *const Font,
}
// The HarfBuzz API is thread safe as well as our `Font`, so we can make the data
// structures here as thread-safe as well. This doesn't seem to be documented,
// but was expressed as one of the original goals of the HarfBuzz API.
unsafe impl Sync for Shaper {}
unsafe impl Send for Shaper {}
impl Drop for Shaper {
fn drop(&mut self) {
unsafe {
assert!(!self.hb_face.is_null());
hb_face_destroy(self.hb_face);
assert!(!self.hb_font.is_null());
hb_font_destroy(self.hb_font);
}
}
}
impl Shaper {
pub fn new(font: &Font) -> Shaper {
unsafe {
let hb_face: *mut hb_face_t = hb_face_create_for_tables(
Some(font_table_func),
font as *const Font as *mut c_void,
None,
);
let hb_font: *mut hb_font_t = hb_font_create(hb_face);
// Set points-per-em. if zero, performs no hinting in that direction.
let pt_size = font.descriptor.pt_size.to_f64_px();
hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
// Set scaling. Note that this takes 16.16 fixed point.
hb_font_set_scale(
hb_font,
Shaper::float_to_fixed(pt_size) as c_int,
Shaper::float_to_fixed(pt_size) as c_int,
);
// configure static function callbacks.
hb_font_set_funcs(
hb_font,
HB_FONT_FUNCS.0,
font as *const Font as *mut c_void,
None,
);
if servo_config::pref!(layout_variable_fonts_enabled) {
let variations = &font.variations();
if !variations.is_empty() {
let variations: Vec<_> = variations
.iter()
.map(|variation| hb_variation_t {
tag: variation.tag,
value: variation.value,
})
.collect();
hb_font_set_variations(hb_font, variations.as_ptr(), variations.len() as u32);
}
}
Shaper {
hb_face,
hb_font,
font,
}
}
}
/// Calculate the layout metrics associated with the given text with the [`Shaper`]s font.
fn shaped_glyph_data(&self, text: &str, options: &ShapingOptions) -> ShapedGlyphData {
unsafe {
let hb_buffer: *mut hb_buffer_t = hb_buffer_create();
hb_buffer_set_direction(
hb_buffer,
if options.flags.contains(ShapingFlags::RTL_FLAG) {
HB_DIRECTION_RTL
} else {
HB_DIRECTION_LTR
},
);
let script =
hb_script_from_iso15924_tag(unicode_script_to_iso15924_tag(options.script));
hb_buffer_set_script(hb_buffer, script);
hb_buffer_add_utf8(
hb_buffer,
text.as_ptr() as *const c_char,
text.len() as c_int,
0,
text.len() as c_int,
);
let mut features = Vec::new();
if options
.flags
.contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG)
{
features.push(hb_feature_t {
tag: LIGA,
value: 0,
start: 0,
end: hb_buffer_get_length(hb_buffer),
})
}
if options
.flags
.contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
{
features.push(hb_feature_t {
tag: KERN,
value: 0,
start: 0,
end: hb_buffer_get_length(hb_buffer),
})
}
hb_shape(
self.hb_font,
hb_buffer,
features.as_mut_ptr(),
features.len() as u32,
);
ShapedGlyphData::new(hb_buffer)
}
}
fn font(&self) -> &Font {
unsafe { &(*self.font) }
}
pub fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
let glyph_data = self.shaped_glyph_data(text, options);
let font = self.font();
super::shape_text_harfbuzz(&glyph_data, font, text, options, glyphs);
}
pub fn baseline(&self) -> Option<FontBaseline> {
unsafe { (*self.font).table_for_tag(BASE)? };
let mut hanging_baseline = 0;
let mut alphabetic_baseline = 0;
let mut ideographic_baseline = 0;
unsafe {
hb_ot_layout_get_baseline(
self.hb_font,
HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
HB_DIRECTION_LTR,
HB_OT_TAG_DEFAULT_SCRIPT,
HB_OT_TAG_DEFAULT_LANGUAGE,
&mut alphabetic_baseline as *mut _,
);
hb_ot_layout_get_baseline(
self.hb_font,
HB_OT_LAYOUT_BASELINE_TAG_HANGING,
HB_DIRECTION_LTR,
HB_OT_TAG_DEFAULT_SCRIPT,
HB_OT_TAG_DEFAULT_LANGUAGE,
&mut hanging_baseline as *mut _,
);
hb_ot_layout_get_baseline(
self.hb_font,
HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
HB_DIRECTION_LTR,
HB_OT_TAG_DEFAULT_SCRIPT,
HB_OT_TAG_DEFAULT_LANGUAGE,
&mut ideographic_baseline as *mut _,
);
}
Some(FontBaseline {
ideographic_baseline: Shaper::fixed_to_float(ideographic_baseline) as f32,
alphabetic_baseline: Shaper::fixed_to_float(alphabetic_baseline) as f32,
hanging_baseline: Shaper::fixed_to_float(hanging_baseline) as f32,
})
}
fn float_to_fixed(f: f64) -> i32 {
float_to_fixed(16, f)
}
fn fixed_to_float(i: hb_position_t) -> f64 {
fixed_to_float(16, i)
}
}
/// Callbacks from Harfbuzz when font map and glyph advance lookup needed.
struct FontFuncs(*mut hb_font_funcs_t);
unsafe impl Sync for FontFuncs {}
unsafe impl Send for FontFuncs {}
static HB_FONT_FUNCS: LazyLock<FontFuncs> = LazyLock::new(|| unsafe {
let hb_funcs = hb_font_funcs_create();
hb_font_funcs_set_nominal_glyph_func(hb_funcs, Some(glyph_func), ptr::null_mut(), None);
hb_font_funcs_set_glyph_h_advance_func(
hb_funcs,
Some(glyph_h_advance_func),
ptr::null_mut(),
None,
);
FontFuncs(hb_funcs)
});
extern "C" fn glyph_func(
_: *mut hb_font_t,
font_data: *mut c_void,
unicode: hb_codepoint_t,
glyph: *mut hb_codepoint_t,
_: *mut c_void,
) -> hb_bool_t {
let font: *const Font = font_data as *const Font;
assert!(!font.is_null());
match unsafe { (*font).glyph_index(char::from_u32(unicode).unwrap()) } {
Some(g) => {
unsafe { *glyph = g as hb_codepoint_t };
true as hb_bool_t
},
None => false as hb_bool_t,
}
}
extern "C" fn glyph_h_advance_func(
_: *mut hb_font_t,
font_data: *mut c_void,
glyph: hb_codepoint_t,
_: *mut c_void,
) -> hb_position_t {
let font: *mut Font = font_data as *mut Font;
assert!(!font.is_null());
let advance = unsafe { (*font).glyph_h_advance(glyph as GlyphId) };
Shaper::float_to_fixed(advance)
}
/// Callback to get a font table out of a font.
extern "C" fn font_table_func(
_: *mut hb_face_t,
tag: hb_tag_t,
user_data: *mut c_void,
) -> *mut hb_blob_t {
// NB: These asserts have security implications.
let font = user_data as *const Font;
assert!(!font.is_null());
// TODO(Issue #197): reuse font table data, which will change the unsound trickery here.
let Some(font_table) = (unsafe { (*font).table_for_tag(tag as FontTableTag) }) else {
return ptr::null_mut();
};
// `Box::into_raw` intentionally leaks the FontTable so we don't destroy the buffer
// while HarfBuzz is using it. When HarfBuzz is done with the buffer, it will pass
// this raw pointer back to `destroy_blob_func` which will deallocate the Box.
let font_table_ptr = Box::into_raw(Box::new(font_table));
let buf = unsafe { (*font_table_ptr).buffer() };
// HarfBuzz calls `destroy_blob_func` when the buffer is no longer needed.
let blob = unsafe {
hb_blob_create(
buf.as_ptr() as *const c_char,
buf.len() as c_uint,
HB_MEMORY_MODE_READONLY,
font_table_ptr as *mut c_void,
Some(destroy_blob_func),
)
};
assert!(!blob.is_null());
blob
}
extern "C" fn destroy_blob_func(font_table_ptr: *mut c_void) {
unsafe {
drop(Box::from_raw(font_table_ptr as *mut FontTable));
}
}

View File

@@ -0,0 +1,252 @@
/* 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 app_units::Au;
use euclid::default::Point2D;
use harfrust::{
Feature, FontRef as HarfRustFontRef, GlyphBuffer, Script, ShaperData, ShaperInstance, Tag,
UnicodeBuffer, Variation,
};
use num_traits::Zero as _;
use read_fonts::TableProvider;
use read_fonts::types::BigEndian;
use smallvec::SmallVec;
use super::{HarfBuzzShapedGlyphData, ShapedGlyphEntry, unicode_script_to_iso15924_tag};
use crate::{Font, FontBaseline, FontData, GlyphStore, ShapingFlags, ShapingOptions};
/// Convert a `webrender_api::FontVariation` to a `harfrust::Variation`
fn wr_variation_to_hr_varation(wr_variation: webrender_api::FontVariation) -> harfrust::Variation {
Variation {
tag: Tag::from_u32(wr_variation.tag),
value: wr_variation.value,
}
}
pub struct ShapedGlyphData {
data: GlyphBuffer,
scale: f64,
}
impl HarfBuzzShapedGlyphData for ShapedGlyphData {
fn len(&self) -> usize {
self.data.len()
}
fn byte_offset_of_glyph(&self, i: usize) -> u32 {
self.data.glyph_infos()[i].cluster
}
fn entry_for_glyph(&self, i: usize, y_pos: &mut app_units::Au) -> super::ShapedGlyphEntry {
let glyph_info_i = self.data.glyph_infos()[i];
let pos_info_i = self.data.glyph_positions()[i];
let x_offset = Au::from_f64_px(pos_info_i.x_offset as f64 * self.scale);
let y_offset = Au::from_f64_px(pos_info_i.y_offset as f64 * self.scale);
let x_advance = Au::from_f64_px(pos_info_i.x_advance as f64 * self.scale);
let y_advance = Au::from_f64_px(pos_info_i.y_advance as f64 * self.scale);
let offset = if x_offset.is_zero() && y_offset.is_zero() && y_advance.is_zero() {
None
} else {
// adjust the pen..
if y_advance > Au::zero() {
*y_pos -= y_advance;
}
Some(Point2D::new(x_offset, *y_pos - y_offset))
};
ShapedGlyphEntry {
codepoint: glyph_info_i.glyph_id,
advance: x_advance,
offset,
}
}
}
pub struct Shaper {
font: *const Font,
/// The raw byte data of the font
font_data: FontData,
/// The index of a font in it's collection (.ttc)
/// If the font file is not a collection then this is 0
font_index: u32,
// Used for scaling HarfRust's output
scale: f64,
ppem: f64,
/// Pre-computed data for shaping a font
shaper_data: ShaperData,
/// Pre-computed data for shaping a variable font with a particular set of variations.
/// If there are no variations then we don't create a ShaperInstance.
shaper_instance: Option<ShaperInstance>,
}
// `Font` and `FontData` are both threadsafe, so we can make the data structures here as thread-safe as well.
#[allow(unsafe_code)]
unsafe impl Sync for Shaper {}
#[allow(unsafe_code)]
unsafe impl Send for Shaper {}
impl Shaper {
pub(crate) fn new(font: &Font) -> Self {
let raw_font = font
.font_data_and_index()
.expect("Error creating shaper for font");
let font_data = raw_font.data.clone();
let font_index = raw_font.index;
// Set points-per-em. if zero, performs no hinting in that direction
let ppem = font.descriptor.pt_size.to_f64_px();
let font_ref = read_fonts::FontRef::from_index(font_data.as_ref(), font_index).unwrap();
let units_per_em = font_ref.head().unwrap().units_per_em();
let scale = ppem / (units_per_em as f64);
// Create cached shaping data for the font
let hr_font = HarfRustFontRef::from_index(font_data.as_ref(), font_index).unwrap();
let shaper_data = ShaperData::new(&hr_font);
// If variable fonts are enabled and the font has variations, create a ShaperInstance to set on the shaper.
let variations = font.variations();
let shaper_instance =
if servo_config::pref!(layout_variable_fonts_enabled) && !variations.is_empty() {
let variations_iter = variations.iter().copied().map(wr_variation_to_hr_varation);
Some(ShaperInstance::from_variations(&hr_font, variations_iter))
} else {
None
};
Self {
font: font as *const Font,
font_data,
font_index,
scale,
ppem,
shaper_data,
shaper_instance,
}
}
}
impl Shaper {
fn shaped_glyph_data(&self, text: &str, options: &crate::ShapingOptions) -> ShapedGlyphData {
let mut buffer = UnicodeBuffer::new();
// Set direction
buffer.set_direction(if options.flags.contains(ShapingFlags::RTL_FLAG) {
harfrust::Direction::RightToLeft
} else {
harfrust::Direction::LeftToRight
});
// Set script
let script_tag = Tag::from_u32(unicode_script_to_iso15924_tag(options.script));
let script = Script::from_iso15924_tag(script_tag).unwrap();
buffer.set_script(script);
// Push text
buffer.push_str(text);
// Features
let mut features = SmallVec::<[Feature; 2]>::new();
if options
.flags
.contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG)
{
features.push(Feature::new(Tag::new(b"liga"), 0, ..));
}
if options
.flags
.contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
{
features.push(Feature::new(Tag::new(b"kern"), 0, ..));
}
let hr_font =
HarfRustFontRef::from_index(self.font_data.as_ref(), self.font_index).unwrap();
let shaper = self
.shaper_data
.shaper(&hr_font)
.instance(self.shaper_instance.as_ref())
.build();
let glyph_buffer = shaper.shape(buffer, &features);
ShapedGlyphData {
data: glyph_buffer,
scale: self.scale,
}
}
#[allow(unsafe_code)]
fn font(&self) -> &Font {
// SAFETY: the font actually owns this shaper so it cannot have been dropped
unsafe { &(*self.font) }
}
pub fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
let glyph_data = self.shaped_glyph_data(text, options);
let font = self.font();
super::shape_text_harfbuzz(&glyph_data, font, text, options, glyphs);
}
pub fn baseline(&self) -> Option<crate::FontBaseline> {
let font_ref =
read_fonts::FontRef::from_index(self.font_data.as_ref(), self.font_index).unwrap();
// Load the horizontal axis of the BASE table
let base_table = font_ref.base().ok()?;
let horiz_axis = base_table.horiz_axis()?.ok()?;
// Get the index of each baseline tag in the tag list
let tag_list = horiz_axis.base_tag_list()?.ok()?;
let baseline_tags = tag_list.baseline_tags();
let romn_tag_idx = baseline_tags
.binary_search(&BigEndian::from(Tag::new(b"romn")))
.ok();
let hang_tag_idx = baseline_tags
.binary_search(&BigEndian::from(Tag::new(b"hang")))
.ok();
let ideo_tag_idx = baseline_tags
.binary_search(&BigEndian::from(Tag::new(b"ideo")))
.ok();
// Bail early if none of the baseline tags exist in the tag list
if romn_tag_idx.is_none() && hang_tag_idx.is_none() && ideo_tag_idx.is_none() {
return None;
}
// Find the DFLT (default) script record
let script_list = horiz_axis.base_script_list().ok()?;
let script_records = script_list.base_script_records();
let default_script_record_idx = script_records
.binary_search_by_key(&Tag::from_be_bytes(*b"DFLT"), |record| {
record.base_script_tag()
})
.ok()?;
let default_script_record = &script_records[default_script_record_idx];
// Lookup the baseline coordinates DFLT script record
let base_script = default_script_record
.base_script(script_list.offset_data())
.ok()?;
let base_script_values = base_script.base_values()?.ok()?;
let base_coords = base_script_values.base_coords();
// Search for the baseline corresponding to a given baseline index
let get_coord = |idx: usize| -> Option<f32> {
base_coords
.get(idx)
.ok()
.map(|coord| coord.coordinate() as f32 * self.scale as f32)
};
Some(FontBaseline {
ideographic_baseline: ideo_tag_idx.and_then(get_coord).unwrap_or(0.0),
alphabetic_baseline: romn_tag_idx.and_then(get_coord).unwrap_or(0.0),
hanging_baseline: hang_tag_idx.and_then(get_coord).unwrap_or(0.0),
})
}
}

View File

@@ -0,0 +1,227 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cmp;
use app_units::Au;
use base::text::is_bidi_control;
use euclid::default::Point2D;
use fonts_traits::ByteIndex;
use log::debug;
use num_traits::Zero as _;
use crate::{Font, GlyphData, GlyphId, GlyphStore, ShapingOptions, advance_for_shaped_glyph};
#[cfg(feature = "harfbuzz")]
mod harfbuzz;
#[cfg(feature = "harfbuzz")]
pub use harfbuzz::Shaper;
#[cfg(feature = "harfrust")]
mod harfrust;
#[cfg(all(feature = "harfrust", not(feature = "harfbuzz")))]
pub use harfrust::Shaper;
const NO_GLYPH: i32 = -1;
/// Utility function to convert a `unicode_script::Script` enum into the corresponding `c_uint` tag that
/// harfbuzz uses to represent unicode scipts.
fn unicode_script_to_iso15924_tag(script: unicode_script::Script) -> u32 {
let bytes: [u8; 4] = match script {
unicode_script::Script::Unknown => *b"Zzzz",
_ => {
let short_name = script.short_name();
short_name.as_bytes().try_into().unwrap()
},
};
u32::from_be_bytes(bytes)
}
struct ShapedGlyphEntry {
codepoint: GlyphId,
advance: Au,
offset: Option<Point2D<Au>>,
}
/// Holds the results of shaping. Abstracts over HarfBuzz and HarfRust which return data in very similar
/// form but with different types
trait HarfBuzzShapedGlyphData {
/// The number of shaped glyphs
fn len(&self) -> usize;
/// The byte offset of the shaped glyph in the souce text
fn byte_offset_of_glyph(&self, i: usize) -> u32;
/// Returns shaped glyph data for one glyph, and updates the y-position of the pen.
fn entry_for_glyph(&self, i: usize, y_pos: &mut Au) -> ShapedGlyphEntry;
}
/// Shape text using an `impl HarfBuzzShaper`
fn shape_text_harfbuzz<ShapedGlyphData: HarfBuzzShapedGlyphData>(
glyph_data: &ShapedGlyphData,
font: &Font,
text: &str,
options: &ShapingOptions,
glyphs: &mut GlyphStore,
) {
let glyph_count = glyph_data.len();
let byte_max = text.len();
debug!(
"Shaped text[byte count={}], got back {} glyph info records.",
byte_max, glyph_count
);
// make map of what chars have glyphs
let mut byte_to_glyph = vec![NO_GLYPH; byte_max];
debug!("(glyph idx) -> (text byte offset)");
for i in 0..glyph_data.len() {
let loc = glyph_data.byte_offset_of_glyph(i) as usize;
if loc < byte_max {
byte_to_glyph[loc] = i as i32;
} else {
debug!(
"ERROR: tried to set out of range byte_to_glyph: idx={}, glyph idx={}",
loc, i
);
}
debug!("{} -> {}", i, loc);
}
debug!("text: {:?}", text);
debug!("(char idx): char->(glyph index):");
for (i, ch) in text.char_indices() {
debug!("{}: {:?} --> {}", i, ch, byte_to_glyph[i]);
}
let mut glyph_span = 0..0;
let mut byte_range = 0..0;
let mut y_pos = Au::zero();
// main loop over each glyph. each iteration usually processes 1 glyph and 1+ chars.
// in cases with complex glyph-character associations, 2+ glyphs and 1+ chars can be
// processed.
while glyph_span.start < glyph_count {
debug!("Processing glyph at idx={}", glyph_span.start);
glyph_span.end = glyph_span.start;
byte_range.end = glyph_data.byte_offset_of_glyph(glyph_span.start) as usize;
while byte_range.end < byte_max {
byte_range.end += 1;
// Extend the byte range to include any following byte without its own glyph.
while byte_range.end < byte_max && byte_to_glyph[byte_range.end] == NO_GLYPH {
byte_range.end += 1;
}
// Extend the glyph range to include all glyphs covered by bytes processed so far.
let mut max_glyph_idx = glyph_span.end;
for glyph_idx in &byte_to_glyph[byte_range.clone()] {
if *glyph_idx != NO_GLYPH {
max_glyph_idx = cmp::max(*glyph_idx as usize + 1, max_glyph_idx);
}
}
if max_glyph_idx > glyph_span.end {
glyph_span.end = max_glyph_idx;
debug!("Extended glyph span to {:?}", glyph_span);
}
// if there's just one glyph, then we don't need further checks.
if glyph_span.len() == 1 {
break;
}
// if no glyphs were found yet, extend the char byte range more.
if glyph_span.is_empty() {
continue;
}
// If byte_range now includes all the byte offsets found in glyph_span, then we
// have found a contiguous "cluster" and can stop extending it.
let mut all_glyphs_are_within_cluster: bool = true;
for j in glyph_span.clone() {
let loc = glyph_data.byte_offset_of_glyph(j) as usize;
if !(byte_range.start <= loc && loc < byte_range.end) {
all_glyphs_are_within_cluster = false;
break;
}
}
if all_glyphs_are_within_cluster {
break;
}
// Otherwise, the bytes we have seen so far correspond to a non-contiguous set of
// glyphs. Keep extending byte_range until we fill in all the holes in the glyph
// span or reach the end of the text.
}
assert!(!byte_range.is_empty());
assert!(!glyph_span.is_empty());
// Now byte_range is the ligature clump formed by the glyphs in glyph_span.
// We will save these glyphs to the glyph store at the index of the first byte.
let byte_idx = ByteIndex(byte_range.start as isize);
if glyph_span.len() == 1 {
// Fast path: 1-to-1 mapping of byte offset to single glyph.
//
// TODO(Issue #214): cluster ranges need to be computed before
// shaping, and then consulted here.
// for now, just pretend that every character is a cluster start.
// (i.e., pretend there are no combining character sequences).
// 1-to-1 mapping of character to glyph also treated as ligature start.
//
// NB: When we acquire the ability to handle ligatures that cross word boundaries,
// we'll need to do something special to handle `word-spacing` properly.
let character = text[byte_range.clone()].chars().next().unwrap();
if is_bidi_control(character) {
// Don't add any glyphs for bidi control chars
} else {
let (glyph_id, advance, offset) = if character == '\t' {
// Treat tabs in pre-formatted text as a fixed number of spaces. The glyph id does
// not matter here as Servo doesn't render any glyphs for whitespace.
//
// TODO: Proper tab stops. This should happen in layout and be based on the
// size of the space character of the inline formatting context.
(
font.glyph_index(' ').unwrap_or(0),
font.metrics.space_advance * 8,
Default::default(),
)
} else {
let shape = glyph_data.entry_for_glyph(glyph_span.start, &mut y_pos);
let advance = advance_for_shaped_glyph(shape.advance, character, options);
(shape.codepoint, advance, shape.offset)
};
let data = GlyphData::new(glyph_id, advance, offset, true, true);
glyphs.add_glyph_for_byte_index(byte_idx, character, &data);
}
} else {
// collect all glyphs to be assigned to the first character.
let mut datas = vec![];
for glyph_i in glyph_span.clone() {
let shape = glyph_data.entry_for_glyph(glyph_i, &mut y_pos);
datas.push(GlyphData::new(
shape.codepoint,
shape.advance,
shape.offset,
true, // treat as cluster start
glyph_i > glyph_span.start,
));
// all but first are ligature continuations
}
// now add the detailed glyph entry.
glyphs.add_glyphs_for_byte_index(byte_idx, &datas);
}
glyph_span.start = glyph_span.end;
byte_range.start = byte_range.end;
}
// this must be called after adding all glyph data; it sorts the
// lookup table for finding detailed glyphs by associated char index.
glyphs.finalize_changes();
}

View File

@@ -28,7 +28,7 @@ use style::values::computed::font::{
};
use style::values::computed::{FontStretch, FontWeight};
use style::values::specified::FontStretch as SpecifiedFontStretch;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::FontDescriptor;
use crate::font_store::FontStore;
@@ -65,6 +65,7 @@ pub enum SystemFontServiceMessage {
FontIdentifier,
Au,
FontInstanceFlags,
Vec<FontVariation>,
IpcSender<FontInstanceKey>,
),
GetFontKey(IpcSender<FontKey>),
@@ -94,7 +95,7 @@ pub struct SystemFontService {
local_families: FontStore,
compositor_api: CrossProcessCompositorApi,
webrender_fonts: HashMap<FontIdentifier, FontKey>,
font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
font_instances: HashMap<(FontKey, Au, Vec<FontVariation>), FontInstanceKey>,
generic_fonts: ResolvedGenericFontFamilies,
/// This is an optimization that allows the [`SystemFontService`] to send font data to
@@ -176,8 +177,15 @@ impl SystemFontService {
let _ =
result_sender.send(self.get_font_templates(font_descriptor, font_family));
},
SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => {
let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
SystemFontServiceMessage::GetFontInstance(
identifier,
pt_size,
flags,
variations,
result,
) => {
let _ =
result.send(self.get_font_instance(identifier, pt_size, flags, variations));
},
SystemFontServiceMessage::GetFontKey(result_sender) => {
self.fetch_new_keys();
@@ -281,6 +289,7 @@ impl SystemFontService {
identifier: FontIdentifier,
pt_size: Au,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey {
self.fetch_new_keys();
@@ -301,7 +310,7 @@ impl SystemFontService {
*self
.font_instances
.entry((font_key, pt_size))
.entry((font_key, pt_size, variations.clone()))
.or_insert_with(|| {
let font_instance_key = self.free_font_instance_keys.pop().unwrap();
compositor_api.add_font_instance(
@@ -309,6 +318,7 @@ impl SystemFontService {
font_key,
pt_size.to_f32_px(),
flags,
variations,
);
font_instance_key
})
@@ -363,7 +373,7 @@ struct FontTemplateCacheKey {
/// The public interface to the [`SystemFontService`], used by per-Document
/// `FontContext` instances.
#[derive(Debug)]
#[derive(Debug, MallocSizeOf)]
pub struct SystemFontServiceProxy {
sender: Mutex<IpcSender<SystemFontServiceMessage>>,
templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
@@ -473,6 +483,7 @@ impl SystemFontServiceProxy {
identifier: FontIdentifier,
size: Au,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) -> FontInstanceKey {
let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
self.sender
@@ -481,6 +492,7 @@ impl SystemFontServiceProxy {
identifier,
size,
flags,
variations,
response_chan,
))
.expect("failed to send message to system font service");

View File

@@ -27,7 +27,7 @@ fn make_font(path: PathBuf) -> Font {
let data = FontData::from_bytes(&bytes);
let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path).unwrap());
let platform_font = PlatformFont::new_from_data(identifier.clone(), &data, None).unwrap();
let platform_font = PlatformFont::new_from_data(identifier.clone(), &data, None, &[]).unwrap();
let template = FontTemplate {
identifier,
@@ -40,6 +40,7 @@ fn make_font(path: PathBuf) -> Font {
style: FontStyle::normal(),
variant: FontVariantCaps::Normal,
pt_size: Au::from_px(24),
variation_settings: vec![],
};
Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap()
}

View File

@@ -9,8 +9,8 @@ mod font_context {
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::{Arc, Once};
use std::thread;
use app_units::Au;
@@ -23,7 +23,7 @@ mod font_context {
SystemFontServiceProxySender, fallback_font_families,
};
use ipc_channel::ipc::{self, IpcReceiver};
use net_traits::ResourceThreads;
use net_traits::{ResourceThreads, start_fetch_thread};
use parking_lot::Mutex;
use servo_arc::Arc as ServoArc;
use style::ArcSlice;
@@ -36,6 +36,8 @@ mod font_context {
use stylo_atoms::Atom;
use webrender_api::{FontInstanceKey, FontKey, IdNamespace};
static INIT: Once = Once::new();
struct TestContext {
context: FontContext,
system_font_service: Arc<MockSystemFontService>,
@@ -53,6 +55,9 @@ mod font_context {
let mock_compositor_api = CrossProcessCompositorApi::dummy();
let proxy_clone = Arc::new(system_font_service_proxy.to_sender().to_proxy());
INIT.call_once(|| {
start_fetch_thread();
});
Self {
context: FontContext::new(proxy_clone, mock_compositor_api, mock_resource_threads),
system_font_service,
@@ -128,7 +133,7 @@ mod font_context {
);
},
SystemFontServiceMessage::GetFontInstanceKey(result_sender) |
SystemFontServiceMessage::GetFontInstance(_, _, _, result_sender) => {
SystemFontServiceMessage::GetFontInstance(_, _, _, _, result_sender) => {
let _ = result_sender.send(FontInstanceKey(IdNamespace(0), 0));
},
SystemFontServiceMessage::GetFontKey(result_sender) => {
@@ -182,9 +187,12 @@ mod font_context {
path: path.to_str().expect("Could not load test font").into(),
variation_index: 0,
};
let handle =
PlatformFont::new_from_local_font_identifier(local_font_identifier.clone(), None)
.expect("Could not load test font");
let handle = PlatformFont::new_from_local_font_identifier(
local_font_identifier.clone(),
None,
&[],
)
.expect("Could not load test font");
family.add_template(FontTemplate::new(
FontIdentifier::Local(local_font_identifier),
@@ -347,6 +355,7 @@ mod font_context {
style: FontStyle::normal(),
variant: FontVariantCaps::Normal,
pt_size: Au(10),
variation_settings: vec![],
};
let family = SingleFontFamily::FamilyName(FamilyName {

View File

@@ -36,7 +36,7 @@ fn test_font_template_descriptor() {
.unwrap();
let data = FontData::from_bytes(&bytes);
let handle = PlatformFont::new_from_data(identifier, &data, None).unwrap();
let handle = PlatformFont::new_from_data(identifier, &data, None, &[]).unwrap();
handle.descriptor()
}

View File

@@ -16,4 +16,5 @@ app_units = { workspace = true }
euclid = { workspace = true }
malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
webrender = { workspace = true }
webrender_api = { workspace = true }

View File

@@ -8,9 +8,10 @@ use app_units::{Au, MAX_AU, MIN_AU};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
use euclid::{Box2D, Length, Point2D, Scale, SideOffsets2D, Size2D, Vector2D};
use malloc_size_of_derive::MallocSizeOf;
use webrender::FastTransform;
use webrender_api::units::{
DeviceIntRect, DeviceIntSize, DevicePixel, FramebufferPixel, LayoutPoint, LayoutRect,
LayoutSize,
DeviceIntRect, DeviceIntSize, DevicePixel, FramebufferPixel, LayoutPixel, LayoutPoint,
LayoutRect, LayoutSize,
};
// Units for use with euclid::length and euclid::scale_factor.
@@ -46,6 +47,8 @@ pub type DeviceIndependentPoint = Point2D<f32, DeviceIndependentPixel>;
pub type DeviceIndependentVector2D = Vector2D<f32, DeviceIndependentPixel>;
pub type DeviceIndependentSize = Size2D<f32, DeviceIndependentPixel>;
pub type FastLayoutTransform = FastTransform<LayoutPixel, LayoutPixel>;
// An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was
// originally proposed in 2002 as a standard unit of measure in Gecko.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info.

View File

@@ -431,6 +431,10 @@ impl Serialize for Ser<'_, HeaderMap> {
{
let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
for v in self.0 {
#[allow(
clippy::collapsible_if,
reason = "let chains are not available in 1.85"
)]
if self.1 {
if let Ok(v) = str::from_utf8(v) {
serializer.serialize_element(v)?;

View File

@@ -22,12 +22,12 @@ atomic_refcell = { workspace = true }
base = { workspace = true }
bitflags = { workspace = true }
compositing_traits = { workspace = true }
constellation_traits = { workspace = true }
cssparser = { workspace = true }
data-url = { workspace = true }
embedder_traits = { workspace = true }
euclid = { workspace = true }
fnv = { workspace = true }
fonts = { path = "../fonts" }
fonts = { path = "../fonts", default-features = false }
fonts_traits = { workspace = true }
fxhash = { workspace = true }
html5ever = { workspace = true }
@@ -35,6 +35,7 @@ icu_locid = { workspace = true }
icu_segmenter = { workspace = true }
ipc-channel = { workspace = true }
itertools = { workspace = true }
kurbo = { workspace = true }
layout_api = { workspace = true }
log = { workspace = true }
malloc_size_of = { workspace = true }

View File

@@ -69,10 +69,10 @@ impl<'dom> ModernContainerJob<'dom> {
BlockContainer::InlineFormattingContext(inline_formatting_context),
);
let info: &NodeAndStyleInfo = anonymous_info;
let formatting_context = IndependentFormattingContext {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
contents: IndependentFormattingContextContents::Flow(block_formatting_context),
};
let formatting_context = IndependentFormattingContext::new(
LayoutBoxBase::new(info.into(), info.style.clone()),
IndependentFormattingContextContents::Flow(block_formatting_context),
);
Some(ModernItem {
kind: ModernItemKind::InFlow(formatting_context),
@@ -228,7 +228,7 @@ impl<'a, 'dom> ModernContainerBuilder<'a, 'dom> {
let anonymous_info = LazyLock::new(|| {
self.info
.pseudo(self.context, PseudoElement::ServoAnonymousBox)
.with_pseudo_element(self.context, PseudoElement::ServoAnonymousBox)
.expect("Should always be able to construct info for anonymous boxes.")
});

View File

@@ -4,10 +4,12 @@
use std::sync::Arc;
use embedder_traits::UntrustedNodeAddress;
use euclid::Size2D;
use fnv::FnvHashMap;
use fonts::FontContext;
use fxhash::FxHashMap;
use layout_api::wrapper_traits::ThreadSafeLayoutNode;
use layout_api::{
IFrameSizes, ImageAnimationState, PendingImage, PendingImageState, PendingRasterizationImage,
};
@@ -17,6 +19,7 @@ use net_traits::image_cache::{
};
use parking_lot::{Mutex, RwLock};
use pixels::RasterImage;
use script::layout_dom::ServoThreadSafeLayoutNode;
use servo_url::{ImmutableOrigin, ServoUrl};
use style::context::SharedStyleContext;
use style::dom::OpaqueNode;
@@ -56,13 +59,11 @@ pub enum ResolvedImage<'a> {
pub enum ResolveImageError {
LoadError,
ImagePending,
ImageRequested,
OnlyMetadata,
InvalidUrl,
MissingNode,
ImageMissingFromImageSet,
FailedToResolveImageFromImageSet,
NotImplementedYet(&'static str),
NotImplementedYet,
None,
}
@@ -86,6 +87,13 @@ pub(crate) struct ImageResolver {
/// size determined by layout. This will be shared with the script thread.
pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
/// A list of `SVGSVGElement`s encountered during layout that are not
/// serialized yet. This is needed to support inline SVGs as they are treated
/// as replaced elements and the layout is responsible for triggering the
/// network load for the corresponding serialzed data: urls (similar to
/// background images).
pub pending_svg_elements_for_serialization: Mutex<Vec<UntrustedNodeAddress>>,
/// A shared reference to script's map of DOM nodes with animated images. This is used
/// to manage image animations in script and inform the script about newly animating
/// nodes.
@@ -105,6 +113,11 @@ impl Drop for ImageResolver {
if !std::thread::panicking() {
assert!(self.pending_images.lock().is_empty());
assert!(self.pending_rasterization_images.lock().is_empty());
assert!(
self.pending_svg_elements_for_serialization
.lock()
.is_empty()
);
}
}
}
@@ -174,7 +187,7 @@ impl ImageResolver {
}
}
fn get_cached_image_for_url(
pub(crate) fn get_cached_image_for_url(
&self,
node: OpaqueNode,
url: ServoUrl,
@@ -234,6 +247,15 @@ impl ImageResolver {
result
}
pub(crate) fn queue_svg_element_for_serialization(
&self,
element: ServoThreadSafeLayoutNode<'_>,
) {
self.pending_svg_elements_for_serialization
.lock()
.push(element.opaque().into())
}
pub(crate) fn resolve_image<'a>(
&self,
node: Option<OpaqueNode>,
@@ -242,10 +264,8 @@ impl ImageResolver {
match image {
// TODO: Add support for PaintWorklet and CrossFade rendering.
Image::None => Result::Err(ResolveImageError::None),
Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet("CrossFade")),
Image::PaintWorklet(_) => {
Result::Err(ResolveImageError::NotImplementedYet("PaintWorklet"))
},
Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet),
Image::PaintWorklet(_) => Result::Err(ResolveImageError::NotImplementedYet),
Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
Image::Url(image_url) => {
// FIXME: images wont always have in intrinsic width or

View File

@@ -65,7 +65,11 @@ impl<'a> BackgroundPainter<'a> {
if &BackgroundAttachment::Fixed ==
get_cyclic(&background.background_attachment.0, layer_index)
{
return builder.compositor_info.viewport_size.into();
return builder
.compositor_info
.viewport_details
.layout_size()
.into();
}
match get_cyclic(&background.background_clip.0, layer_index) {
@@ -149,7 +153,11 @@ impl<'a> BackgroundPainter<'a> {
Origin::PaddingBox => *fragment_builder.padding_rect(),
Origin::BorderBox => fragment_builder.border_rect,
},
BackgroundAttachment::Fixed => builder.compositor_info.viewport_size.into(),
BackgroundAttachment::Fixed => builder
.compositor_info
.viewport_details
.layout_size()
.into(),
}
}
}

View File

@@ -4,6 +4,7 @@
use app_units::Au;
use base::id::ScrollTreeNodeId;
use malloc_size_of_derive::MallocSizeOf;
use style::values::computed::basic_shape::{BasicShape, ClipPath};
use style::values::computed::length_percentage::NonNegativeLengthPercentage;
use style::values::computed::position::Position;
@@ -16,7 +17,7 @@ use super::{BuilderForBoxFragment, compute_margin_box_radius, normalize_radii};
/// An identifier for a clip used during StackingContextTree construction. This is a simple index in
/// a [`ClipStore`]s vector of clips.
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub(crate) struct ClipId(pub usize);
impl ClipId {
@@ -27,7 +28,7 @@ impl ClipId {
/// All the information needed to create a clip on a WebRender display list. These are created at
/// two times: during `StackingContextTree` creation and during WebRender display list construction.
/// Only the former are stored in a [`ClipStore`].
#[derive(Clone)]
#[derive(Clone, MallocSizeOf)]
pub(crate) struct Clip {
pub id: ClipId,
pub radii: BorderRadius,
@@ -39,10 +40,14 @@ pub(crate) struct Clip {
/// A simple vector of [`Clip`] that is built during `StackingContextTree` construction.
/// These are later turned into WebRender clips and clip chains during WebRender display
/// list construction.
#[derive(Clone, Default)]
#[derive(Clone, Default, MallocSizeOf)]
pub(crate) struct StackingContextTreeClipStore(pub Vec<Clip>);
impl StackingContextTreeClipStore {
pub(super) fn get(&self, clip_id: ClipId) -> &Clip {
&self.0[clip_id.0]
}
pub(crate) fn add(
&mut self,
radii: webrender_api::BorderRadius,

View File

@@ -0,0 +1,415 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use app_units::Au;
use base::id::ScrollTreeNodeId;
use embedder_traits::Cursor;
use euclid::{Box2D, Vector2D};
use kurbo::{Ellipse, Shape};
use layout_api::{ElementsFromPointFlags, ElementsFromPointResult};
use servo_geometry::FastLayoutTransform;
use style::computed_values::backface_visibility::T as BackfaceVisibility;
use style::computed_values::pointer_events::T as PointerEvents;
use style::computed_values::visibility::T as Visibility;
use style::properties::ComputedValues;
use style::values::computed::ui::CursorKind;
use webrender_api::BorderRadius;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutSize, RectExt};
use crate::display_list::clip::{Clip, ClipId};
use crate::display_list::stacking_context::StackingContextSection;
use crate::display_list::{
StackingContext, StackingContextContent, StackingContextTree, ToWebRender,
};
use crate::fragment_tree::{Fragment, FragmentFlags};
use crate::geom::PhysicalRect;
pub(crate) struct HitTest<'a> {
/// The flags which describe how to perform this [`HitTest`].
flags: ElementsFromPointFlags,
/// The point to test for this hit test, relative to the page.
point_to_test: LayoutPoint,
/// A cached version of [`Self::point_to_test`] projected to a spatial node, to avoid
/// doing a lot of matrix math over and over.
projected_point_to_test: Option<(ScrollTreeNodeId, LayoutPoint, FastLayoutTransform)>,
/// The stacking context tree against which to perform the hit test.
stacking_context_tree: &'a StackingContextTree,
/// The resulting [`HitTestResultItems`] for this hit test.
results: Vec<ElementsFromPointResult>,
/// A cache of hit test results for shared clip nodes.
clip_hit_test_results: HashMap<ClipId, bool>,
}
impl<'a> HitTest<'a> {
pub(crate) fn run(
stacking_context_tree: &'a StackingContextTree,
point_to_test: LayoutPoint,
flags: ElementsFromPointFlags,
) -> Vec<ElementsFromPointResult> {
let mut hit_test = Self {
flags,
point_to_test,
projected_point_to_test: None,
stacking_context_tree,
results: Vec::new(),
clip_hit_test_results: HashMap::new(),
};
stacking_context_tree
.root_stacking_context
.hit_test(&mut hit_test);
hit_test.results
}
/// Perform a hit test against a the clip node for the given [`ClipId`], returning
/// true if it is not clipped out or false if is clipped out.
fn hit_test_clip_id(&mut self, clip_id: ClipId) -> bool {
if clip_id == ClipId::INVALID {
return true;
}
if let Some(result) = self.clip_hit_test_results.get(&clip_id) {
return *result;
}
let clip = self.stacking_context_tree.clip_store.get(clip_id);
let result = self
.location_in_spatial_node(clip.parent_scroll_node_id)
.is_some_and(|(point, _)| {
clip.contains(point) && self.hit_test_clip_id(clip.parent_clip_id)
});
self.clip_hit_test_results.insert(clip_id, result);
result
}
/// Get the hit test location in the coordinate system of the given spatial node,
/// returning `None` if the transformation is uninvertible or the point cannot be
/// projected into the spatial node.
fn location_in_spatial_node(
&mut self,
scroll_tree_node_id: ScrollTreeNodeId,
) -> Option<(LayoutPoint, FastLayoutTransform)> {
match self.projected_point_to_test {
Some((cached_scroll_tree_node_id, projected_point, transform))
if cached_scroll_tree_node_id == scroll_tree_node_id =>
{
return Some((projected_point, transform));
},
_ => {},
}
let transform = self
.stacking_context_tree
.compositor_info
.scroll_tree
.cumulative_root_to_node_transform(&scroll_tree_node_id)?;
let projected_point = transform.project_point2d(self.point_to_test)?;
self.projected_point_to_test = Some((scroll_tree_node_id, projected_point, transform));
Some((projected_point, transform))
}
}
impl Clip {
fn contains(&self, point: LayoutPoint) -> bool {
rounded_rect_contains_point(self.rect, &self.radii, point)
}
}
impl StackingContext {
/// Perform a hit test against a [`StackingContext`]. Note that this is the reverse
/// of the stacking context walk algorithm in `stacking_context.rs`. Any changes made
/// here should be reflected in the forward version in that file.
fn hit_test(&self, hit_test: &mut HitTest) -> bool {
let mut contents = self.contents.iter().rev().peekable();
// Step 10: Outlines
while contents
.peek()
.is_some_and(|child| child.section() == StackingContextSection::Outline)
{
// The hit test will not hit the outline.
let _ = contents.next().unwrap();
}
// Steps 8 and 9: Stacking contexts with non-negative z-index, and
// positioned stacking containers (where z-index is auto)
let mut real_stacking_contexts_and_positioned_stacking_containers = self
.real_stacking_contexts_and_positioned_stacking_containers
.iter()
.rev()
.peekable();
while real_stacking_contexts_and_positioned_stacking_containers
.peek()
.is_some_and(|child| child.z_index() >= 0)
{
let child = real_stacking_contexts_and_positioned_stacking_containers
.next()
.unwrap();
if child.hit_test(hit_test) {
return true;
}
}
// Steps 7 and 8: Fragments and inline stacking containers
while contents
.peek()
.is_some_and(|child| child.section() == StackingContextSection::Foreground)
{
let child = contents.next().unwrap();
if self.hit_test_content(child, hit_test) {
return true;
}
}
// Step 6: Float stacking containers
for child in self.float_stacking_containers.iter().rev() {
if child.hit_test(hit_test) {
return true;
}
}
// Step 5: Block backgrounds and borders
while contents.peek().is_some_and(|child| {
child.section() == StackingContextSection::DescendantBackgroundsAndBorders
}) {
let child = contents.next().unwrap();
if self.hit_test_content(child, hit_test) {
return true;
}
}
// Step 4: Stacking contexts with negative z-index
for child in real_stacking_contexts_and_positioned_stacking_containers {
if child.hit_test(hit_test) {
return true;
}
}
// Steps 2 and 3: Borders and background for the root
while contents.peek().is_some_and(|child| {
child.section() == StackingContextSection::OwnBackgroundsAndBorders
}) {
let child = contents.next().unwrap();
if self.hit_test_content(child, hit_test) {
return true;
}
}
false
}
pub(crate) fn hit_test_content(
&self,
content: &StackingContextContent,
hit_test: &mut HitTest<'_>,
) -> bool {
match content {
StackingContextContent::Fragment {
scroll_node_id,
clip_id,
containing_block,
fragment,
..
} => {
hit_test.hit_test_clip_id(*clip_id) &&
fragment.hit_test(hit_test, *scroll_node_id, containing_block)
},
StackingContextContent::AtomicInlineStackingContainer { index } => {
self.atomic_inline_stacking_containers[*index].hit_test(hit_test)
},
}
}
}
impl Fragment {
pub(crate) fn hit_test(
&self,
hit_test: &mut HitTest,
spatial_node_id: ScrollTreeNodeId,
containing_block: &PhysicalRect<Au>,
) -> bool {
let Some(tag) = self.tag() else {
return false;
};
let mut hit_test_fragment_inner =
|style: &ComputedValues,
fragment_rect: PhysicalRect<Au>,
border_radius: BorderRadius,
fragment_flags: FragmentFlags,
auto_cursor: Cursor| {
let is_root_element = fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT);
if !is_root_element {
if style.get_inherited_ui().pointer_events == PointerEvents::None {
return false;
}
if style.get_inherited_box().visibility != Visibility::Visible {
return false;
}
}
let (point_in_spatial_node, transform) =
match hit_test.location_in_spatial_node(spatial_node_id) {
Some(point) => point,
None => return false,
};
if !is_root_element &&
style.get_box().backface_visibility == BackfaceVisibility::Hidden &&
transform.is_backface_visible()
{
return false;
}
let fragment_rect = fragment_rect.translate(containing_block.origin.to_vector());
if is_root_element {
let viewport_size = hit_test
.stacking_context_tree
.compositor_info
.viewport_details
.size;
let viewport_rect = LayoutRect::from_origin_and_size(
Default::default(),
viewport_size.cast_unit(),
);
if !viewport_rect.contains(hit_test.point_to_test) {
return false;
}
} else if !rounded_rect_contains_point(
fragment_rect.to_webrender(),
&border_radius,
point_in_spatial_node,
) {
return false;
}
let point_in_target = point_in_spatial_node.cast_unit() -
Vector2D::new(
fragment_rect.origin.x.to_f32_px(),
fragment_rect.origin.y.to_f32_px(),
);
hit_test.results.push(ElementsFromPointResult {
node: tag.node,
point_in_target,
cursor: cursor(style.get_inherited_ui().cursor.keyword, auto_cursor),
});
!hit_test.flags.contains(ElementsFromPointFlags::FindAll)
};
match self {
Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
let box_fragment = box_fragment.borrow();
hit_test_fragment_inner(
&box_fragment.style,
box_fragment.border_rect(),
box_fragment.border_radius(),
box_fragment.base.flags,
Cursor::Default,
)
},
Fragment::Text(text) => {
let text = &*text.borrow();
hit_test_fragment_inner(
&text.inline_styles.style.borrow(),
text.rect,
BorderRadius::zero(),
FragmentFlags::empty(),
Cursor::Text,
)
},
_ => false,
}
}
}
fn rounded_rect_contains_point(
rect: LayoutRect,
border_radius: &BorderRadius,
point: LayoutPoint,
) -> bool {
if !rect.contains(point) {
return false;
}
if border_radius.is_zero() {
return true;
}
let check_corner = |corner: LayoutPoint, radius: &LayoutSize, is_right, is_bottom| {
let mut origin = corner;
if is_right {
origin.x -= radius.width;
}
if is_bottom {
origin.y -= radius.height;
}
if !Box2D::from_origin_and_size(origin, *radius).contains(point) {
return true;
}
let center = (
if is_right {
corner.x - radius.width
} else {
corner.x + radius.width
},
if is_bottom {
corner.y - radius.height
} else {
corner.y + radius.height
},
);
let radius = (radius.width as f64, radius.height as f64);
Ellipse::new(center, radius, 0.0).contains((point.x, point.y).into())
};
check_corner(rect.top_left(), &border_radius.top_left, false, false) &&
check_corner(rect.top_right(), &border_radius.top_right, true, false) &&
check_corner(rect.bottom_right(), &border_radius.bottom_right, true, true) &&
check_corner(rect.bottom_left(), &border_radius.bottom_left, false, true)
}
fn cursor(kind: CursorKind, auto_cursor: Cursor) -> Cursor {
match kind {
CursorKind::Auto => auto_cursor,
CursorKind::None => Cursor::None,
CursorKind::Default => Cursor::Default,
CursorKind::Pointer => Cursor::Pointer,
CursorKind::ContextMenu => Cursor::ContextMenu,
CursorKind::Help => Cursor::Help,
CursorKind::Progress => Cursor::Progress,
CursorKind::Wait => Cursor::Wait,
CursorKind::Cell => Cursor::Cell,
CursorKind::Crosshair => Cursor::Crosshair,
CursorKind::Text => Cursor::Text,
CursorKind::VerticalText => Cursor::VerticalText,
CursorKind::Alias => Cursor::Alias,
CursorKind::Copy => Cursor::Copy,
CursorKind::Move => Cursor::Move,
CursorKind::NoDrop => Cursor::NoDrop,
CursorKind::NotAllowed => Cursor::NotAllowed,
CursorKind::Grab => Cursor::Grab,
CursorKind::Grabbing => Cursor::Grabbing,
CursorKind::EResize => Cursor::EResize,
CursorKind::NResize => Cursor::NResize,
CursorKind::NeResize => Cursor::NeResize,
CursorKind::NwResize => Cursor::NwResize,
CursorKind::SResize => Cursor::SResize,
CursorKind::SeResize => Cursor::SeResize,
CursorKind::SwResize => Cursor::SwResize,
CursorKind::WResize => Cursor::WResize,
CursorKind::EwResize => Cursor::EwResize,
CursorKind::NsResize => Cursor::NsResize,
CursorKind::NeswResize => Cursor::NeswResize,
CursorKind::NwseResize => Cursor::NwseResize,
CursorKind::ColResize => Cursor::ColResize,
CursorKind::RowResize => Cursor::RowResize,
CursorKind::AllScroll => Cursor::AllScroll,
CursorKind::ZoomIn => Cursor::ZoomIn,
CursorKind::ZoomOut => Cursor::ZoomOut,
}
}

View File

@@ -6,15 +6,12 @@ use std::cell::{OnceCell, RefCell};
use std::sync::Arc;
use app_units::{AU_PER_PX, Au};
use base::WebRenderEpochToU16;
use base::id::ScrollTreeNodeId;
use clip::{Clip, ClipId};
use compositing_traits::display_list::{CompositorDisplayListInfo, SpatialTreeNodeInfo};
use embedder_traits::Cursor;
use euclid::{Point2D, Scale, SideOffsets2D, Size2D, UnknownUnit, Vector2D};
use fonts::GlyphStore;
use gradient::WebRenderGradient;
use layout_api::ReflowRequest;
use net_traits::image_cache::Image as CachedImage;
use range::Range as ServoRange;
use servo_arc::Arc as ServoArc;
@@ -37,19 +34,18 @@ use style::values::computed::{
use style::values::generics::NonNegative;
use style::values::generics::rect::Rect;
use style::values::specified::text::TextDecorationLine;
use style::values::specified::ui::CursorKind;
use style_traits::{CSSPixel as StyloCSSPixel, DevicePixel as StyloDevicePixel};
use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutRect, LayoutSize};
use webrender_api::{
self as wr, BorderDetails, BoxShadowClipMode, BuiltDisplayList, ClipChainId, ClipMode,
CommonItemProperties, ComplexClipRegion, ImageRendering, NinePatchBorder,
NinePatchBorderSource, PropertyBinding, SpatialId, SpatialTreeItemKey, units,
self as wr, BorderDetails, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipChainId,
ClipMode, CommonItemProperties, ComplexClipRegion, NinePatchBorder, NinePatchBorderSource,
PrimitiveFlags, PropertyBinding, SpatialId, SpatialTreeItemKey, units,
};
use wr::units::LayoutVector2D;
use crate::cell::ArcRefCell;
use crate::context::{ImageResolver, ResolvedImage};
pub use crate::display_list::conversions::ToWebRender;
pub(crate) use crate::display_list::conversions::ToWebRender;
use crate::display_list::stacking_context::StackingContextSection;
use crate::fragment_tree::{
BackgroundMode, BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, Tag,
@@ -65,14 +61,13 @@ mod background;
mod clip;
mod conversions;
mod gradient;
mod hit_test;
mod stacking_context;
use background::BackgroundPainter;
pub use stacking_context::*;
pub(crate) use hit_test::HitTest;
pub(crate) use stacking_context::*;
// webrender's `ItemTag` is private.
type ItemTag = (u64, u16);
type HitInfo = Option<ItemTag>;
const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(AU_PER_PX);
pub(crate) struct DisplayListBuilder<'a> {
@@ -149,7 +144,11 @@ struct HighlightTraversalState {
impl InspectorHighlight {
fn for_node(node: OpaqueNode) -> Self {
Self {
tag: Tag::new(node),
tag: Tag {
node,
// TODO: Support highlighting pseudo-elements.
pseudo_element_chain: Default::default(),
},
state: None,
}
}
@@ -157,19 +156,18 @@ impl InspectorHighlight {
impl DisplayListBuilder<'_> {
pub(crate) fn build(
reflow_request: &ReflowRequest,
stacking_context_tree: &mut StackingContextTree,
fragment_tree: &FragmentTree,
image_resolver: Arc<ImageResolver>,
device_pixel_ratio: Scale<f32, StyloCSSPixel, StyloDevicePixel>,
highlighted_dom_node: Option<OpaqueNode>,
debug: &DebugOptions,
) -> BuiltDisplayList {
// Build the rest of the display list which inclues all of the WebRender primitives.
let compositor_info = &mut stacking_context_tree.compositor_info;
compositor_info.hit_test_info.clear();
let pipeline_id = compositor_info.pipeline_id;
let mut webrender_display_list_builder =
webrender_api::DisplayListBuilder::new(compositor_info.pipeline_id);
webrender_api::DisplayListBuilder::new(pipeline_id);
webrender_display_list_builder.begin();
// `dump_serialized_display_list` doesn't actually print anything. It sets up
@@ -189,9 +187,7 @@ impl DisplayListBuilder<'_> {
current_clip_id: ClipId::INVALID,
webrender_display_list_builder: &mut webrender_display_list_builder,
compositor_info,
inspector_highlight: reflow_request
.highlighted_dom_node
.map(InspectorHighlight::for_node),
inspector_highlight: highlighted_dom_node.map(InspectorHighlight::for_node),
paint_body_background: true,
clip_map: Default::default(),
image_resolver,
@@ -204,6 +200,8 @@ impl DisplayListBuilder<'_> {
builder.add_clip_to_display_list(clip);
}
builder.push_hit_tests_for_scrollable_areas(&stacking_context_tree.hit_test_items);
// Paint the canvas background (if any) before/under everything else
stacking_context_tree
.root_stacking_context
@@ -275,7 +273,7 @@ impl DisplayListBuilder<'_> {
info.origin,
*parent_spatial_node_id,
info.transform_style,
PropertyBinding::Value(info.transform),
PropertyBinding::Value(*info.transform.to_transform()),
info.kind,
spatial_tree_item_key,
);
@@ -313,6 +311,39 @@ impl DisplayListBuilder<'_> {
self.compositor_info.scroll_tree = scroll_tree;
}
fn push_hit_tests_for_scrollable_areas(
&mut self,
scroll_frame_hit_test_items: &[ScrollFrameHitTestItem],
) {
// Add a single hit test that covers the entire viewport, so that WebRender knows
// which pipeline it hits when doing hit testing.
let pipeline_id = self.compositor_info.pipeline_id;
let viewport_size = self.compositor_info.viewport_details.size;
let viewport_rect = LayoutRect::from_size(viewport_size.cast_unit());
self.wr().push_hit_test(
viewport_rect,
ClipChainId::INVALID,
SpatialId::root_reference_frame(pipeline_id),
PrimitiveFlags::default(),
(0, 0), /* tag */
);
for item in scroll_frame_hit_test_items {
let spatial_id = self
.compositor_info
.scroll_tree
.webrender_id(&item.scroll_node_id);
let clip_chain_id = self.clip_chain_id(item.clip_id);
self.wr().push_hit_test(
item.rect,
clip_chain_id,
spatial_id,
PrimitiveFlags::default(),
(item.external_scroll_id.0, 0), /* tag */
);
}
}
/// Add the given [`Clip`] to the WebRender display list and create a mapping from
/// its [`ClipId`] to a WebRender [`ClipChainId`]. This happens:
/// - When WebRender display list construction starts: All clips created during the
@@ -394,27 +425,6 @@ impl DisplayListBuilder<'_> {
}
}
fn hit_info(
&mut self,
style: &ComputedValues,
tag: Option<Tag>,
auto_cursor: Cursor,
) -> HitInfo {
use style::computed_values::pointer_events::T as PointerEvents;
let inherited_ui = style.get_inherited_ui();
if inherited_ui.pointer_events == PointerEvents::None {
return None;
}
let hit_test_index = self.compositor_info.add_hit_test_info(
tag?.node.0 as u64,
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
self.current_scroll_node_id,
);
Some((hit_test_index as u64, self.compositor_info.epoch.as_u16()))
}
/// Draw highlights around the node that is currently hovered in the devtools.
fn paint_dom_inspector_highlight(&mut self) {
let Some(highlight) = self
@@ -573,7 +583,6 @@ impl Fragment {
builder: &mut DisplayListBuilder,
containing_block: &PhysicalRect<Au>,
section: StackingContextSection,
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
text_decorations: &Arc<Vec<FragmentTextDecoration>>,
) {
@@ -597,7 +606,6 @@ impl Fragment {
Visibility::Visible => BuilderForBoxFragment::new(
box_fragment,
containing_block,
is_hit_test_for_scrollable_overflow,
is_collapsed_table_borders,
)
.build(builder, section),
@@ -605,20 +613,7 @@ impl Fragment {
Visibility::Collapse => (),
}
},
Fragment::AbsoluteOrFixedPositioned(_) => {},
Fragment::Positioning(positioning_fragment) => {
let positioning_fragment = positioning_fragment.borrow();
let rect = positioning_fragment
.rect
.translate(containing_block.origin.to_vector());
self.maybe_push_hit_test_for_style_and_tag(
builder,
&positioning_fragment.style,
positioning_fragment.base.tag,
rect,
Cursor::Default,
);
},
Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Positioning(_) => {},
Fragment::Image(image) => {
let image = image.borrow();
match image.style.get_inherited_box().visibility {
@@ -700,31 +695,6 @@ impl Fragment {
}
}
fn maybe_push_hit_test_for_style_and_tag(
&self,
builder: &mut DisplayListBuilder,
style: &ComputedValues,
tag: Option<Tag>,
rect: PhysicalRect<Au>,
cursor: Cursor,
) {
let hit_info = builder.hit_info(style, tag, cursor);
let hit_info = match hit_info {
Some(hit_info) => hit_info,
None => return,
};
let clip_chain_id = builder.clip_chain_id(builder.current_clip_id);
let spatial_id = builder.spatial_id(builder.current_scroll_node_id);
builder.wr().push_hit_test(
rect.to_webrender(),
clip_chain_id,
spatial_id,
style.get_webrender_primitive_flags(),
hit_info,
);
}
fn build_display_list_for_text_fragment(
&self,
fragment: &TextFragment,
@@ -754,14 +724,6 @@ impl Fragment {
}
let parent_style = fragment.inline_styles.style.borrow();
self.maybe_push_hit_test_for_style_and_tag(
builder,
&parent_style,
fragment.base.tag,
rect,
Cursor::Text,
);
let color = parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
let dppx = builder.device_pixel_ratio.get();
@@ -975,7 +937,6 @@ struct BuilderForBoxFragment<'a> {
border_edge_clip_chain_id: RefCell<Option<ClipChainId>>,
padding_edge_clip_chain_id: RefCell<Option<ClipChainId>>,
content_edge_clip_chain_id: RefCell<Option<ClipChainId>>,
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
}
@@ -983,48 +944,22 @@ impl<'a> BuilderForBoxFragment<'a> {
fn new(
fragment: &'a BoxFragment,
containing_block: &'a PhysicalRect<Au>,
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
) -> Self {
let border_rect = fragment
.border_rect()
.translate(containing_block.origin.to_vector());
let webrender_border_rect = border_rect.to_webrender();
let border_radius = {
let resolve = |radius: &LengthPercentage, box_size: Au| {
radius.to_used_value(box_size).to_f32_px()
};
let corner = |corner: &style::values::computed::BorderCornerRadius| {
Size2D::new(
resolve(&corner.0.width.0, border_rect.size.width),
resolve(&corner.0.height.0, border_rect.size.height),
)
};
let b = fragment.style.get_border();
let mut radius = wr::BorderRadius {
top_left: corner(&b.border_top_left_radius),
top_right: corner(&b.border_top_right_radius),
bottom_right: corner(&b.border_bottom_right_radius),
bottom_left: corner(&b.border_bottom_left_radius),
};
normalize_radii(&webrender_border_rect, &mut radius);
radius
};
Self {
fragment,
containing_block,
border_rect: webrender_border_rect,
border_radius,
border_rect: border_rect.to_webrender(),
border_radius: fragment.border_radius(),
margin_rect: OnceCell::new(),
padding_rect: OnceCell::new(),
content_rect: OnceCell::new(),
border_edge_clip_chain_id: RefCell::new(None),
padding_edge_clip_chain_id: RefCell::new(None),
content_edge_clip_chain_id: RefCell::new(None),
is_hit_test_for_scrollable_overflow,
is_collapsed_table_borders,
}
}
@@ -1107,11 +1042,6 @@ impl<'a> BuilderForBoxFragment<'a> {
}
fn build(&mut self, builder: &mut DisplayListBuilder, section: StackingContextSection) {
if self.is_hit_test_for_scrollable_overflow {
self.build_hit_test(builder, self.fragment.scrollable_overflow().to_webrender());
return;
}
if self.is_collapsed_table_borders {
self.build_collapsed_table_borders(builder);
return;
@@ -1122,7 +1052,6 @@ impl<'a> BuilderForBoxFragment<'a> {
return;
}
self.build_hit_test(builder, self.border_rect);
if self
.fragment
.base
@@ -1137,30 +1066,6 @@ impl<'a> BuilderForBoxFragment<'a> {
self.build_border(builder);
}
fn build_hit_test(&self, builder: &mut DisplayListBuilder, rect: LayoutRect) {
let hit_info = builder.hit_info(
&self.fragment.style,
self.fragment.base.tag,
Cursor::Default,
);
let hit_info = match hit_info {
Some(hit_info) => hit_info,
None => return,
};
let mut common = builder.common_properties(rect, &self.fragment.style);
if let Some(clip_chain_id) = self.border_edge_clip(builder, false) {
common.clip_chain_id = clip_chain_id;
}
builder.wr().push_hit_test(
common.clip_rect,
common.clip_chain_id,
common.spatial_id,
common.flags,
hit_info,
);
}
fn build_background_for_painter(
&mut self,
builder: &mut DisplayListBuilder,
@@ -1362,7 +1267,7 @@ impl<'a> BuilderForBoxFragment<'a> {
fn build_collapsed_table_borders(&mut self, builder: &mut DisplayListBuilder) {
let Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(table_info)) =
&self.fragment.specific_layout_info
self.fragment.specific_layout_info()
else {
return;
};
@@ -1501,7 +1406,8 @@ impl<'a> BuilderForBoxFragment<'a> {
width = size.width;
height = size.height;
NinePatchBorderSource::Image(key, ImageRendering::Auto)
let image_rendering = self.fragment.style.clone_image_rendering().to_webrender();
NinePatchBorderSource::Image(key, image_rendering)
},
Ok(ResolvedImage::Gradient(gradient)) => {
match gradient::build(&self.fragment.style, gradient, border_image_size, builder) {
@@ -1552,13 +1458,19 @@ impl<'a> BuilderForBoxFragment<'a> {
if width == 0.0 {
return;
}
let offset = outline
.outline_offset
.px()
.max(-self.border_rect.width() / 2.0)
.max(-self.border_rect.height() / 2.0) +
width;
let outline_rect = self.border_rect.inflate(offset, offset);
// <https://drafts.csswg.org/css-ui-3/#outline-offset>
// > Negative values must cause the outline to shrink into the border box. Both
// > the height and the width of outside of the shape drawn by the outline should
// > not become smaller than twice the computed value of the outline-width
// > property, to make sure that an outline can be rendered even with large
// > negative values. User agents should apply this constraint independently in
// > each dimension. If the outline is drawn as multiple disconnected shapes, this
// > constraint applies to each shape separately.
let offset = outline.outline_offset.px() + width;
let outline_rect = self.border_rect.inflate(
offset.max(-self.border_rect.width() / 2.0 + width),
offset.max(-self.border_rect.height() / 2.0 + width),
);
let common = builder.common_properties(outline_rect, &self.fragment.style);
let widths = SideOffsets2D::new_all_same(width);
let border_style = match outline.outline_style {
@@ -1678,47 +1590,6 @@ fn glyphs_advance_by_index(
point
}
fn cursor(kind: CursorKind, auto_cursor: Cursor) -> Cursor {
match kind {
CursorKind::Auto => auto_cursor,
CursorKind::None => Cursor::None,
CursorKind::Default => Cursor::Default,
CursorKind::Pointer => Cursor::Pointer,
CursorKind::ContextMenu => Cursor::ContextMenu,
CursorKind::Help => Cursor::Help,
CursorKind::Progress => Cursor::Progress,
CursorKind::Wait => Cursor::Wait,
CursorKind::Cell => Cursor::Cell,
CursorKind::Crosshair => Cursor::Crosshair,
CursorKind::Text => Cursor::Text,
CursorKind::VerticalText => Cursor::VerticalText,
CursorKind::Alias => Cursor::Alias,
CursorKind::Copy => Cursor::Copy,
CursorKind::Move => Cursor::Move,
CursorKind::NoDrop => Cursor::NoDrop,
CursorKind::NotAllowed => Cursor::NotAllowed,
CursorKind::Grab => Cursor::Grab,
CursorKind::Grabbing => Cursor::Grabbing,
CursorKind::EResize => Cursor::EResize,
CursorKind::NResize => Cursor::NResize,
CursorKind::NeResize => Cursor::NeResize,
CursorKind::NwResize => Cursor::NwResize,
CursorKind::SResize => Cursor::SResize,
CursorKind::SeResize => Cursor::SeResize,
CursorKind::SwResize => Cursor::SwResize,
CursorKind::WResize => Cursor::WResize,
CursorKind::EwResize => Cursor::EwResize,
CursorKind::NsResize => Cursor::NsResize,
CursorKind::NeswResize => Cursor::NeswResize,
CursorKind::NwseResize => Cursor::NwseResize,
CursorKind::ColResize => Cursor::ColResize,
CursorKind::RowResize => Cursor::RowResize,
CursorKind::AllScroll => Cursor::AllScroll,
CursorKind::ZoomIn => Cursor::ZoomIn,
CursorKind::ZoomOut => Cursor::ZoomOut,
}
}
/// Radii for the padding edge or content edge
fn inner_radii(mut radii: wr::BorderRadius, insets: units::LayoutSideOffsets) -> wr::BorderRadius {
assert!(insets.left >= 0.0, "left inset must not be negative");
@@ -1914,3 +1785,36 @@ pub(super) fn compute_margin_box_radius(
),
}
}
impl BoxFragment {
fn border_radius(&self) -> BorderRadius {
let border = self.style.get_border();
if border.border_top_left_radius.0.is_zero() &&
border.border_top_right_radius.0.is_zero() &&
border.border_bottom_right_radius.0.is_zero() &&
border.border_bottom_left_radius.0.is_zero()
{
return BorderRadius::zero();
}
let border_rect = self.border_rect();
let resolve =
|radius: &LengthPercentage, box_size: Au| radius.to_used_value(box_size).to_f32_px();
let corner = |corner: &style::values::computed::BorderCornerRadius| {
Size2D::new(
resolve(&corner.0.width.0, border_rect.size.width),
resolve(&corner.0.height.0, border_rect.size.height),
)
};
let mut radius = wr::BorderRadius {
top_left: corner(&border.border_top_left_radius),
top_right: corner(&border.border_top_right_radius),
bottom_right: corner(&border.border_bottom_right_radius),
bottom_left: corner(&border.border_bottom_left_radius),
};
normalize_radii(&border_rect.to_webrender(), &mut radius);
radius
}
}

View File

@@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use core::f32;
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::mem;
use std::sync::Arc;
@@ -14,9 +14,11 @@ use compositing_traits::display_list::{
AxesScrollSensitivity, CompositorDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
SpatialTreeNodeInfo, StickyNodeInfo,
};
use embedder_traits::ViewportDetails;
use euclid::SideOffsets2D;
use euclid::default::{Point2D, Rect, Size2D};
use log::warn;
use malloc_size_of_derive::MallocSizeOf;
use servo_config::opts::DebugOptions;
use style::Zero;
use style::color::AbsoluteColor;
@@ -32,7 +34,7 @@ use style::values::generics::box_::Perspective;
use style::values::generics::transform::{self, GenericRotate, GenericScale, GenericTranslate};
use style::values::specified::box_::DisplayOutside;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::{self as wr, BorderRadius};
use webrender_api::{self as wr, BorderRadius, ExternalScrollId};
use wr::StickyOffsetBounds;
use wr::units::{LayoutPixel, LayoutSize};
@@ -91,7 +93,7 @@ impl ContainingBlock {
pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[derive(Clone, Copy, Debug, Eq, Ord, MallocSizeOf, PartialEq, PartialOrd)]
pub(crate) enum StackingContextSection {
OwnBackgroundsAndBorders,
DescendantBackgroundsAndBorders,
@@ -99,6 +101,24 @@ pub(crate) enum StackingContextSection {
Outline,
}
#[derive(MallocSizeOf)]
pub(crate) struct ScrollFrameHitTestItem {
/// The [`ScrollTreeNodeId`] of the spatial node that contains this hit test item.
pub scroll_node_id: ScrollTreeNodeId,
/// The [`ClipId`] of the clip that clips this [`ScrollFrameHitTestItems`].
pub clip_id: ClipId,
/// The rectangle of the scroll frame in the coordinate space of [`Self::scroll_node_id`].
pub rect: LayoutRect,
/// The WebRender [`ExternalScrollId`] of the scrolling spatial node that
/// this [`ScrollFrameHitTestItem`] identifies. Note that this is a *different*
/// spatial node than the one identified by [`Self::scroll_node_id`] (the parent).
pub external_scroll_id: ExternalScrollId,
}
#[derive(MallocSizeOf)]
pub(crate) struct StackingContextTree {
/// The root stacking context of this [`StackingContextTree`].
pub root_stacking_context: StackingContext,
@@ -113,6 +133,10 @@ pub(crate) struct StackingContextTree {
/// for things like `overflow`. More clips may be created later during WebRender
/// display list construction, but they are never added here.
pub clip_store: StackingContextTreeClipStore,
/// A vector of hit test items, one per scroll frame. These are used for allowing
/// renderer-side scrolling in the Servo renderer.
pub hit_test_items: Vec<ScrollFrameHitTestItem>,
}
impl StackingContextTree {
@@ -120,7 +144,7 @@ impl StackingContextTree {
/// pipeline id.
pub fn new(
fragment_tree: &FragmentTree,
viewport_size: LayoutSize,
viewport_details: ViewportDetails,
pipeline_id: wr::PipelineId,
first_reflow: bool,
debug: &DebugOptions,
@@ -131,8 +155,9 @@ impl StackingContextTree {
scrollable_overflow.size.height.to_f32_px(),
));
let viewport_size = viewport_details.layout_size();
let compositor_info = CompositorDisplayListInfo::new(
viewport_size,
viewport_details,
scrollable_overflow,
pipeline_id,
// This epoch is set when the WebRender display list is built. For now use a dummy value.
@@ -145,7 +170,7 @@ impl StackingContextTree {
let cb_for_non_fixed_descendants = ContainingBlock::new(
fragment_tree.initial_containing_block,
root_scroll_node_id,
Some(compositor_info.viewport_size),
Some(viewport_size),
ClipId::INVALID,
);
let cb_for_fixed_descendants = ContainingBlock::new(
@@ -172,6 +197,7 @@ impl StackingContextTree {
root_stacking_context: StackingContext::create_root(root_scroll_node_id, debug),
compositor_info,
clip_store: Default::default(),
hit_test_items: Vec::new(),
};
let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug);
@@ -199,6 +225,7 @@ impl StackingContextTree {
fn push_reference_frame(
&mut self,
origin: LayoutPoint,
frame_origin_for_query: LayoutPoint,
parent_scroll_node_id: &ScrollTreeNodeId,
transform_style: wr::TransformStyle,
transform: LayoutTransform,
@@ -208,8 +235,9 @@ impl StackingContextTree {
Some(parent_scroll_node_id),
SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
origin,
frame_origin_for_query,
transform_style,
transform,
transform: transform.into(),
kind,
}),
)
@@ -231,6 +259,7 @@ impl StackingContextTree {
clip_rect,
scroll_sensitivity,
offset: LayoutVector2D::zero(),
offset_changed: Cell::new(false),
}),
)
}
@@ -256,7 +285,7 @@ impl StackingContextTree {
}
/// The text decorations for a Fragment, collecting during [`StackingContextTree`] construction.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) struct FragmentTextDecoration {
pub line: TextDecorationLine,
pub color: AbsoluteColor,
@@ -267,6 +296,7 @@ pub(crate) struct FragmentTextDecoration {
///
/// This is generally part of a fragment, like its borders or foreground, but it
/// can also be a stacking container that needs to be painted in fragment order.
#[derive(MallocSizeOf)]
pub(crate) enum StackingContextContent {
/// A fragment that does not generate a stacking context or stacking container.
Fragment {
@@ -276,8 +306,8 @@ pub(crate) enum StackingContextContent {
section: StackingContextSection,
containing_block: PhysicalRect<Au>,
fragment: Fragment,
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
#[conditional_malloc_size_of]
text_decorations: Arc<Vec<FragmentTextDecoration>>,
},
@@ -288,7 +318,7 @@ pub(crate) enum StackingContextContent {
}
impl StackingContextContent {
fn section(&self) -> StackingContextSection {
pub(crate) fn section(&self) -> StackingContextSection {
match self {
Self::Fragment { section, .. } => *section,
Self::AtomicInlineStackingContainer { .. } => StackingContextSection::Foreground,
@@ -308,7 +338,6 @@ impl StackingContextContent {
section,
containing_block,
fragment,
is_hit_test_for_scrollable_overflow,
is_collapsed_table_borders,
text_decorations,
} => {
@@ -319,7 +348,6 @@ impl StackingContextContent {
builder,
containing_block,
*section,
*is_hit_test_for_scrollable_overflow,
*is_collapsed_table_borders,
text_decorations,
);
@@ -331,7 +359,7 @@ impl StackingContextContent {
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
pub(crate) enum StackingContextType {
RealStackingContext,
PositionedStackingContainer,
@@ -344,6 +372,7 @@ pub(crate) enum StackingContextType {
///
/// We use the term “real stacking context” in situations that call for a
/// stacking context but not a stacking container.
#[derive(MallocSizeOf)]
pub struct StackingContext {
/// The spatial id of this fragment. This is used to properly handle
/// things like preserve-3d.
@@ -360,7 +389,7 @@ pub struct StackingContext {
context_type: StackingContextType,
/// The contents that need to be painted in fragment order.
contents: Vec<StackingContextContent>,
pub(super) contents: Vec<StackingContextContent>,
/// Stacking contexts that need to be stolen by the parent stacking context
/// if this is a stacking container, that is, real stacking contexts and
@@ -371,13 +400,13 @@ pub struct StackingContext {
/// > if it created a new stacking context, but omitting any positioned
/// > descendants or descendants that actually create a stacking context
/// > (letting the parent stacking context paint them, instead).
real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
pub(super) real_stacking_contexts_and_positioned_stacking_containers: Vec<StackingContext>,
/// Float stacking containers.
/// Separate from real_stacking_contexts_or_positioned_stacking_containers
/// because they should never be stolen by the parent stacking context.
/// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
float_stacking_containers: Vec<StackingContext>,
pub(super) float_stacking_containers: Vec<StackingContext>,
/// Atomic inline stacking containers.
/// Separate from real_stacking_contexts_or_positioned_stacking_containers
@@ -386,26 +415,25 @@ pub struct StackingContext {
/// can index into this vec to paint them in fragment order.
/// <https://drafts.csswg.org/css-position-4/#paint-a-stacking-container>
/// <https://drafts.csswg.org/css-position-4/#paint-a-box-in-a-line-box>
atomic_inline_stacking_containers: Vec<StackingContext>,
pub(super) atomic_inline_stacking_containers: Vec<StackingContext>,
/// Information gathered about the painting order, for [Self::debug_print].
debug_print_items: Option<RefCell<Vec<DebugPrintItem>>>,
}
/// Refers to one of the child contents or stacking contexts of a [StackingContext].
#[derive(Clone, Copy)]
#[derive(Clone, Copy, MallocSizeOf)]
pub struct DebugPrintItem {
field: DebugPrintField,
index: usize,
}
/// Refers to one of the vecs of a [StackingContext].
#[derive(Clone, Copy)]
#[derive(Clone, Copy, MallocSizeOf)]
pub enum DebugPrintField {
Contents,
RealStackingContextsAndPositionedStackingContainers,
FloatStackingContainers,
AtomicInlineStackingContainers,
}
impl StackingContext {
@@ -468,7 +496,7 @@ impl StackingContext {
.push(stacking_context)
}
fn z_index(&self) -> i32 {
pub(crate) fn z_index(&self) -> i32 {
self.initializing_fragment.as_ref().map_or(0, |fragment| {
let fragment = fragment.borrow();
fragment.style.effective_z_index(fragment.base.flags)
@@ -576,7 +604,7 @@ impl StackingContext {
pub(crate) fn build_canvas_background_display_list(
&self,
builder: &mut DisplayListBuilder,
fragment_tree: &crate::FragmentTree,
fragment_tree: &crate::fragment_tree::FragmentTree,
) {
let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
fragment
@@ -636,7 +664,6 @@ impl StackingContext {
let mut fragment_builder = BuilderForBoxFragment::new(
&root_fragment,
&fragment_tree.initial_containing_block,
false, /* is_hit_test_for_scrollable_overflow */
false, /* is_collapsed_table_borders */
);
let painter = super::background::BackgroundPainter {
@@ -647,6 +674,9 @@ impl StackingContext {
fragment_builder.build_background_image(builder, &painter);
}
/// Build a display list from a a [`StackingContext`]. Note that this is the forward
/// version of the reversed stacking context walk algorithm in `hit_test.rs`. Any
/// changes made here should be reflected in the reverse version in that file.
pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) {
let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
@@ -791,9 +821,6 @@ impl StackingContext {
DebugPrintField::FloatStackingContainers => {
self.float_stacking_containers[*index].debug_print_with_tree(tree);
},
DebugPrintField::AtomicInlineStackingContainers => {
// do nothing; we print these in DebugPrintField::Contents
},
}
}
match self.context_type {
@@ -884,7 +911,6 @@ impl Fragment {
clip_id: containing_block.clip_id,
containing_block: containing_block.rect,
fragment: fragment_clone,
is_hit_test_for_scrollable_overflow: false,
is_collapsed_table_borders: false,
text_decorations: text_decorations.clone(),
});
@@ -910,7 +936,8 @@ struct OverflowFrameData {
impl BoxFragment {
fn get_stacking_context_type(&self) -> Option<StackingContextType> {
if self.style.establishes_stacking_context(self.base.flags) {
let flags = self.base.flags;
if self.style.establishes_stacking_context(flags) {
return Some(StackingContextType::RealStackingContext);
}
@@ -923,7 +950,10 @@ impl BoxFragment {
return Some(StackingContextType::FloatStackingContainer);
}
if self.is_atomic_inline_level() {
// Flex and grid items are painted like inline blocks.
// <https://drafts.csswg.org/css-flexbox-1/#painting>
// <https://drafts.csswg.org/css-grid/#z-order>
if self.is_atomic_inline_level() || flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM) {
return Some(StackingContextType::AtomicInlineStackingContainer);
}
@@ -992,8 +1022,11 @@ impl BoxFragment {
return;
}
let frame_origin_for_query = self.cumulative_border_box_rect().origin.to_webrender();
let new_spatial_id = stacking_context_tree.push_reference_frame(
reference_frame_data.origin.to_webrender(),
frame_origin_for_query,
&containing_block.scroll_node_id,
self.style.get_box().transform_style.to_webrender(),
reference_frame_data.transform,
@@ -1081,7 +1114,6 @@ impl BoxFragment {
BuilderForBoxFragment::new(
self,
&containing_block.rect,
false, /* is_hit_test_for_scrollable_overflow */
false, /* is_collapsed_table_borders */
),
)
@@ -1165,7 +1197,6 @@ impl BoxFragment {
BuilderForBoxFragment::new(
self,
&containing_block.rect,
false, /* is_hit_test_for_scrollable_overflow*/
false, /* is_collapsed_table_borders */
),
) {
@@ -1198,7 +1229,6 @@ impl BoxFragment {
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: false,
is_collapsed_table_borders: false,
text_decorations: text_decorations.clone(),
});
@@ -1210,6 +1240,11 @@ impl BoxFragment {
add_fragment(StackingContextSection::Outline);
}
// Spatial tree node that will affect the transform of the fragment. Note that the next frame,
// scroll frame, does not affect the transform of the fragment but affect the transform of it
// children.
*self.spatial_tree_node.borrow_mut() = Some(new_scroll_node_id);
// We want to build the scroll frame after the background and border, because
// they shouldn't scroll with the rest of the box content.
if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
@@ -1222,21 +1257,6 @@ impl BoxFragment {
if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
stacking_context
.contents
.push(StackingContextContent::Fragment {
scroll_node_id: new_scroll_node_id,
reference_frame_scroll_node_id:
reference_frame_scroll_node_id_for_fragments,
clip_id: new_clip_id,
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: true,
is_collapsed_table_borders: false,
text_decorations: text_decorations.clone(),
});
}
}
@@ -1321,7 +1341,7 @@ impl BoxFragment {
}
if matches!(&fragment, Fragment::Box(box_fragment) if matches!(
box_fragment.borrow().specific_layout_info,
box_fragment.borrow().specific_layout_info(),
Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(_))
)) {
stacking_context
@@ -1333,7 +1353,6 @@ impl BoxFragment {
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: false,
is_collapsed_table_borders: true,
text_decorations: text_decorations.clone(),
});
@@ -1405,7 +1424,7 @@ impl BoxFragment {
// https://drafts.csswg.org/css-overflow-3/#corner-clipping
let radii;
if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
let builder = BuilderForBoxFragment::new(self, containing_block_rect, false, false);
let builder = BuilderForBoxFragment::new(self, containing_block_rect, false);
radii = offset_radii(builder.border_radius, clip_margin);
} else if overflow.x != ComputedOverflow::Clip {
overflow_clip_rect.min.x = f32::MIN;
@@ -1430,39 +1449,20 @@ impl BoxFragment {
});
}
// scrollable overflow path
// From https://drafts.csswg.org/css-overflow/#propdef-overflow:
// > UAs must apply the overflow-* values set on the root element to the viewport when the
// > root elements display value is not none. However, when the root element is an [HTML]
// > html element (including XML syntax for HTML) whose overflow value is visible (in both
// > axes), and that element has as a child a body element whose display value is also not
// > none, user agents must instead apply the overflow-* values of the first such child
// > element to the viewport. The element from which the value is propagated must then have a
// > used overflow value of visible.
//
// TODO: This should only happen when the `display` value is actually propagated.
if self
.base
.flags
.contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
{
return None;
}
let scroll_frame_rect = self
.padding_rect()
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
let clip_id = stacking_context_tree.clip_store.add(
BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
BuilderForBoxFragment::new(self, containing_block_rect, false).border_radius,
scroll_frame_rect,
*parent_scroll_node_id,
parent_clip_id,
);
let tag = self.base.tag?;
let external_id = wr::ExternalScrollId(
let external_scroll_id = wr::ExternalScrollId(
tag.to_display_list_fragment_id(),
stacking_context_tree.compositor_info.pipeline_id,
);
@@ -1474,12 +1474,21 @@ impl BoxFragment {
let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
parent_scroll_node_id,
external_id,
external_scroll_id,
self.scrollable_overflow().to_webrender(),
scroll_frame_rect,
sensitivity,
);
stacking_context_tree
.hit_test_items
.push(ScrollFrameHitTestItem {
scroll_node_id: *parent_scroll_node_id,
clip_id,
rect: scroll_frame_rect,
external_scroll_id,
});
Some(OverflowFrameData {
clip_id,
scroll_frame_data: Some(ScrollFrameData {
@@ -1504,7 +1513,10 @@ impl BoxFragment {
Some(size) => size,
None => {
// This is a direct descendant of a reference frame.
&stacking_context_tree.compositor_info.viewport_size
&stacking_context_tree
.compositor_info
.viewport_details
.layout_size()
},
};

View File

@@ -2,21 +2,19 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::any::Any;
use std::marker::PhantomData;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use base::id::{BrowsingContextId, PipelineId};
use html5ever::{local_name, ns};
use layout_api::wrapper_traits::{
LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
use layout_api::wrapper_traits::{LayoutDataTrait, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use layout_api::{
GenericLayoutDataTrait, LayoutDamage, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType,
GenericLayoutDataTrait, LayoutDamage, LayoutElementType,
LayoutNodeType as ScriptLayoutNodeType, SVGElementData,
};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::image_cache::Image;
use script::layout_dom::ServoLayoutNode;
use script::layout_dom::ServoThreadSafeLayoutNode;
use servo_arc::Arc as ServoArc;
use smallvec::SmallVec;
use style::context::SharedStyleContext;
@@ -29,6 +27,7 @@ use crate::flow::BlockLevelBox;
use crate::flow::inline::{InlineItem, SharedInlineStyles};
use crate::fragment_tree::Fragment;
use crate::geom::PhysicalSize;
use crate::layout_box_base::LayoutBoxBase;
use crate::replaced::CanvasInfo;
use crate::table::TableLevelBox;
use crate::taffy::TaffyItemBox;
@@ -36,7 +35,7 @@ use crate::taffy::TaffyItemBox;
#[derive(MallocSizeOf)]
pub struct PseudoLayoutData {
pseudo: PseudoElement,
box_slot: ArcRefCell<Option<LayoutBox>>,
data: ArcRefCell<InnerDOMLayoutData>,
}
/// The data that is stored in each DOM node that is used by layout.
@@ -47,22 +46,65 @@ pub struct InnerDOMLayoutData {
}
impl InnerDOMLayoutData {
pub(crate) fn for_pseudo(
fn pseudo_layout_data(
&self,
pseudo_element: Option<PseudoElement>,
) -> Option<AtomicRef<Option<LayoutBox>>> {
let Some(pseudo_element) = pseudo_element else {
return Some(self.self_box.borrow());
};
pseudo_element: PseudoElement,
) -> Option<ArcRefCell<InnerDOMLayoutData>> {
for pseudo_layout_data in self.pseudo_boxes.iter() {
if pseudo_element == pseudo_layout_data.pseudo {
return Some(pseudo_layout_data.box_slot.borrow());
return Some(pseudo_layout_data.data.clone());
}
}
None
}
fn create_pseudo_layout_data(
&mut self,
pseudo_element: PseudoElement,
) -> ArcRefCell<InnerDOMLayoutData> {
let data: ArcRefCell<InnerDOMLayoutData> = Default::default();
self.pseudo_boxes.push(PseudoLayoutData {
pseudo: pseudo_element,
data: data.clone(),
});
data
}
fn fragments(&self) -> Vec<Fragment> {
self.self_box
.borrow()
.as_ref()
.map(|layout_box| layout_box.with_base_flat(LayoutBoxBase::fragments))
.unwrap_or_default()
}
fn repair_style(&self, node: &ServoThreadSafeLayoutNode, context: &SharedStyleContext) {
if let Some(layout_object) = &*self.self_box.borrow() {
layout_object.repair_style(context, node, &node.style(context));
}
for pseudo_layout_data in self.pseudo_boxes.iter() {
let Some(node_with_pseudo) = node.with_pseudo(pseudo_layout_data.pseudo) else {
continue;
};
pseudo_layout_data
.data
.borrow()
.repair_style(&node_with_pseudo, context);
}
}
fn clear_fragment_layout_cache(&self) {
if let Some(data) = self.self_box.borrow().as_ref() {
data.clear_fragment_layout_cache();
}
for pseudo_layout_data in self.pseudo_boxes.iter() {
pseudo_layout_data
.data
.borrow()
.clear_fragment_layout_cache();
}
}
}
/// A box that is stored in one of the `DOMLayoutData` slots.
@@ -98,30 +140,51 @@ impl LayoutBox {
}
}
pub(crate) fn fragments(&self) -> Vec<Fragment> {
pub(crate) fn with_base_flat<T>(&self, callback: impl Fn(&LayoutBoxBase) -> Vec<T>) -> Vec<T> {
match self {
LayoutBox::DisplayContents(..) => vec![],
LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().fragments(),
LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback),
LayoutBox::InlineLevel(inline_items) => inline_items
.iter()
.flat_map(|inline_item| inline_item.borrow().fragments())
.flat_map(|inline_item| inline_item.borrow().with_base(&callback))
.collect(),
LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().fragments(),
LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().fragments(),
LayoutBox::TableLevelBox(table_box) => table_box.fragments(),
LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback),
LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback),
LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback),
}
}
pub(crate) fn with_base_mut(&mut self, callback: impl Fn(&mut LayoutBoxBase)) {
match self {
LayoutBox::DisplayContents(..) => {},
LayoutBox::BlockLevel(block_level_box) => {
block_level_box.borrow_mut().with_base_mut(callback);
},
LayoutBox::InlineLevel(inline_items) => {
for inline_item in inline_items {
inline_item.borrow_mut().with_base_mut(&callback);
}
},
LayoutBox::FlexLevel(flex_level_box) => {
flex_level_box.borrow_mut().with_base_mut(callback)
},
LayoutBox::TableLevelBox(table_level_box) => table_level_box.with_base_mut(callback),
LayoutBox::TaffyItemBox(taffy_item_box) => {
taffy_item_box.borrow_mut().with_base_mut(callback)
},
}
}
fn repair_style(
&self,
context: &SharedStyleContext,
node: &ServoLayoutNode,
node: &ServoThreadSafeLayoutNode,
new_style: &ServoArc<ComputedValues>,
) {
match self {
LayoutBox::DisplayContents(inline_shared_styles) => {
*inline_shared_styles.style.borrow_mut() = new_style.clone();
*inline_shared_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
*inline_shared_styles.selected.borrow_mut() = node.selected_style();
},
LayoutBox::BlockLevel(block_level_box) => {
block_level_box
@@ -171,7 +234,7 @@ pub struct DOMLayoutData(AtomicRefCell<InnerDOMLayoutData>);
// The implementation of this trait allows the data to be stored in the DOM.
impl LayoutDataTrait for DOMLayoutData {}
impl GenericLayoutDataTrait for DOMLayoutData {
fn as_any(&self) -> &dyn Any {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
@@ -193,14 +256,6 @@ impl From<ArcRefCell<Option<LayoutBox>>> for BoxSlot<'_> {
/// A mutable reference to a `LayoutBox` stored in a DOM element.
impl BoxSlot<'_> {
pub(crate) fn dummy() -> Self {
let slot = None;
Self {
slot,
marker: PhantomData,
}
}
pub(crate) fn set(mut self, box_: LayoutBox) {
if let Some(slot) = &mut self.slot {
*slot.borrow_mut() = Some(box_);
@@ -232,13 +287,12 @@ pub(crate) trait NodeExt<'dom> {
fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>;
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
fn as_svg(&self) -> Option<SVGElementData>;
fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
fn element_box_slot(&self) -> BoxSlot<'dom>;
fn pseudo_element_box_slot(&self, pseudo_element: PseudoElement) -> BoxSlot<'dom>;
fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
fn box_slot(&self) -> BoxSlot<'dom>;
/// Remove boxes for the element itself, and all of its pseudo-element boxes.
fn unset_all_boxes(&self);
@@ -253,10 +307,9 @@ pub(crate) trait NodeExt<'dom> {
fn take_restyle_damage(&self) -> LayoutDamage;
}
impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
impl<'dom> NodeExt<'dom> for ServoThreadSafeLayoutNode<'dom> {
fn as_image(&self) -> Option<(Option<Image>, PhysicalSize<f64>)> {
let node = self.to_threadsafe();
let (resource, metadata) = node.image_data()?;
let (resource, metadata) = self.image_data()?;
let (width, height) = resource
.as_ref()
.map(|image| {
@@ -266,16 +319,19 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
.or_else(|| metadata.map(|metadata| (metadata.width, metadata.height)))
.unwrap_or((0, 0));
let (mut width, mut height) = (width as f64, height as f64);
if let Some(density) = node.image_density().filter(|density| *density != 1.) {
if let Some(density) = self.image_density().filter(|density| *density != 1.) {
width /= density;
height /= density;
}
Some((resource, PhysicalSize::new(width, height)))
}
fn as_svg(&self) -> Option<SVGElementData> {
self.svg_data()
}
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
let node = self.to_threadsafe();
let data = node.media_data()?;
let data = self.media_data()?;
let natural_size = if let Some(frame) = data.current_frame {
Some(PhysicalSize::new(frame.width.into(), frame.height.into()))
} else {
@@ -289,8 +345,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
let node = self.to_threadsafe();
let canvas_data = node.canvas_data()?;
let canvas_data = self.canvas_data()?;
let source = canvas_data.source;
Some((
CanvasInfo { source },
@@ -299,8 +354,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)> {
let node = self.to_threadsafe();
match (node.iframe_pipeline_id(), node.iframe_browsing_context_id()) {
match (self.iframe_pipeline_id(), self.iframe_browsing_context_id()) {
(Some(pipeline_id), Some(browsing_context_id)) => {
Some((pipeline_id, browsing_context_id))
},
@@ -309,8 +363,10 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_typeless_object_with_data_attribute(&self) -> Option<String> {
if LayoutNode::type_id(self) !=
ScriptLayoutNodeType::Element(LayoutElementType::HTMLObjectElement)
if self.type_id() !=
Some(ScriptLayoutNodeType::Element(
LayoutElementType::HTMLObjectElement,
))
{
return None;
}
@@ -318,7 +374,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
// TODO: This is the what the legacy layout system did, but really if Servo
// supports any `<object>` that's an image, it should support those with URLs
// and `type` attributes with image mime types.
let element = self.to_threadsafe().as_element()?;
let element = self.as_element()?;
if element.get_attr(&ns!(), &local_name!("type")).is_some() {
return None;
}
@@ -327,15 +383,11 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
.map(|string| string.to_owned())
}
fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues> {
self.to_threadsafe().style(context)
}
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
if LayoutNode::layout_data(self).is_none() {
fn ensure_inner_layout_data(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
if self.layout_data().is_none() {
self.initialize_layout_data::<DOMLayoutData>();
}
LayoutNode::layout_data(self)
self.layout_data()
.unwrap()
.as_any()
.downcast_ref::<DOMLayoutData>()
@@ -344,8 +396,8 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
.borrow_mut()
}
fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
LayoutNode::layout_data(self).map(|data| {
fn inner_layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
self.layout_data().map(|data| {
data.as_any()
.downcast_ref::<DOMLayoutData>()
.unwrap()
@@ -354,22 +406,38 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
})
}
fn element_box_slot(&self) -> BoxSlot<'dom> {
self.layout_data_mut().self_box.clone().into()
}
fn box_slot(&self) -> BoxSlot<'dom> {
let pseudo_element_chain = self.pseudo_element_chain();
let Some(primary) = pseudo_element_chain.primary else {
return self.ensure_inner_layout_data().self_box.clone().into();
};
fn pseudo_element_box_slot(&self, pseudo_element: PseudoElement) -> BoxSlot<'dom> {
let mut layout_data = self.layout_data_mut();
let box_slot = ArcRefCell::new(None);
layout_data.pseudo_boxes.push(PseudoLayoutData {
pseudo: pseudo_element,
box_slot: box_slot.clone(),
});
box_slot.into()
let Some(secondary) = pseudo_element_chain.secondary else {
let primary_layout_data = self
.ensure_inner_layout_data()
.create_pseudo_layout_data(primary);
return primary_layout_data.borrow().self_box.clone().into();
};
// It's *very* important that this not borrow the element's main
// `InnerLayoutData`. Primary pseudo-elements are processed at the same recursion
// level as the main data, so the `BoxSlot` is created sequentially with other
// primary pseudo-elements and the element itself. The secondary pseudo-element is
// one level deep, so could be happening in parallel with the primary
// pseudo-elements or main element layout.
let primary_layout_data = self
.inner_layout_data()
.expect("Should already have element InnerLayoutData here.")
.pseudo_layout_data(primary)
.expect("Should already have primary pseudo-element InnerLayoutData here");
let secondary_layout_data = primary_layout_data
.borrow_mut()
.create_pseudo_layout_data(secondary);
secondary_layout_data.borrow().self_box.clone().into()
}
fn unset_all_boxes(&self) {
let mut layout_data = self.layout_data_mut();
let mut layout_data = self.ensure_inner_layout_data();
*layout_data.self_box.borrow_mut() = None;
layout_data.pseudo_boxes.clear();
@@ -378,48 +446,31 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn unset_all_pseudo_boxes(&self) {
self.layout_data_mut().pseudo_boxes.clear();
self.ensure_inner_layout_data().pseudo_boxes.clear();
}
fn clear_fragment_layout_cache(&self) {
let data = self.layout_data_mut();
if let Some(data) = data.self_box.borrow_mut().as_ref() {
data.clear_fragment_layout_cache();
}
for pseudo_layout_data in data.pseudo_boxes.iter() {
if let Some(layout_box) = pseudo_layout_data.box_slot.borrow().as_ref() {
layout_box.clear_fragment_layout_cache();
}
if let Some(inner_layout_data) = self.inner_layout_data() {
inner_layout_data.clear_fragment_layout_cache();
}
}
fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> {
let Some(layout_data) = NodeExt::layout_data(self) else {
let Some(layout_data) = self.inner_layout_data() else {
return vec![];
};
let Some(layout_data) = layout_data.for_pseudo(pseudo_element) else {
return vec![];
};
layout_data
.as_ref()
.map(LayoutBox::fragments)
.unwrap_or_default()
match pseudo_element {
Some(pseudo_element) => layout_data
.pseudo_layout_data(pseudo_element)
.map(|pseudo_layout_data| pseudo_layout_data.borrow().fragments())
.unwrap_or_default(),
None => layout_data.fragments(),
}
}
fn repair_style(&self, context: &SharedStyleContext) {
let data = self.layout_data_mut();
if let Some(layout_object) = &*data.self_box.borrow() {
let style = self.to_threadsafe().style(context);
layout_object.repair_style(context, self, &style);
}
for pseudo_layout_data in data.pseudo_boxes.iter() {
if let Some(layout_box) = pseudo_layout_data.box_slot.borrow().as_ref() {
if let Some(node) = self.to_threadsafe().with_pseudo(pseudo_layout_data.pseudo) {
layout_box.repair_style(context, self, &node.style(context));
}
}
if let Some(layout_data) = self.inner_layout_data() {
layout_data.repair_style(self, context);
}
}

View File

@@ -3,17 +3,18 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::iter::FusedIterator;
use fonts::ByteIndex;
use html5ever::{LocalName, local_name};
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use html5ever::LocalName;
use layout_api::wrapper_traits::{
PseudoElementChain, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
use layout_api::{LayoutDamage, LayoutElementType, LayoutNodeType};
use range::Range;
use script::layout_dom::ServoLayoutNode;
use script::layout_dom::ServoThreadSafeLayoutNode;
use selectors::Element as SelectorsElement;
use servo_arc::Arc as ServoArc;
use style::dom::{NodeInfo, TElement, TNode, TShadowRoot};
use style::dom::NodeInfo;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use style::values::generics::counters::{Content, ContentItem};
@@ -22,7 +23,6 @@ use style::values::specified::Quotes;
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, LayoutBox, NodeExt};
use crate::flow::inline::SharedInlineStyles;
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag};
use crate::quotes::quotes_for_lang;
use crate::replaced::ReplacedContents;
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside};
@@ -31,113 +31,44 @@ use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOuts
/// avoid having to repeat the same arguments in argument lists.
#[derive(Clone)]
pub(crate) struct NodeAndStyleInfo<'dom> {
pub node: ServoLayoutNode<'dom>,
pub pseudo_element_type: Option<PseudoElement>,
pub node: ServoThreadSafeLayoutNode<'dom>,
pub style: ServoArc<ComputedValues>,
pub damage: LayoutDamage,
}
impl<'dom> NodeAndStyleInfo<'dom> {
pub(crate) fn new(
node: ServoLayoutNode<'dom>,
node: ServoThreadSafeLayoutNode<'dom>,
style: ServoArc<ComputedValues>,
damage: LayoutDamage,
) -> Self {
Self {
node,
pseudo_element_type: None,
style,
damage,
}
}
pub(crate) fn is_single_line_text_input(&self) -> bool {
self.node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLInputElement)
pub(crate) fn pseudo_element_chain(&self) -> PseudoElementChain {
self.node.pseudo_element_chain()
}
pub(crate) fn pseudo(
pub(crate) fn with_pseudo_element(
&self,
context: &LayoutContext,
pseudo_element_type: PseudoElement,
) -> Option<Self> {
let style = self
.node
.to_threadsafe()
.as_element()?
.with_pseudo(pseudo_element_type)?
.style(&context.style_context);
let element = self.node.as_element()?.with_pseudo(pseudo_element_type)?;
let style = element.style(&context.style_context);
Some(NodeAndStyleInfo {
node: self.node,
pseudo_element_type: Some(pseudo_element_type),
node: element.as_node(),
style,
damage: self.damage,
})
}
pub(crate) fn get_selected_style(&self) -> ServoArc<ComputedValues> {
self.node.to_threadsafe().selected_style()
}
pub(crate) fn get_selection_range(&self) -> Option<Range<ByteIndex>> {
self.node.to_threadsafe().selection()
}
}
impl<'dom> From<&NodeAndStyleInfo<'dom>> for BaseFragmentInfo {
fn from(info: &NodeAndStyleInfo<'dom>) -> Self {
let node = info.node;
let pseudo = info.pseudo_element_type;
let threadsafe_node = node.to_threadsafe();
let mut flags = FragmentFlags::empty();
// Anonymous boxes should not have a tag, because they should not take part in hit testing.
//
// TODO(mrobinson): It seems that anonymous boxes should take part in hit testing in some
// cases, but currently this means that the order of hit test results isn't as expected for
// some WPT tests. This needs more investigation.
if matches!(
pseudo,
Some(PseudoElement::ServoAnonymousBox) |
Some(PseudoElement::ServoAnonymousTable) |
Some(PseudoElement::ServoAnonymousTableCell) |
Some(PseudoElement::ServoAnonymousTableRow)
) {
return Self::anonymous();
}
if let Some(element) = threadsafe_node.as_html_element() {
if element.is_body_element_of_html_element_root() {
flags.insert(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
}
match element.get_local_name() {
&local_name!("br") => {
flags.insert(FragmentFlags::IS_BR_ELEMENT);
},
&local_name!("table") | &local_name!("th") | &local_name!("td") => {
flags.insert(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT);
},
_ => {},
}
if matches!(
element.type_id(),
Some(LayoutNodeType::Element(
LayoutElementType::HTMLInputElement | LayoutElementType::HTMLTextAreaElement
))
) {
flags.insert(FragmentFlags::IS_TEXT_CONTROL);
}
if ThreadSafeLayoutElement::is_root(&element) {
flags.insert(FragmentFlags::IS_ROOT_ELEMENT);
}
};
Self {
tag: Some(Tag::new_pseudo(threadsafe_node.opaque(), pseudo)),
flags,
}
self.node.selection()
}
}
@@ -194,29 +125,26 @@ fn traverse_children_of<'dom>(
) {
traverse_eager_pseudo_element(PseudoElement::Before, parent_element_info, context, handler);
// TODO(stevennovaryo): In the past we are rendering text input as a normal element,
// and the processing of text is happening here. Remove this
// special case after the implementation of UA Shadow DOM for
// all affected input elements.
if parent_element_info.node.is_text_input() {
let node_text_content = parent_element_info.node.to_threadsafe().node_text_content();
let node_text_content = parent_element_info.node.node_text_content();
if node_text_content.is_empty() {
// The addition of zero-width space here forces the text input to have an inline formatting
// context that might otherwise be trimmed if there's no text. This is important to ensure
// that the input element is at least as tall as the line gap of the caret:
// <https://drafts.csswg.org/css-ui/#element-with-default-preferred-size>.
//
// This is also used to ensure that the caret will still be rendered when the input is empty.
// TODO: Is there a less hacky way to do this?
handler.handle_text(parent_element_info, "\u{200B}".into());
} else {
handler.handle_text(parent_element_info, node_text_content);
}
} else {
for child in iter_child_nodes(parent_element_info.node) {
for child in parent_element_info.node.children() {
if child.is_text_node() {
let info = NodeAndStyleInfo::new(
child,
child.style(&context.style_context),
child.take_restyle_damage(),
);
handler.handle_text(&info, child.to_threadsafe().node_text_content());
handler.handle_text(&info, child.node_text_content());
} else if child.is_element() {
traverse_element(child, context, handler);
}
@@ -227,7 +155,7 @@ fn traverse_children_of<'dom>(
}
fn traverse_element<'dom>(
element: ServoLayoutNode<'dom>,
element: ServoThreadSafeLayoutNode<'dom>,
context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom>,
) {
@@ -248,7 +176,7 @@ fn traverse_element<'dom>(
} else {
let shared_inline_styles: SharedInlineStyles = (&info).into();
element
.element_box_slot()
.box_slot()
.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
handler.enter_display_contents(shared_inline_styles);
@@ -261,16 +189,16 @@ fn traverse_element<'dom>(
Contents::Replaced(replaced)
} else if matches!(
element.type_id(),
LayoutNodeType::Element(
Some(LayoutNodeType::Element(
LayoutElementType::HTMLInputElement | LayoutElementType::HTMLTextAreaElement
)
))
) {
NonReplacedContents::OfTextControl.into()
} else {
NonReplacedContents::OfElement.into()
};
let display = display.used_value_for_contents(&contents);
let box_slot = element.element_box_slot();
let box_slot = element.box_slot();
handler.handle_element(&info, display, contents, box_slot);
},
}
@@ -286,7 +214,8 @@ fn traverse_eager_pseudo_element<'dom>(
// If this node doesn't have this eager pseudo-element, exit early. This depends on
// the style applied to the element.
let Some(pseudo_element_info) = node_info.pseudo(context, pseudo_element_type) else {
let Some(pseudo_element_info) = node_info.with_pseudo_element(context, pseudo_element_type)
else {
return;
};
if pseudo_element_info.style.ineffective_content_property() {
@@ -297,9 +226,7 @@ fn traverse_eager_pseudo_element<'dom>(
Display::None => {},
Display::Contents => {
let items = generate_pseudo_element_content(&pseudo_element_info, context);
let box_slot = pseudo_element_info
.node
.pseudo_element_box_slot(pseudo_element_type);
let box_slot = pseudo_element_info.node.box_slot();
let shared_inline_styles: SharedInlineStyles = (&pseudo_element_info).into();
box_slot.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
@@ -309,9 +236,7 @@ fn traverse_eager_pseudo_element<'dom>(
},
Display::GeneratingBox(display) => {
let items = generate_pseudo_element_content(&pseudo_element_info, context);
let box_slot = pseudo_element_info
.node
.pseudo_element_box_slot(pseudo_element_type);
let box_slot = pseudo_element_info.node.box_slot();
let contents = NonReplacedContents::OfPseudoElement(items).into();
handler.handle_element(&pseudo_element_info, display, contents, box_slot);
},
@@ -330,7 +255,7 @@ fn traverse_pseudo_element_contents<'dom>(
PseudoElementContentItem::Text(text) => handler.handle_text(info, text.into()),
PseudoElementContentItem::Replaced(contents) => {
let anonymous_info = anonymous_info.get_or_insert_with(|| {
info.pseudo(context, PseudoElement::ServoAnonymousBox)
info.with_pseudo_element(context, PseudoElement::ServoAnonymousBox)
.unwrap_or_else(|| info.clone())
});
let display_inline = DisplayGeneratingBox::OutsideInside {
@@ -348,8 +273,7 @@ fn traverse_pseudo_element_contents<'dom>(
anonymous_info,
display_inline,
Contents::Replaced(contents),
info.node
.pseudo_element_box_slot(PseudoElement::ServoAnonymousBox),
anonymous_info.node.box_slot(),
)
},
}
@@ -414,7 +338,6 @@ fn generate_pseudo_element_content(
ContentItem::Attr(attr) => {
let element = pseudo_element_info
.node
.to_threadsafe()
.as_element()
.expect("Expected an element");
@@ -485,44 +408,3 @@ fn generate_pseudo_element_content(
Content::Normal | Content::None => unreachable!(),
}
}
pub enum ChildNodeIterator<'dom> {
/// Iterating over the children of a node
Node(Option<ServoLayoutNode<'dom>>),
/// Iterating over the assigned nodes of a `HTMLSlotElement`
Slottables(<Vec<ServoLayoutNode<'dom>> as IntoIterator>::IntoIter),
}
pub(crate) fn iter_child_nodes(parent: ServoLayoutNode<'_>) -> ChildNodeIterator<'_> {
if let Some(element) = parent.as_element() {
if let Some(shadow) = element.shadow_root() {
return iter_child_nodes(shadow.as_node());
};
let slotted_nodes = element.slotted_nodes();
if !slotted_nodes.is_empty() {
#[allow(clippy::unnecessary_to_owned)] // Clippy is wrong.
return ChildNodeIterator::Slottables(slotted_nodes.to_owned().into_iter());
}
}
let first = parent.first_child();
ChildNodeIterator::Node(first)
}
impl<'dom> Iterator for ChildNodeIterator<'dom> {
type Item = ServoLayoutNode<'dom>;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Node(node) => {
let old = *node;
*node = old?.next_sibling();
old
},
Self::Slottables(slots) => slots.next(),
}
}
}
impl FusedIterator for ChildNodeIterator<'_> {}

View File

@@ -31,19 +31,17 @@ use crate::formatting_contexts::Baselines;
use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
};
use crate::geom::{AuOrAuto, LazySize, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes};
use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{
AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
};
use crate::sizing::{
ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, IntrinsicSizingMode,
LazySize, Size, SizeConstraint, Sizes,
};
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
SizeConstraint,
};
use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
/// Layout parameters and intermediate results about a flex container,
/// grouped to avoid passing around many parameters
@@ -58,9 +56,7 @@ struct FlexContext<'a> {
struct FlexItem<'a> {
box_: &'a FlexItemBox,
/// The preferred, min and max inner cross sizes. If the flex container is single-line
/// and [`Self::cross_size_stretches_to_line`] is true, then the preferred cross size
/// is set to [`Size::Stretch`].
/// The preferred, min and max inner cross sizes.
content_cross_sizes: Sizes,
padding: FlexRelativeSides<Au>,
@@ -99,17 +95,10 @@ struct FlexItem<'a> {
/// <https://drafts.csswg.org/css-sizing-4/#preferred-aspect-ratio>
preferred_aspect_ratio: Option<AspectRatio>,
/// Whether the preferred cross size of the item stretches to fill the flex line.
/// This happens when the size computes to `auto`, the used value of `align-self`
/// is `stretch`, and neither of the cross-axis margins are `auto`.
/// <https://drafts.csswg.org/css-flexbox-1/#stretched>
///
/// Note the following sizes are not sufficient:
/// - A size that only behaves as `auto` (like a cyclic percentage).
/// The computed value needs to be `auto` too.
/// - A `stretch` size. It stretches to the containing block, not to the line
/// (under discussion in <https://github.com/w3c/csswg-drafts/issues/11784>).
cross_size_stretches_to_line: bool,
/// The automatic size in the cross axis.
/// <https://drafts.csswg.org/css-sizing-3/#automatic-size>
automatic_cross_size: Size<Au>,
automatic_cross_size_for_intrinsic_sizing: Size<Au>,
}
/// Child of a FlexContainer. Can either be absolutely positioned, or not. If not,
@@ -128,51 +117,20 @@ struct FlexItemLayoutResult {
// Either the first or the last baseline, depending on align-self.
baseline_relative_to_margin_box: Option<Au>,
// The content size of this layout. For replaced elements this is known before layout,
// but for non-replaced it's only known after layout.
content_size: LogicalVec2<Au>,
// The content size of this layout in the block axis. This is known before layout
// for replaced elements, but for non-replaced it's only known after layout.
content_block_size: Au,
// The containing block inline size used to generate this layout.
containing_block_inline_size: Au,
// The containing block block size used to generate this layout.
containing_block_block_size: SizeConstraint,
// The containing block size used to generate this layout.
containing_block_size: ContainingBlockSize,
// Whether or not this layout depended on block constraints.
depends_on_block_constraints: bool,
// Whether or not this layout had a child that dependeded on block constraints.
has_child_which_depends_on_block_constraints: bool,
// The specific layout info that this flex item had.
specific_layout_info: Option<SpecificLayoutInfo>,
}
impl FlexItemLayoutResult {
fn compatible_with_containing_block_size(&self, containing_block: &ContainingBlock) -> bool {
if containing_block.size.inline == self.containing_block_inline_size &&
(containing_block.size.block == self.containing_block_block_size ||
(!self.depends_on_block_constraints &&
!self.has_child_which_depends_on_block_constraints))
{
return true;
}
#[cfg(feature = "tracing")]
tracing::warn!(
name: "NonReplaced stretch cache miss",
cached_inline = ?self.containing_block_inline_size,
cached_block = ?self.containing_block_block_size,
required_inline = ?containing_block.size.inline,
required_block = ?containing_block.size.block,
depends_on_block_constraints = self.depends_on_block_constraints,
has_child_which_depends_on_block_constraints = self.has_child_which_depends_on_block_constraints,
);
false
}
}
/// A data structure to hold all of the information about a flex item that has been placed
/// into a flex line. This happens once the item is laid out and its line has been determined.
struct FlexLineItem<'a> {
@@ -627,7 +585,6 @@ impl FlexContainer {
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
depends_on_block_constraints: bool,
lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
let mut flex_context = FlexContext {
@@ -953,6 +910,20 @@ impl FlexContainer {
.or(all_baselines.last),
};
// TODO: `depends_on_block_constraints` could be false in some corner cases
// in order to improve performance.
// - In a single-line column container where all items have the grow and shrink
// factors set to zero and the flex basis doesn't depend on block constraints,
// and `justify-content` is `start` or equivalent.
// This is unlikely because the flex shrink factor defaults to 1.
// - In a single-line row container where all items have `align-self: start` or
// equivalent, and the cross size doesn't depend on block constraints.
// This is unlikely because `align-self` stretches by default.
// - In a multi-line row container where `align-content` is `start` or equivalent,
// and no item cross size depends on block constraints.
// This is unlikely because `align-content` defaults to `stretch`.
let depends_on_block_constraints = true;
CacheableLayoutResult {
fragments,
content_block_size,
@@ -1030,7 +1001,7 @@ impl FlexContainer {
}
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
LayoutStyle::Default(&self.style)
}
}
@@ -1179,7 +1150,11 @@ fn do_initial_flex_line_layout<'items>(
// We didn't reach the end of the last line, so add all remaining items there.
lines.push((items, line_size_so_far));
lines.par_drain(..).map(construct_line).collect()
if flex_context.layout_context.use_rayon {
lines.par_drain(..).map(construct_line).collect()
} else {
lines.drain(..).map(construct_line).collect()
}
}
/// The result of splitting the flex items into lines using their intrinsic sizes and doing an
@@ -1216,19 +1191,13 @@ impl InitialFlexLineLayout<'_> {
items
.par_iter()
.zip(&item_used_main_sizes)
.map(|(item, used_main_size)| {
item.layout(*used_main_size, flex_context, None, None)
.unwrap()
})
.map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
.collect()
} else {
items
.iter()
.zip(&item_used_main_sizes)
.map(|(item, used_main_size)| {
item.layout(*used_main_size, flex_context, None, None)
.unwrap()
})
.map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
.collect()
};
@@ -1558,42 +1527,63 @@ impl InitialFlexLineLayout<'_> {
let mut item_used_cross_sizes = Vec::with_capacity(item_count);
let mut item_margins = Vec::with_capacity(item_count);
for item in self.items.iter_mut() {
let used_cross_size = if item.item.cross_size_stretches_to_line {
let (axis, content_size) = match flex_context.config.flex_axis {
FlexAxis::Row => (Direction::Block, item.layout_result.content_size.block),
FlexAxis::Column => (Direction::Inline, item.layout_result.content_size.inline),
};
item.item.content_cross_sizes.resolve(
axis,
Size::Stretch,
Au::zero,
Some(final_line_cross_size - item.item.pbm_auto_is_zero.cross),
|| content_size.into(),
// Tables have a special sizing in the block axis in that handles collapsed rows,
// but it would prevent stretching. So we only recognize tables in the inline axis.
// The interaction of collapsed table tracks and the flexbox algorithms is unclear,
// see https://github.com/w3c/csswg-drafts/issues/11408.
item.item.box_.independent_formatting_context.is_table() &&
axis == Direction::Inline,
)
} else {
item.layout_result.hypothetical_cross_size
let cross_axis = match flex_context.config.flex_axis {
FlexAxis::Row => Direction::Block,
FlexAxis::Column => Direction::Inline,
};
let layout = &mut item.layout_result;
let get_content_size = || match cross_axis {
Direction::Block => layout.content_block_size.into(),
Direction::Inline => item
.item
.inline_content_sizes(flex_context, item.used_main_size),
};
let used_cross_size = item.item.content_cross_sizes.resolve(
cross_axis,
item.item.automatic_cross_size,
Au::zero,
Some(Au::zero().max(final_line_cross_size - item.item.pbm_auto_is_zero.cross)),
get_content_size,
// Tables have a special sizing in the block axis in that handles collapsed rows,
// but it would prevent stretching. So we only recognize tables in the inline axis.
// The interaction of collapsed table tracks and the flexbox algorithms is unclear,
// see https://github.com/w3c/csswg-drafts/issues/11408.
item.item.box_.independent_formatting_context.is_table() &&
cross_axis == Direction::Inline,
);
item_used_cross_sizes.push(used_cross_size);
// “If the flex item has `align-self: stretch`, redo layout for its contents,
// treating this used size as its definite cross size so that percentage-sized
// children can be resolved.”
if item.item.cross_size_stretches_to_line {
let new_layout = item.item.layout(
item.used_main_size,
flex_context,
Some(used_cross_size),
Some(&mut item.layout_result),
// However, as resolved in https://github.com/w3c/csswg-drafts/issues/11784,
// we do that when the cross size is `stretch`. We also need to do it if the
// inline size changes, which may happen with a `fit-content` cross size.
let needs_new_layout = match cross_axis {
Direction::Block => {
(match item.item.content_cross_sizes.preferred {
Size::Initial => item.item.automatic_cross_size == Size::Stretch,
Size::Stretch => true,
_ => false,
}) && SizeConstraint::Definite(used_cross_size) !=
layout.containing_block_size.block &&
layout.depends_on_block_constraints
},
Direction::Inline => used_cross_size != layout.containing_block_size.inline,
};
if needs_new_layout {
#[cfg(feature = "tracing")]
tracing::warn!(
name: "Flex item stretch cache miss",
cached_inline = ?layout.containing_block_size.inline,
cached_block = ?layout.containing_block_size.block,
required_cross_size = ?used_cross_size,
cross_axis = ?cross_axis,
depends_on_block_constraints = layout.depends_on_block_constraints,
);
if let Some(layout) = new_layout {
item.layout_result = layout;
}
*layout =
item.item
.layout(item.used_main_size, flex_context, Some(used_cross_size));
}
let baseline = item.get_or_synthesize_baseline_with_cross_size(used_cross_size);
@@ -1736,7 +1726,6 @@ impl FlexItem<'_> {
fields(
self_address = self as *const _ as usize,
box_address = self.box_ as *const _ as usize,
for_stretch = non_stretch_layout_result.is_some()
)
)]
#[allow(clippy::too_many_arguments)]
@@ -1745,8 +1734,7 @@ impl FlexItem<'_> {
used_main_size: Au,
flex_context: &FlexContext,
used_cross_size_override: Option<Au>,
non_stretch_layout_result: Option<&mut FlexItemLayoutResult>,
) -> Option<FlexItemLayoutResult> {
) -> FlexItemLayoutResult {
let containing_block = flex_context.containing_block;
let independent_formatting_context = &self.box_.independent_formatting_context;
let is_table = independent_formatting_context.is_table();
@@ -1764,9 +1752,6 @@ impl FlexItem<'_> {
let cross_size = match used_cross_size_override {
Some(s) => SizeConstraint::Definite(s),
None => {
// This means that an auto size with stretch alignment will behave different than
// a stretch size. That's not what the spec says, but matches other browsers.
// To be discussed in https://github.com/w3c/csswg-drafts/issues/11784.
let stretch_size = containing_block
.size
.block
@@ -1797,22 +1782,12 @@ impl FlexItem<'_> {
let cross_size = used_cross_size_override.unwrap_or_else(|| {
let stretch_size =
Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross);
let get_content_size = || {
let constraint_space = ConstraintSpace::new(
SizeConstraint::Definite(used_main_size),
item_writing_mode,
self.preferred_aspect_ratio,
);
independent_formatting_context
.inline_content_sizes(flex_context.layout_context, &constraint_space)
.sizes
};
self.content_cross_sizes.resolve(
Direction::Inline,
Size::FitContent,
Au::zero,
Some(stretch_size),
get_content_size,
|| self.inline_content_sizes(flex_context, used_main_size),
is_table,
)
});
@@ -1841,20 +1816,11 @@ impl FlexItem<'_> {
style: item_style,
};
if non_stretch_layout_result.is_some_and(|old_result| {
old_result.compatible_with_containing_block_size(&item_as_containing_block)
}) {
return None;
}
let lazy_block_size = if !cross_axis_is_item_block_axis {
used_main_size.into()
} else if let Some(cross_size) = used_cross_size_override {
cross_size.into()
} else {
// This means that an auto size with stretch alignment will behave different than
// a stretch size. That's not what the spec says, but matches other browsers.
// To be discussed in https://github.com/w3c/csswg-drafts/issues/11784.
let stretch_size = containing_block
.size
.block
@@ -1876,9 +1842,6 @@ impl FlexItem<'_> {
&item_as_containing_block,
containing_block,
self.preferred_aspect_ratio,
flex_axis == FlexAxis::Column ||
self.cross_size_stretches_to_line ||
self.depends_on_block_constraints,
&lazy_block_size,
);
let CacheableLayoutResult {
@@ -1890,14 +1853,6 @@ impl FlexItem<'_> {
..
} = layout;
let has_child_which_depends_on_block_constraints = fragments.iter().any(|fragment| {
fragment.base().is_some_and(|base| {
base.flags.contains(
FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
)
})
});
let hypothetical_cross_size = if cross_axis_is_item_block_axis {
lazy_block_size.resolve(|| content_block_size)
} else {
@@ -1929,21 +1884,16 @@ impl FlexItem<'_> {
_ => None,
};
Some(FlexItemLayoutResult {
FlexItemLayoutResult {
hypothetical_cross_size,
fragments,
positioning_context,
baseline_relative_to_margin_box,
content_size: LogicalVec2 {
inline: item_as_containing_block.size.inline,
block: content_block_size,
},
containing_block_inline_size: item_as_containing_block.size.inline,
containing_block_block_size: item_as_containing_block.size.block,
content_block_size,
containing_block_size: item_as_containing_block.size,
depends_on_block_constraints,
has_child_which_depends_on_block_constraints,
specific_layout_info,
})
}
}
fn synthesized_baseline_relative_to_margin_box(&self, content_size: Au) -> Au {
@@ -2077,6 +2027,15 @@ impl FlexItem<'_> {
};
outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
}
#[inline]
fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
self.box_.inline_content_sizes(
flex_context,
SizeConstraint::Definite(block_size),
self.preferred_aspect_ratio,
)
}
}
impl FlexItemBox {
@@ -2087,7 +2046,7 @@ impl FlexItemBox {
content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
config: &FlexContainerConfig,
flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
) -> FlexItem {
) -> FlexItem<'_> {
let flex_axis = config.flex_axis;
let style = self.style();
let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
@@ -2121,28 +2080,31 @@ impl FlexItemBox {
main: padding_border.main,
cross: padding_border.cross,
} + margin_auto_is_zero.sum_by_axis();
let (content_main_sizes, mut content_cross_sizes, cross_size_computes_to_auto) =
match flex_axis {
FlexAxis::Row => (
&content_box_sizes.inline,
content_box_sizes.block.clone(),
preferred_size_computes_to_auto.block,
),
FlexAxis::Column => (
&content_box_sizes.block,
content_box_sizes.inline.clone(),
preferred_size_computes_to_auto.inline,
),
};
let cross_size_stretches_to_line = cross_size_computes_to_auto &&
item_with_auto_cross_size_stretches_to_line_size(align_self, &margin);
if cross_size_stretches_to_line && config.container_is_single_line {
// <https://drafts.csswg.org/css-flexbox-1/#definite-sizes>
// > If a single-line flex container has a definite cross size, the automatic preferred
// > outer cross size of any stretched flex items is the flex containers inner cross size.
// Therefore, set it to `stretch`, which has the desired behavior.
content_cross_sizes.preferred = Size::Stretch;
}
let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
{
FlexAxis::Row => (
&content_box_sizes.inline,
&content_box_sizes.block,
preferred_size_computes_to_auto.block,
),
FlexAxis::Column => (
&content_box_sizes.block,
&content_box_sizes.inline,
preferred_size_computes_to_auto.inline,
),
};
let automatic_cross_size = if cross_size_computes_to_auto &&
item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
{
Size::Stretch
} else {
Size::FitContent
};
let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
automatic_cross_size
} else {
Size::FitContent
};
let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
let stretch_size = FlexRelativeVec2 {
main: containing_block_size
@@ -2163,7 +2125,7 @@ impl FlexItemBox {
let (preferred_cross_size, min_cross_size, max_cross_size) =
if let Some(cross_content_size) = tentative_cross_content_size {
let (preferred, min, max) = content_cross_sizes.resolve_each(
Size::FitContent,
automatic_cross_size_for_intrinsic_sizing,
Au::zero,
stretch_size.cross,
|| cross_content_size,
@@ -2172,7 +2134,7 @@ impl FlexItemBox {
(Some(preferred), min, max)
} else {
content_cross_sizes.resolve_each_extrinsic(
Size::FitContent,
automatic_cross_size_for_intrinsic_sizing,
Au::zero(),
stretch_size.cross,
)
@@ -2237,7 +2199,7 @@ impl FlexItemBox {
&pbm_auto_is_zero,
content_box_sizes,
preferred_aspect_ratio,
content_cross_sizes.preferred == Size::Stretch,
automatic_cross_size_for_intrinsic_sizing,
IntrinsicSizingMode::Size,
)
.into()
@@ -2306,7 +2268,7 @@ impl FlexItemBox {
FlexItem {
box_: self,
content_cross_sizes,
content_cross_sizes: content_cross_sizes.clone(),
padding,
border,
margin: config.sides_to_flex_relative(pbm.margin),
@@ -2320,7 +2282,8 @@ impl FlexItemBox {
align_self,
depends_on_block_constraints: *depends_on_block_constraints,
preferred_aspect_ratio,
cross_size_stretches_to_line,
automatic_cross_size,
automatic_cross_size_for_intrinsic_sizing,
}
}
@@ -2339,12 +2302,12 @@ impl FlexItemBox {
// TODO: when laying out a column container with an indefinite main size,
// we compute the base sizes of the items twice. We should consider caching.
let FlexItem {
content_cross_sizes,
flex_base_size,
content_min_main_size,
content_max_main_size,
pbm_auto_is_zero,
preferred_aspect_ratio,
automatic_cross_size_for_intrinsic_sizing,
..
} = self.to_flex_item(
layout_context,
@@ -2371,7 +2334,7 @@ impl FlexItemBox {
layout_context,
containing_block,
&auto_minimum,
content_cross_sizes.preferred == Size::Stretch,
automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
);
(sizes, depends_on_block_constraints)
},
@@ -2381,7 +2344,7 @@ impl FlexItemBox {
&pbm_auto_is_zero,
&content_box_sizes_and_pbm.content_box_sizes,
preferred_aspect_ratio,
content_cross_sizes.preferred == Size::Stretch,
automatic_cross_size_for_intrinsic_sizing,
IntrinsicSizingMode::Contribution,
);
(size.into(), true)
@@ -2538,7 +2501,7 @@ impl FlexItemBox {
pbm_auto_is_zero: &FlexRelativeVec2<Au>,
content_box_sizes: &LogicalVec2<Sizes>,
preferred_aspect_ratio: Option<AspectRatio>,
cross_size_stretches_to_container_size: bool,
automatic_inline_size: Size<Au>,
intrinsic_sizing_mode: IntrinsicSizingMode,
) -> Au {
let content_block_size = || {
@@ -2553,26 +2516,18 @@ impl FlexItemBox {
// TODO: This is wrong if the item writing mode is different from the flex
// container's writing mode.
let inline_size = {
let initial_behavior = if cross_size_stretches_to_container_size {
Size::Stretch
} else {
Size::FitContent
};
let stretch_size =
flex_context.containing_block.size.inline - pbm_auto_is_zero.cross;
let get_content_size = || {
let constraint_space = ConstraintSpace::new(
self.inline_content_sizes(
flex_context,
tentative_block_size,
style.writing_mode,
preferred_aspect_ratio,
);
self.independent_formatting_context
.inline_content_sizes(flex_context.layout_context, &constraint_space)
.sizes
)
};
content_box_sizes.inline.resolve(
Direction::Inline,
initial_behavior,
automatic_inline_size,
Au::zero,
Some(stretch_size),
get_content_size,
@@ -2593,7 +2548,6 @@ impl FlexItemBox {
&item_as_containing_block,
flex_context.containing_block,
preferred_aspect_ratio,
false, /* depends_on_block_constraints */
&LazySize::intrinsic(),
)
.content_block_size
@@ -2625,4 +2579,18 @@ impl FlexItemBox {
IntrinsicSizingMode::Size => content_block_size(),
}
}
fn inline_content_sizes(
&self,
flex_context: &FlexContext,
block_size: SizeConstraint,
preferred_aspect_ratio: Option<AspectRatio>,
) -> ContentSizes {
let writing_mode = self.independent_formatting_context.style().writing_mode;
let constraint_space =
ConstraintSpace::new(block_size, writing_mode, preferred_aspect_ratio);
self.independent_formatting_context
.inline_content_sizes(flex_context.layout_context, &constraint_space)
.sizes
}
}

View File

@@ -4,7 +4,7 @@
use geom::{FlexAxis, MainStartCrossStart};
use malloc_size_of_derive::MallocSizeOf;
use script::layout_dom::ServoLayoutNode;
use script::layout_dom::ServoThreadSafeLayoutNode;
use servo_arc::Arc as ServoArc;
use style::context::SharedStyleContext;
use style::logical_geometry::WritingMode;
@@ -22,7 +22,8 @@ use crate::context::LayoutContext;
use crate::dom::LayoutBox;
use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{BaseFragmentInfo, Fragment};
use crate::fragment_tree::BaseFragmentInfo;
use crate::layout_box_base::LayoutBoxBase;
use crate::positioned::AbsolutelyPositionedBox;
mod geom;
@@ -151,6 +152,7 @@ impl FlexContainer {
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, MallocSizeOf)]
pub(crate) enum FlexLevelBox {
FlexItem(FlexItemBox),
@@ -161,7 +163,7 @@ impl FlexLevelBox {
pub(crate) fn repair_style(
&mut self,
context: &SharedStyleContext,
node: &ServoLayoutNode,
node: &ServoThreadSafeLayoutNode,
new_style: &ServoArc<ComputedValues>,
) {
match self {
@@ -189,14 +191,24 @@ impl FlexLevelBox {
}
}
pub(crate) fn fragments(&self) -> Vec<Fragment> {
pub(crate) fn with_base<T>(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T {
match self {
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
.independent_formatting_context
.base
.fragments(),
FlexLevelBox::FlexItem(flex_item_box) => {
callback(&flex_item_box.independent_formatting_context.base)
},
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
positioned_box.borrow().context.base.fragments()
callback(&positioned_box.borrow().context.base)
},
}
}
pub(crate) fn with_base_mut<T>(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T {
match self {
FlexLevelBox::FlexItem(flex_item_box) => {
callback(&mut flex_item_box.independent_formatting_context.base)
},
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
callback(&mut positioned_box.borrow_mut().context.base)
},
}
}

View File

@@ -171,11 +171,10 @@ impl BlockContainer {
if let Some((marker_info, marker_contents)) = crate::lists::make_marker(context, info) {
match marker_info.style.clone_list_style_position() {
ListStylePosition::Inside => {
builder.handle_list_item_marker_inside(&marker_info, info, marker_contents)
builder.handle_list_item_marker_inside(&marker_info, marker_contents)
},
ListStylePosition::Outside => builder.handle_list_item_marker_outside(
&marker_info,
info,
marker_contents,
info.style.clone(),
),
@@ -228,7 +227,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
self.inline_formatting_context_builder.take()?.finish(
self.context,
!self.have_already_seen_first_line_for_text_indent,
self.info.is_single_line_text_input(),
self.info.node.is_single_line_text_input(),
self.info.style.to_bidi_level(),
)
}
@@ -299,9 +298,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
}
let box_slot = table_info
.node
.pseudo_element_box_slot(PseudoElement::ServoAnonymousTable);
let box_slot = table_info.node.box_slot();
self.block_level_boxes.push(BlockLevelJob {
info: table_info,
box_slot,
@@ -408,19 +405,9 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
fn handle_list_item_marker_inside(
&mut self,
marker_info: &NodeAndStyleInfo<'dom>,
container_info: &NodeAndStyleInfo<'dom>,
contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
) {
// TODO: We do not currently support saving box slots for ::marker pseudo-elements
// that are part nested in ::before and ::after pseudo elements. For now, just
// forget about them once they are built.
let box_slot = match container_info.pseudo_element_type {
Some(_) => BoxSlot::dummy(),
None => marker_info
.node
.pseudo_element_box_slot(PseudoElement::Marker),
};
let box_slot = marker_info.node.box_slot();
self.handle_inline_level_element(
marker_info,
DisplayInside::Flow {
@@ -434,20 +421,10 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
fn handle_list_item_marker_outside(
&mut self,
marker_info: &NodeAndStyleInfo<'dom>,
container_info: &NodeAndStyleInfo<'dom>,
contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
list_item_style: Arc<ComputedValues>,
) {
// TODO: We do not currently support saving box slots for ::marker pseudo-elements
// that are part nested in ::before and ::after pseudo elements. For now, just
// forget about them once they are built.
let box_slot = match container_info.pseudo_element_type {
Some(_) => BoxSlot::dummy(),
None => marker_info
.node
.pseudo_element_box_slot(PseudoElement::Marker),
};
let box_slot = marker_info.node.box_slot();
self.block_level_boxes.push(BlockLevelJob {
info: marker_info.clone(),
box_slot,
@@ -466,6 +443,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
let (is_list_item, non_replaced_contents) = match (display_inside, contents) {
(
DisplayInside::Flow { is_list_item },
@@ -485,7 +463,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
propagated_data,
))
};
let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
let atomic = self
.ensure_inline_formatting_context_builder()
.push_atomic(construction_callback, old_layout_box);
@@ -497,7 +475,11 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
// Otherwise, this is just a normal inline box. Whatever happened before, all we need to do
// before recurring is to remember this ongoing inline level box.
self.ensure_inline_formatting_context_builder()
.start_inline_box(InlineBox::new(info), None);
.start_inline_box(
|| ArcRefCell::new(InlineBox::new(info)),
None,
old_layout_box,
);
if is_list_item {
if let Some((marker_info, marker_contents)) =
@@ -506,7 +488,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
// Ignore `list-style-position` here:
// “If the list item is an inline box: this value is equivalent to `inside`.”
// https://drafts.csswg.org/css-lists/#list-style-position-outside
self.handle_list_item_marker_inside(&marker_info, info, marker_contents)
self.handle_list_item_marker_inside(&marker_info, marker_contents)
}
}
@@ -677,21 +659,18 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
inline_formatting_context: InlineFormattingContext,
) {
let layout_context = self.context;
let info = self
let anonymous_info = self
.anonymous_box_info
.get_or_insert_with(|| {
self.info
.pseudo(layout_context, PseudoElement::ServoAnonymousBox)
.with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
.expect("Should never fail to create anonymous box")
})
.clone();
let box_slot = self
.info
.node
.pseudo_element_box_slot(PseudoElement::ServoAnonymousBox);
let box_slot = anonymous_info.node.box_slot();
self.block_level_boxes.push(BlockLevelJob {
info,
info: anonymous_info,
box_slot,
kind: BlockLevelCreator::SameFormattingContextBlock(
IntermediateBlockContainer::InlineFormattingContext(

Some files were not shown because too many files have changed in this diff Show More