mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
script: Do not set up an IPC route for every image load (#35041)
Instead of setting up a route for every image load in the DOM / Layout, route all incoming image cache responses through the `ScriptThread`. This avoids creating a set of file descriptor for every image that is loaded. This change requires having the `ImageCache` track the `PipelineId` of the original the listener so that the `ScriptThread` can route it properly to the correct `Window`. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
2ce7709b8b
commit
37695c8c8c
17 changed files with 260 additions and 272 deletions
|
@ -15,16 +15,13 @@ use cssparser::{Parser, ParserInput};
|
|||
use dom_struct::dom_struct;
|
||||
use euclid::Point2D;
|
||||
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix, QualName};
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::jsapi::JSAutoRealm;
|
||||
use js::rust::HandleObject;
|
||||
use mime::{self, Mime};
|
||||
use net_traits::http_status::HttpStatus;
|
||||
use net_traits::image_cache::{
|
||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, PendingImageId,
|
||||
PendingImageResponse, UsePlaceholder,
|
||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponder, ImageResponse,
|
||||
PendingImageId, UsePlaceholder,
|
||||
};
|
||||
use net_traits::request::{
|
||||
CorsSettings, Destination, Initiator, Referrer, RequestBuilder, RequestId,
|
||||
|
@ -89,7 +86,6 @@ use crate::dom::values::UNSIGNED_LONG_MAX;
|
|||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::dom::window::Window;
|
||||
use crate::fetch::create_a_potential_cors_request;
|
||||
use crate::image_listener::{generate_cache_listener_for_element, ImageCacheListener};
|
||||
use crate::microtask::{Microtask, MicrotaskRunnable};
|
||||
use crate::network_listener::{self, PreInvoke, ResourceTimingListener};
|
||||
use crate::realms::enter_realm;
|
||||
|
@ -355,13 +351,11 @@ impl HTMLImageElement {
|
|||
/// Update the current image with a valid URL.
|
||||
fn fetch_image(&self, img_url: &ServoUrl, can_gc: CanGc) {
|
||||
let window = self.owner_window();
|
||||
let image_cache = window.image_cache();
|
||||
let sender = generate_cache_listener_for_element(self);
|
||||
let cache_result = image_cache.track_image(
|
||||
|
||||
let cache_result = window.image_cache().get_cached_image_status(
|
||||
img_url.clone(),
|
||||
window.origin().immutable().clone(),
|
||||
cors_setting_for_element(self.upcast()),
|
||||
sender,
|
||||
UsePlaceholder::Yes,
|
||||
);
|
||||
|
||||
|
@ -380,15 +374,63 @@ impl HTMLImageElement {
|
|||
self.process_image_response(ImageResponse::Loaded(image, url), can_gc)
|
||||
}
|
||||
},
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
|
||||
self.process_image_response(ImageResponse::MetadataLoaded(m), can_gc)
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
|
||||
metadata,
|
||||
id,
|
||||
)) => {
|
||||
self.process_image_response(ImageResponse::MetadataLoaded(metadata), can_gc);
|
||||
self.register_image_cache_callback(id, ChangeType::Element);
|
||||
},
|
||||
ImageCacheResult::Pending(id) => {
|
||||
self.register_image_cache_callback(id, ChangeType::Element);
|
||||
},
|
||||
ImageCacheResult::ReadyForRequest(id) => {
|
||||
self.fetch_request(img_url, id);
|
||||
self.register_image_cache_callback(id, ChangeType::Element);
|
||||
},
|
||||
ImageCacheResult::Pending(_) => (),
|
||||
ImageCacheResult::ReadyForRequest(id) => self.fetch_request(img_url, id),
|
||||
ImageCacheResult::LoadError => self.process_image_response(ImageResponse::None, can_gc),
|
||||
};
|
||||
}
|
||||
|
||||
fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
|
||||
let trusted_node = Trusted::new(self);
|
||||
let generation = self.generation_id();
|
||||
let window = self.owner_window();
|
||||
let sender = window.register_image_cache_listener(id, move |response| {
|
||||
let trusted_node = trusted_node.clone();
|
||||
let window = trusted_node.root().owner_window();
|
||||
let callback_type = change_type.clone();
|
||||
|
||||
window
|
||||
.as_global_scope()
|
||||
.task_manager()
|
||||
.networking_task_source()
|
||||
.queue(task!(process_image_response: move || {
|
||||
let element = trusted_node.root();
|
||||
|
||||
// Ignore any image response for a previous request that has been discarded.
|
||||
if generation != element.generation_id() {
|
||||
return;
|
||||
}
|
||||
|
||||
match callback_type {
|
||||
ChangeType::Element => {
|
||||
element.process_image_response(response.response, CanGc::note());
|
||||
}
|
||||
ChangeType::Environment { selected_source, selected_pixel_density } => {
|
||||
element.process_image_response_for_environment_change(
|
||||
response.response, selected_source, generation, selected_pixel_density, CanGc::note()
|
||||
);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
window
|
||||
.image_cache()
|
||||
.add_listener(ImageResponder::new(sender, window.pipeline_id(), id));
|
||||
}
|
||||
|
||||
fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
|
||||
let document = self.owner_document();
|
||||
let window = self.owner_window();
|
||||
|
@ -1054,48 +1096,6 @@ impl HTMLImageElement {
|
|||
|
||||
/// Step 2-12 of <https://html.spec.whatwg.org/multipage/#img-environment-changes>
|
||||
fn react_to_environment_changes_sync_steps(&self, generation: u32, can_gc: CanGc) {
|
||||
// TODO reduce duplicacy of this code
|
||||
|
||||
fn generate_cache_listener_for_element(
|
||||
elem: &HTMLImageElement,
|
||||
selected_source: String,
|
||||
selected_pixel_density: f64,
|
||||
) -> IpcSender<PendingImageResponse> {
|
||||
let trusted_node = Trusted::new(elem);
|
||||
let (responder_sender, responder_receiver) = ipc::channel().unwrap();
|
||||
let task_source = elem
|
||||
.owner_global()
|
||||
.task_manager()
|
||||
.networking_task_source()
|
||||
.to_sendable();
|
||||
let generation = elem.generation.get();
|
||||
|
||||
ROUTER.add_typed_route(
|
||||
responder_receiver,
|
||||
Box::new(move |message| {
|
||||
debug!("Got image {:?}", message);
|
||||
// Return the image via a message to the script thread, which marks
|
||||
// the element as dirty and triggers a reflow.
|
||||
let element = trusted_node.clone();
|
||||
let image: PendingImageResponse = message.unwrap();
|
||||
let selected_source_clone = selected_source.clone();
|
||||
task_source.queue(
|
||||
task!(process_image_response_for_environment_change: move || {
|
||||
let element = element.root();
|
||||
// Ignore any image response for a previous request that has been discarded.
|
||||
if generation == element.generation.get() {
|
||||
element.process_image_response_for_environment_change(image.response,
|
||||
USVString::from(selected_source_clone), generation,
|
||||
selected_pixel_density, CanGc::note());
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
responder_sender
|
||||
}
|
||||
|
||||
let elem = self.upcast::<Element>();
|
||||
let document = elem.owner_document();
|
||||
let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
|
||||
|
@ -1143,23 +1143,20 @@ impl HTMLImageElement {
|
|||
can_gc,
|
||||
);
|
||||
|
||||
let window = self.owner_window();
|
||||
let image_cache = window.image_cache();
|
||||
|
||||
// Step 14
|
||||
let sender = generate_cache_listener_for_element(
|
||||
self,
|
||||
selected_source.0.clone(),
|
||||
selected_pixel_density,
|
||||
);
|
||||
let cache_result = image_cache.track_image(
|
||||
let window = self.owner_window();
|
||||
let cache_result = window.image_cache().get_cached_image_status(
|
||||
img_url.clone(),
|
||||
window.origin().immutable().clone(),
|
||||
cors_setting_for_element(self.upcast()),
|
||||
sender,
|
||||
UsePlaceholder::No,
|
||||
);
|
||||
|
||||
let change_type = ChangeType::Environment {
|
||||
selected_source: selected_source.clone(),
|
||||
selected_pixel_density,
|
||||
};
|
||||
|
||||
match cache_result {
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
|
||||
// Step 15
|
||||
|
@ -1169,7 +1166,7 @@ impl HTMLImageElement {
|
|||
selected_pixel_density,
|
||||
)
|
||||
},
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
|
||||
self.process_image_response_for_environment_change(
|
||||
ImageResponse::MetadataLoaded(m),
|
||||
selected_source,
|
||||
|
@ -1177,6 +1174,7 @@ impl HTMLImageElement {
|
|||
selected_pixel_density,
|
||||
can_gc,
|
||||
);
|
||||
self.register_image_cache_callback(id, change_type);
|
||||
},
|
||||
ImageCacheResult::LoadError => {
|
||||
self.process_image_response_for_environment_change(
|
||||
|
@ -1187,8 +1185,13 @@ impl HTMLImageElement {
|
|||
can_gc,
|
||||
);
|
||||
},
|
||||
ImageCacheResult::ReadyForRequest(id) => self.fetch_request(&img_url, id),
|
||||
ImageCacheResult::Pending(_) => (),
|
||||
ImageCacheResult::ReadyForRequest(id) => {
|
||||
self.fetch_request(&img_url, id);
|
||||
self.register_image_cache_callback(id, change_type);
|
||||
},
|
||||
ImageCacheResult::Pending(id) => {
|
||||
self.register_image_cache_callback(id, change_type);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1383,6 +1386,10 @@ impl HTMLImageElement {
|
|||
.as_ref()
|
||||
.is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
|
||||
}
|
||||
|
||||
fn generation_id(&self) -> u32 {
|
||||
self.generation.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
|
@ -1882,16 +1889,6 @@ impl FormControl for HTMLImageElement {
|
|||
}
|
||||
}
|
||||
|
||||
impl ImageCacheListener for HTMLImageElement {
|
||||
fn generation_id(&self) -> u32 {
|
||||
self.generation.get()
|
||||
}
|
||||
|
||||
fn process_image_response(&self, response: ImageResponse, can_gc: CanGc) {
|
||||
self.process_image_response(response, can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
fn image_dimension_setter(element: &Element, attr: LocalName, value: u32, can_gc: CanGc) {
|
||||
// This setter is a bit weird: the IDL type is unsigned long, but it's parsed as
|
||||
// a dimension for rendering.
|
||||
|
@ -2186,3 +2183,12 @@ pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
|
|||
}
|
||||
candidates
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ChangeType {
|
||||
Environment {
|
||||
selected_source: USVString,
|
||||
selected_pixel_density: f64,
|
||||
},
|
||||
Element,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue