Don't slice a sliced blob (#36866)

When slicing a blob that is already sliced we should reference it's
parent's data instead of creating a subview into the sliced blob. This
keeps the blob ancestry chain small and reduces the number of blobs that
we have to resolve.

Testing: Includes a new crashtest
Fixes: https://github.com/servo/servo/issues/36843

[try
run](https://github.com/simonwuelker/servo/actions/runs/14844873660)

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-05-06 09:25:11 +02:00 committed by GitHub
parent 3b806ca424
commit 54c2818974
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 76 additions and 69 deletions

View file

@ -7,7 +7,7 @@ use std::ptr;
use std::rc::Rc;
use base::id::{BlobId, BlobIndex};
use constellation_traits::BlobImpl;
use constellation_traits::{BlobData, BlobImpl};
use dom_struct::dom_struct;
use encoding_rs::UTF_8;
use js::jsapi::JSObject;
@ -33,7 +33,7 @@ use crate::dom::readablestream::ReadableStream;
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::CanGc;
// https://w3c.github.io/FileAPI/#blob
/// <https://w3c.github.io/FileAPI/#dfn-Blob>
#[dom_struct]
pub(crate) struct Blob {
reflector_: Reflector,
@ -198,7 +198,7 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
self.get_stream(can_gc)
}
// https://w3c.github.io/FileAPI/#slice-method-algo
/// <https://w3c.github.io/FileAPI/#slice-method-algo>
fn Slice(
&self,
start: Option<i64>,
@ -206,11 +206,24 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
content_type: Option<DOMString>,
can_gc: CanGc,
) -> DomRoot<Blob> {
let type_string =
normalize_type_string(content_type.unwrap_or(DOMString::from("")).as_ref());
let rel_pos = RelativePos::from_opts(start, end);
let blob_impl = BlobImpl::new_sliced(rel_pos, self.blob_id, type_string);
Blob::new(&self.global(), blob_impl, can_gc)
let global = self.global();
let type_string = normalize_type_string(&content_type.unwrap_or_default());
// If our parent is already a sliced blob then we reference the data from the grandparent instead,
// to keep the blob ancestry chain short.
let (parent, range) = match *global.get_blob_data(&self.blob_id) {
BlobData::Sliced(grandparent, parent_range) => {
let range = RelativePos {
start: parent_range.start + start.unwrap_or_default(),
end: end.map(|end| end + parent_range.start).or(parent_range.end),
};
(grandparent, range)
},
_ => (self.blob_id, RelativePos::from_opts(start, end)),
};
let blob_impl = BlobImpl::new_sliced(range, parent, type_string);
Blob::new(&global, blob_impl, can_gc)
}
// https://w3c.github.io/FileAPI/#text-method-algo