WPT tests require us to reliably take screenshots when test pages are
fully loaded and testing is complete, and the WPT runner uses
[test-wait.js](a2f551eb2d/tests/wpt/tests/tools/wptrunner/wptrunner/executors/test-wait.js)
to do this in userland. when testing Servo with [--product
servo](a2f551eb2d/tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorservo.py),
we use servoshell’s --output option, which backs that up with more
reliable waiting in Servo. but when testing Servo with [--product
servodriver](a2f551eb2d/tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorservodriver.py),
we use the WebDriver Take Screenshot action, which currently takes the
screenshot immediately. we think this might be a source of regressions.
this patch makes the WebDriver actions Take Screenshot and Take Element
Screenshot use the same new WebView::take_screenshot() API as
servoshell’s --output option, such that those actions now wait for [a
variety of
conditions](a2f551eb2d/components/servo/webview.rs (L596-L602))
that may affect test output. it’s not clear if this is
[conformant](https://w3c.github.io/webdriver/#screen-capture), so we may
want to refine this to only wait when running tests at some point. other
changes:
- we remove the retry loop where we try to take a screenshot every
second for up to 30 seconds
- we send the result as a image::RgbaImage over crossbeam without shared
memory (it’s not cross-process)
- we now handle the zero-sized element case directly in the WebDriver
server
Testing: This should fix some flaky tests.
Fixes: #36715.
Fixes: (partially) #39180.
Fixes: (partially) #34683.
Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This change adds a new API to the `WebView` for capturing screenshots.
This makes it possible to:
- use the reftest waiting infrastructure via the API
easily.
- take more than a single screenshot in one Servo run.
- take screenshots, but still paint the `WebView` normally prior
to the moment that the screenshot is ready, instead of preventing
all non-screenshot-ready paints while taking a screenshot.
In addition, the previous infrastructure, the `wait_for_stable_image`
option is removed completely.
Testing: This change is tested by the passing of the WPT tests,
as they commonly use the screenshot feature.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Delan Azabani <dazabani@igalia.com>
The first epoch is 0 as that is the one used in the initial transaction,
but the code was setting the first `Epoch` to `Epoch(1)`. This means
that
when layout advanced the epoch, the `Epoch` of the first produced
display list was `Epoch(2)`.
This change makes the value reflected in `current_epoch` actually match
the index of the display list produced. In addition, we always store
this epoch in `PipelineDetails` in the renderer. This will be important
when adding the `WebView::take_screenshot` API.
Testing: This should not change behavior, so is covered by existing
tests which
rely on proper `Epoch` advancement.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Motivation: The font cache currently has to store a cache of Keys which
need to be given by the webrender instance.
Having a cache for every WebViewId in the future when we have every
webview have the different webrender::DocumentId might be too wasteful
to store this key cache per DocumentId. This proposes to include in the
WebViewId another id, the RenderingGroupId. This id can be easily
changed
to be equivalent to the DocumentId when we support multiple DocumentIds
for a unique Webrender instance.
Additionally this will keep it easier to integrate the currently out of
tree patches for multiple rendering contexts with different webrenders.
Change:
We introduce the RenderingGroupId in the WebViewId and allow a method to
extract it. The font key cache uses this cache
and forwards it to the Compositor when requesting new changes. The
compositor currently ignores this id.
Additionally, the WebView can return the RenderingGroupId. The WebViewId
also has an appropiate constructor for specifying a RenderingGroupId.
Because there currently will be only one RenderingGroupId the
performance will be minimal.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Testing: This should be covered by WPT tests and normal browsing
behavior works fine.
---------
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Moved more functions to fxhash. And provide comments about the choices
when necessary.
Testing: Hash functions shouldn't change functionality.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
When taking element screenshot, we need to convert y-coordinates to
comply with OpenGL. With dpi > 1, sometimes y can be -1 due to rounding,
resulting in panic when
660f90f687/components/shared/compositing/rendering_context.rs (L655)
This PR safely converts.
Testing: When running in headed mode with dpi > 1,
`/webdriver/tests/classic/take_element_screenshot/scroll_into_view.py`
always passes locally now without panic.
Fixes the panic:
https://github.com/servo/servo/issues/39306#issuecomment-3342204869
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
According to spec, we should wait animation frame callbacks before
taking (element) screenshot. As "element screenshot" would automatically
scroll into view, this solves intermittency.
Testing: Manually tested on pages, and
`take_element_screenshot/scroll_into_view.py` passes stably now.
Fixes: #39306
---------
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
In the past, all scroll offsets were reset when navigating between
pages,
and layouts probably didn't preserve their scroll positions between back
and forward operations. Whatever the reason, the compositor was still
trying to
reset these positions right after a load.
This is an issue, because a page can request a scroll during a load, and
this
scroll might be requested from `<iframe>` content. In that case, the
scroll
positions should be the ones that layout reflects and not cleared from
the
Compositor. This reset operation had the potential to stomp on scroll
positions
set during load.
It's quite likely that the Compositor shouldn't be trying to reset them
at all,
so just stop doing that. This removes a source of intermittency when
running
tests that set scroll positions.
Testing: Once #39475 lands after this, its test should always pass.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This change removes the `DebugOption` (`-Z`) for touch event simulation
and moves the implementation of the feature to servoshell. The resaoning
for this is:
- This is really a servoshell feature and can be implemented on top of
the API. This moves more code out of the already too-complicated
renderer.
- I would like to consolidate `DebugOptions` into a `ServoLogOptions`
to collect all options for configuring Servo logging. This requires
moving away all of the non-logging options.
- Eventually touch event simulation will be able to reuse the fling
implementation from servoshell as we are actually simulating touch
events sent to the `WebView`.
Testing: This changes a conditional feature that's used for manual
debugging.
It is difficult to write tests for this as there are no servoshell tests
that
verify input handling.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Changes function signatures to accept `ClipId`, `ExternalScrollId` and
`ScrollTreeNodeId` instead of `&ClipId`, `&ExternalScrollId` and
`&ScrollTreeNodeId`. This avoids several `&` and `*`.
Testing: not needed, no behavior change.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This should be the final PR for the Hash Function series that is
trivial.
Of note: I decided to transform `HashMapTracedValues<Atom,..>` to use
FxBuildHasher. This is likely not going to improve performance as Atom's
already have a unique u32 that is used as the Hash but it safes a few
bytes for the RandomState that is normally in the HashMap.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Testing: Hash function changes should not change functionality, we
slightly decrease the size and unit tests still work.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
FNV is faster for hashing less than 16 bytes of data and the
cryptographic properties of the default HashMap are not needed for the
various ids.
Testing: This does not change functionality.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
There were still some accesses to the inner BrowsingContextId from the
WebViewId. This changes it to completely rely on the From trait for
these methods. This also means we can make the field private.
For testing we add a way to create arbitrary WebViewIds.
Testing: Does not change functionality.
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Adds epoch to each WR image op command that is sent to compositor. The
renderer now has a `FrameDelayer` data structure that is responsible for
tracking when a frame is ready to be displayed. When asking canvases to
update their rendering, they are given an optional `Epoch` which denotes
the `Document`'s canvas epoch. When all image updates for that `Epoch`
are seen in the renderer, the frame can be displayed.
Testing: Existing WPT tests
Fixes: #35733
Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Forward any deserialization errors to the receiver, instead of panicking
on the router thread. This change was previously part of #38782, which
got reverted, since generic channels don't support custom router
callbacks yet. Propagating the error is still something we want, and
landing this separately will reduce the diff of the PR that introduces
generic callbacks.
Testing: Should be covered by existing tests. Also manually tested
https://github.com/servo/servo/issues/38939
---------
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>
Ports the channel returning the result of `GenerateFontKeys` to generic
channel
Testing: No functional changes - Covered by existing tests
Part of https://github.com/servo/servo/issues/38912
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Instead of generating a frame for every display list, which might be one
rendered frame per `<iframe>`, generate only a single frame per call to
"update the rendering." This should make rendering more efficient when
there are `<iframe>`s present and also open up optimizations for
non-display list frames.
Testing: This could potentially reduce flashing of content during
rendering
updates, but that is very difficult to test.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This reverts commit fb1c0a4c48.
Previously in `create_compositor_channel`, the [routing callback][1] was
setup so that a message received on the Compositor's IPC receiver will
be
forwarded to the local receiver using the `CompositorProxy` which also
takes care of waking up the event loop. In #38782, this was changed so
that the routing callbacks simply forwards the message directly without
going via the `CompositorProxy`. This breaks behaviours that rely on the
event loop being woken up on message sending, e.g. updating image frames
for animated gifs.
Since the GenericChannel API doesn't allow custom routing callbacks,
revert this change until we figure out a better solution.
[1]:
d2ccce6052/components/servo/lib.rs (L1114)
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Adds a new memory report that aggregates the size of all scroll trees
from within all pipelines for each web view.
Testing: Acessing `about:memory`
Fixes: #38726
---------
Signed-off-by: criskell <96352451+criskell@users.noreply.github.com>
Besides migrating the channel to GenericChannel, this PR adds
`routed_channel_with_local_sender()` to `generic_channel`. This is for
existing use-cases, where we want to provide both an IPC capable
GenericSender, as well as a crossbeam Sender, for efficient sending if
the sender is in the same process.
Testing: All of our channels should send / receive at least some
messages during WPT tests.
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
["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>
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>
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>
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>
`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>
This patch contains 2 components:
1. Instead of passing `self.pinch_zoom_level().get()` while checking
`zoom_result`, initialize it in `combined_magnification`. Ideally, this
part shouldn't have any effect on behavior.
2. Separates the logic for PinchZoom and ViewportZoom. So, when a new
page is opened, it will start with its own viewport zoom scale (rather
than the previous scale multiples).
i.e `self.pinch_zoom_level().get() * 1.0 * magnification`
```rust
let mut combined_magnification = 1.0;
...
ScrollZoomEvent::ViewportZoom(magnification) => {
combined_magnification *= magnification
},
...
let pinch_zoom_result = match self.set_pinch_zoom_level(self.pinch_zoom_level().get() * combined_magnification)
```
Testing: This change adds a new WPT test.
Fixes: #37314
---------
Signed-off-by: Shubham Gupta <shubham13297@gmail.com>
WPT tests are expected to create screenshots as soon as everything is
loaded. If an animation is happening adding the "reftest-wait" class to
the root element is appropriate way to delay the screenshot. Previously,
the
test harness was waiting for all animations to finish, but that is just
leading to many timeouts. Removing that code fixes the timeouts.
Two Servo-specific tests are also updated as they were written with
Servo's previous behavior in mind.
Testing: There are test result updates for this change. Many TIMEOUTS
now either correctly
PASS OR FAIL.
Fixes: #36931.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Similar to #37960, previously, `AvailHeight`, `AvailWidth`, `Height`,
`Width` ask compositor for screen metrics. This PR moves the request to
embedder.
This simplifies code, and reduces workload of compositor, which is
busier most of time.
Testing: No behaviour change. Updated some tests. `Width/Height` matches
other browsers.
---------
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Previously, `screenX`, `screenY`, `outerHeight`, `outerWidth`, `moveBy`,
`resizeBy` ask compositor for window rectangle, which then return
"inner" rectangle after consulting Embedder.
This PR
1. removes `GetClientWindowRect` from compositor, and directly let
script ask embedder.
2. add `window_size` to `ScreenGeometry`
3. add a lot of docs to `ScreenGeometry`
Testing: `tests\wpt\mozilla\tests\mozilla\window_resizeTo.html` can now
pass for Headed Window.
Fixes: #37824
---------
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
[RootCause]:Tochdown executes normally, but touchup fails during hittest
and does not send a touchup event to the script, causing the script to
save an incorrect number of active_touch_points. The application
receives an incorrect event.touches.length, causing a logic error and
preventing the carousel from sliding.
[Solution]:
When hit test on compositor fails, we also need to send the
EmbedderToConstellationMessage::ForwardInputEvent message to
constellation. In the script's handle_touch_event, if it exits early, we
also need to update active_touch_points.
Testing:
Fixes: #37763
---------
Signed-off-by: kongbai1996 <1782765876@qq.com>
This creates a new method in shared/compositing/lib to generate image
keys that are send over the webview. This does not immediately return
the keys but goes over the constellation to receive the keys from the
IOCompositor. To make this more efficient, we now cache the keys in
image_cache in a simple FIFO order. The old blocking method stays intact
for now but got renamed to make the blocking clear.
The blocking calls that are left are in:
- `components/canvas/canvas_data.rs`
- `components/script/dom/htmlmediaelement.rs`
Testing: WPT tests should cover this as this doesn't change any
functionality.
Fixes: Was mentioned in
https://github.com/servo/servo/issues/37161#issuecomment-2915750051 and
part of https://github.com/servo/servo/issues/37086
---------
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
Co-authored-by: gterzian <2792687+gterzian@users.noreply.github.com>
Previously, our Servo-specific spatial tree scroll offsets were opposite
to
that of WebRender and also the web platform. This is due to the fact,
likely, that `winit` wheel directionality is also flipped. This change
has both the Servo spatial tree and the API take offsets that are
consistent with the web.
Any possible changes to the meaning of wheel directionality will be
handled in a followup change.
This is a breaking change to the Servo API.
Testing: This change updates unit tests.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Before we only offered helper to add single image (no update or delete)
that got special IPC message, now we simplify this by offering all ops
helpers for dealing with single image (that happens most of the time),
that simply uses `update_images` under the hood. We also optimize for
this use case by using `SmallVec<[ImageUpdate; 1]>` to avoid alloc.
Testing: Just refactor, but code is covered by WPT tests
---------
Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>