mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Make image cache per-document rather than global
This commit is contained in:
parent
9eb6bb78b0
commit
72d7ee613b
25 changed files with 448 additions and 649 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1755,6 +1755,7 @@ dependencies = [
|
||||||
"hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper_serde 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper_serde 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"image 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"image 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"immeta 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -87,7 +87,6 @@ use msg::constellation_msg::{FrameId, FrameType, PipelineId};
|
||||||
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
|
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
|
||||||
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
|
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
|
||||||
use net_traits::{self, IpcSend, ResourceThreads};
|
use net_traits::{self, IpcSend, ResourceThreads};
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
|
||||||
use net_traits::pub_domains::reg_host;
|
use net_traits::pub_domains::reg_host;
|
||||||
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
|
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
|
||||||
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
||||||
|
@ -175,10 +174,6 @@ pub struct Constellation<Message, LTF, STF> {
|
||||||
/// browsing.
|
/// browsing.
|
||||||
private_resource_threads: ResourceThreads,
|
private_resource_threads: ResourceThreads,
|
||||||
|
|
||||||
/// A channel for the constellation to send messages to the image
|
|
||||||
/// cache thread.
|
|
||||||
image_cache_thread: ImageCacheThread,
|
|
||||||
|
|
||||||
/// A channel for the constellation to send messages to the font
|
/// A channel for the constellation to send messages to the font
|
||||||
/// cache thread.
|
/// cache thread.
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
|
@ -302,9 +297,6 @@ pub struct InitialConstellationState {
|
||||||
/// A channel to the bluetooth thread.
|
/// A channel to the bluetooth thread.
|
||||||
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||||
|
|
||||||
/// A channel to the image cache thread.
|
|
||||||
pub image_cache_thread: ImageCacheThread,
|
|
||||||
|
|
||||||
/// A channel to the font cache thread.
|
/// A channel to the font cache thread.
|
||||||
pub font_cache_thread: FontCacheThread,
|
pub font_cache_thread: FontCacheThread,
|
||||||
|
|
||||||
|
@ -518,7 +510,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
||||||
bluetooth_thread: state.bluetooth_thread,
|
bluetooth_thread: state.bluetooth_thread,
|
||||||
public_resource_threads: state.public_resource_threads,
|
public_resource_threads: state.public_resource_threads,
|
||||||
private_resource_threads: state.private_resource_threads,
|
private_resource_threads: state.private_resource_threads,
|
||||||
image_cache_thread: state.image_cache_thread,
|
|
||||||
font_cache_thread: state.font_cache_thread,
|
font_cache_thread: state.font_cache_thread,
|
||||||
swmanager_chan: None,
|
swmanager_chan: None,
|
||||||
swmanager_receiver: swmanager_receiver,
|
swmanager_receiver: swmanager_receiver,
|
||||||
|
@ -657,7 +648,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
||||||
devtools_chan: self.devtools_chan.clone(),
|
devtools_chan: self.devtools_chan.clone(),
|
||||||
bluetooth_thread: self.bluetooth_thread.clone(),
|
bluetooth_thread: self.bluetooth_thread.clone(),
|
||||||
swmanager_thread: self.swmanager_sender.clone(),
|
swmanager_thread: self.swmanager_sender.clone(),
|
||||||
image_cache_thread: self.image_cache_thread.clone(),
|
|
||||||
font_cache_thread: self.font_cache_thread.clone(),
|
font_cache_thread: self.font_cache_thread.clone(),
|
||||||
resource_threads: resource_threads,
|
resource_threads: resource_threads,
|
||||||
time_profiler_chan: self.time_profiler_chan.clone(),
|
time_profiler_chan: self.time_profiler_chan.clone(),
|
||||||
|
@ -1210,9 +1200,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
||||||
let (core_sender, core_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
let (core_sender, core_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||||
let (storage_sender, storage_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
let (storage_sender, storage_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||||
|
|
||||||
debug!("Exiting image cache.");
|
|
||||||
self.image_cache_thread.exit();
|
|
||||||
|
|
||||||
debug!("Exiting core resource threads.");
|
debug!("Exiting core resource threads.");
|
||||||
if let Err(e) = self.public_resource_threads.send(net_traits::CoreResourceMsg::Exit(core_sender)) {
|
if let Err(e) = self.public_resource_threads.send(net_traits::CoreResourceMsg::Exit(core_sender)) {
|
||||||
warn!("Exit resource thread failed ({})", e);
|
warn!("Exit resource thread failed ({})", e);
|
||||||
|
|
|
@ -17,7 +17,7 @@ use ipc_channel::router::ROUTER;
|
||||||
use layout_traits::LayoutThreadFactory;
|
use layout_traits::LayoutThreadFactory;
|
||||||
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespaceId};
|
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespaceId};
|
||||||
use net_traits::{IpcSend, ResourceThreads};
|
use net_traits::{IpcSend, ResourceThreads};
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
use net_traits::image_cache::ImageCache;
|
||||||
use profile_traits::mem as profile_mem;
|
use profile_traits::mem as profile_mem;
|
||||||
use profile_traits::time;
|
use profile_traits::time;
|
||||||
use script_traits::{ConstellationControlMsg, DevicePixel, DiscardBrowsingContext};
|
use script_traits::{ConstellationControlMsg, DevicePixel, DiscardBrowsingContext};
|
||||||
|
@ -133,9 +133,6 @@ pub struct InitialPipelineState {
|
||||||
/// A channel to the service worker manager thread
|
/// A channel to the service worker manager thread
|
||||||
pub swmanager_thread: IpcSender<SWManagerMsg>,
|
pub swmanager_thread: IpcSender<SWManagerMsg>,
|
||||||
|
|
||||||
/// A channel to the image cache thread.
|
|
||||||
pub image_cache_thread: ImageCacheThread,
|
|
||||||
|
|
||||||
/// A channel to the font cache thread.
|
/// A channel to the font cache thread.
|
||||||
pub font_cache_thread: FontCacheThread,
|
pub font_cache_thread: FontCacheThread,
|
||||||
|
|
||||||
|
@ -250,7 +247,6 @@ impl Pipeline {
|
||||||
devtools_chan: script_to_devtools_chan,
|
devtools_chan: script_to_devtools_chan,
|
||||||
bluetooth_thread: state.bluetooth_thread,
|
bluetooth_thread: state.bluetooth_thread,
|
||||||
swmanager_thread: state.swmanager_thread,
|
swmanager_thread: state.swmanager_thread,
|
||||||
image_cache_thread: state.image_cache_thread,
|
|
||||||
font_cache_thread: state.font_cache_thread,
|
font_cache_thread: state.font_cache_thread,
|
||||||
resource_threads: state.resource_threads,
|
resource_threads: state.resource_threads,
|
||||||
time_profiler_chan: state.time_profiler_chan,
|
time_profiler_chan: state.time_profiler_chan,
|
||||||
|
@ -451,7 +447,6 @@ pub struct UnprivilegedPipelineContent {
|
||||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
bluetooth_thread: IpcSender<BluetoothRequest>,
|
bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||||
swmanager_thread: IpcSender<SWManagerMsg>,
|
swmanager_thread: IpcSender<SWManagerMsg>,
|
||||||
image_cache_thread: ImageCacheThread,
|
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
resource_threads: ResourceThreads,
|
resource_threads: ResourceThreads,
|
||||||
time_profiler_chan: time::ProfilerChan,
|
time_profiler_chan: time::ProfilerChan,
|
||||||
|
@ -477,6 +472,7 @@ impl UnprivilegedPipelineContent {
|
||||||
where LTF: LayoutThreadFactory<Message=Message>,
|
where LTF: LayoutThreadFactory<Message=Message>,
|
||||||
STF: ScriptThreadFactory<Message=Message>
|
STF: ScriptThreadFactory<Message=Message>
|
||||||
{
|
{
|
||||||
|
let image_cache = ImageCache::new(self.webrender_api_sender.create_api());
|
||||||
let layout_pair = STF::create(InitialScriptState {
|
let layout_pair = STF::create(InitialScriptState {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
frame_id: self.frame_id,
|
frame_id: self.frame_id,
|
||||||
|
@ -489,7 +485,7 @@ impl UnprivilegedPipelineContent {
|
||||||
scheduler_chan: self.scheduler_chan,
|
scheduler_chan: self.scheduler_chan,
|
||||||
bluetooth_thread: self.bluetooth_thread,
|
bluetooth_thread: self.bluetooth_thread,
|
||||||
resource_threads: self.resource_threads,
|
resource_threads: self.resource_threads,
|
||||||
image_cache_thread: self.image_cache_thread.clone(),
|
image_cache: image_cache.clone(),
|
||||||
time_profiler_chan: self.time_profiler_chan.clone(),
|
time_profiler_chan: self.time_profiler_chan.clone(),
|
||||||
mem_profiler_chan: self.mem_profiler_chan.clone(),
|
mem_profiler_chan: self.mem_profiler_chan.clone(),
|
||||||
devtools_chan: self.devtools_chan,
|
devtools_chan: self.devtools_chan,
|
||||||
|
@ -507,7 +503,7 @@ impl UnprivilegedPipelineContent {
|
||||||
self.pipeline_port,
|
self.pipeline_port,
|
||||||
self.layout_to_constellation_chan,
|
self.layout_to_constellation_chan,
|
||||||
self.script_chan,
|
self.script_chan,
|
||||||
self.image_cache_thread,
|
image_cache.clone(),
|
||||||
self.font_cache_thread,
|
self.font_cache_thread,
|
||||||
self.time_profiler_chan,
|
self.time_profiler_chan,
|
||||||
self.mem_profiler_chan,
|
self.mem_profiler_chan,
|
||||||
|
|
|
@ -9,8 +9,8 @@ use gfx::display_list::{WebRenderImageInfo, OpaqueNode};
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
use net_traits::image_cache_thread::{ImageCacheThread, ImageState, CanRequestImages};
|
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageState};
|
||||||
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
|
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
|
||||||
use opaque_node::OpaqueNodeMethods;
|
use opaque_node::OpaqueNodeMethods;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use script_layout_interface::{PendingImage, PendingImageState};
|
use script_layout_interface::{PendingImage, PendingImageState};
|
||||||
|
@ -79,8 +79,8 @@ pub struct LayoutContext<'a> {
|
||||||
/// Bits shared by the layout and style system.
|
/// Bits shared by the layout and style system.
|
||||||
pub style_context: SharedStyleContext<'a>,
|
pub style_context: SharedStyleContext<'a>,
|
||||||
|
|
||||||
/// The shared image cache thread.
|
/// Reference to the script thread image cache.
|
||||||
pub image_cache_thread: Mutex<ImageCacheThread>,
|
pub image_cache: Arc<ImageCache>,
|
||||||
|
|
||||||
/// Interface to the font cache thread.
|
/// Interface to the font cache thread.
|
||||||
pub font_cache_thread: Mutex<FontCacheThread>,
|
pub font_cache_thread: Mutex<FontCacheThread>,
|
||||||
|
@ -126,8 +126,7 @@ impl<'a> LayoutContext<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// See if the image is already available
|
// See if the image is already available
|
||||||
let result = self.image_cache_thread.lock().unwrap()
|
let result = self.image_cache.find_image_or_metadata(url.clone(),
|
||||||
.find_image_or_metadata(url.clone(),
|
|
||||||
use_placeholder,
|
use_placeholder,
|
||||||
can_request);
|
can_request);
|
||||||
match result {
|
match result {
|
||||||
|
|
|
@ -36,7 +36,7 @@ use list_item::ListItemFlow;
|
||||||
use model::{self, MaybeAuto};
|
use model::{self, MaybeAuto};
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image::base::PixelFormat;
|
use net_traits::image::base::PixelFormat;
|
||||||
use net_traits::image_cache_thread::UsePlaceholder;
|
use net_traits::image_cache::UsePlaceholder;
|
||||||
use range::Range;
|
use range::Range;
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
|
|
@ -27,7 +27,7 @@ use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeC
|
||||||
use model::{style_length, ToGfxMatrix};
|
use model::{style_length, ToGfxMatrix};
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image::base::{Image, ImageMetadata};
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
|
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
|
||||||
use range::*;
|
use range::*;
|
||||||
use script_layout_interface::HTMLCanvasData;
|
use script_layout_interface::HTMLCanvasData;
|
||||||
use script_layout_interface::SVGSVGData;
|
use script_layout_interface::SVGSVGData;
|
||||||
|
|
|
@ -76,8 +76,7 @@ use layout::wrapper::LayoutNodeLayoutData;
|
||||||
use layout::wrapper::drop_style_and_layout_data;
|
use layout::wrapper::drop_style_and_layout_data;
|
||||||
use layout_traits::LayoutThreadFactory;
|
use layout_traits::LayoutThreadFactory;
|
||||||
use msg::constellation_msg::{FrameId, PipelineId};
|
use msg::constellation_msg::{FrameId, PipelineId};
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
||||||
use net_traits::image_cache_thread::UsePlaceholder;
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
|
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
|
||||||
use profile_traits::time::{self, TimerMetadata, profile};
|
use profile_traits::time::{self, TimerMetadata, profile};
|
||||||
|
@ -157,8 +156,8 @@ pub struct LayoutThread {
|
||||||
/// The channel on which messages can be sent to the memory profiler.
|
/// The channel on which messages can be sent to the memory profiler.
|
||||||
mem_profiler_chan: mem::ProfilerChan,
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
|
|
||||||
/// The channel on which messages can be sent to the image cache.
|
/// Reference to the script thread image cache.
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
|
|
||||||
/// Public interface to the font cache thread.
|
/// Public interface to the font cache thread.
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
|
@ -245,7 +244,7 @@ impl LayoutThreadFactory for LayoutThread {
|
||||||
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||||
constellation_chan: IpcSender<ConstellationMsg>,
|
constellation_chan: IpcSender<ConstellationMsg>,
|
||||||
script_chan: IpcSender<ConstellationControlMsg>,
|
script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
time_profiler_chan: time::ProfilerChan,
|
time_profiler_chan: time::ProfilerChan,
|
||||||
mem_profiler_chan: mem::ProfilerChan,
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
|
@ -268,7 +267,7 @@ impl LayoutThreadFactory for LayoutThread {
|
||||||
pipeline_port,
|
pipeline_port,
|
||||||
constellation_chan,
|
constellation_chan,
|
||||||
script_chan,
|
script_chan,
|
||||||
image_cache_thread,
|
image_cache.clone(),
|
||||||
font_cache_thread,
|
font_cache_thread,
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
mem_profiler_chan.clone(),
|
mem_profiler_chan.clone(),
|
||||||
|
@ -382,7 +381,7 @@ impl LayoutThread {
|
||||||
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||||
constellation_chan: IpcSender<ConstellationMsg>,
|
constellation_chan: IpcSender<ConstellationMsg>,
|
||||||
script_chan: IpcSender<ConstellationControlMsg>,
|
script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
time_profiler_chan: time::ProfilerChan,
|
time_profiler_chan: time::ProfilerChan,
|
||||||
mem_profiler_chan: mem::ProfilerChan,
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
|
@ -432,7 +431,7 @@ impl LayoutThread {
|
||||||
constellation_chan: constellation_chan.clone(),
|
constellation_chan: constellation_chan.clone(),
|
||||||
time_profiler_chan: time_profiler_chan,
|
time_profiler_chan: time_profiler_chan,
|
||||||
mem_profiler_chan: mem_profiler_chan,
|
mem_profiler_chan: mem_profiler_chan,
|
||||||
image_cache_thread: image_cache_thread,
|
image_cache: image_cache.clone(),
|
||||||
font_cache_thread: font_cache_thread,
|
font_cache_thread: font_cache_thread,
|
||||||
first_reflow: true,
|
first_reflow: true,
|
||||||
font_cache_receiver: font_cache_receiver,
|
font_cache_receiver: font_cache_receiver,
|
||||||
|
@ -522,7 +521,7 @@ impl LayoutThread {
|
||||||
quirks_mode: self.quirks_mode.unwrap(),
|
quirks_mode: self.quirks_mode.unwrap(),
|
||||||
animation_only_restyle: false,
|
animation_only_restyle: false,
|
||||||
},
|
},
|
||||||
image_cache_thread: Mutex::new(self.image_cache_thread.clone()),
|
image_cache: self.image_cache.clone(),
|
||||||
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
||||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||||
pending_images: if request_images { Some(Mutex::new(vec![])) } else { None },
|
pending_images: if request_images { Some(Mutex::new(vec![])) } else { None },
|
||||||
|
@ -693,7 +692,7 @@ impl LayoutThread {
|
||||||
info.pipeline_port,
|
info.pipeline_port,
|
||||||
info.constellation_chan,
|
info.constellation_chan,
|
||||||
info.script_chan.clone(),
|
info.script_chan.clone(),
|
||||||
self.image_cache_thread.clone(),
|
info.image_cache.clone(),
|
||||||
self.font_cache_thread.clone(),
|
self.font_cache_thread.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
self.mem_profiler_chan.clone(),
|
self.mem_profiler_chan.clone(),
|
||||||
|
|
|
@ -21,11 +21,12 @@ extern crate webrender_traits;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use msg::constellation_msg::{FrameId, PipelineId};
|
use msg::constellation_msg::{FrameId, PipelineId};
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
use net_traits::image_cache::ImageCache;
|
||||||
use profile_traits::{mem, time};
|
use profile_traits::{mem, time};
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
|
||||||
use script_traits::LayoutMsg as ConstellationMsg;
|
use script_traits::LayoutMsg as ConstellationMsg;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
|
|
||||||
// A static method creating a layout thread
|
// A static method creating a layout thread
|
||||||
|
@ -40,7 +41,7 @@ pub trait LayoutThreadFactory {
|
||||||
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||||
constellation_chan: IpcSender<ConstellationMsg>,
|
constellation_chan: IpcSender<ConstellationMsg>,
|
||||||
script_chan: IpcSender<ConstellationControlMsg>,
|
script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
time_profiler_chan: time::ProfilerChan,
|
time_profiler_chan: time::ProfilerChan,
|
||||||
mem_profiler_chan: mem::ProfilerChan,
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![feature(mpsc_select)]
|
|
||||||
#![feature(step_by)]
|
|
||||||
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate brotli;
|
extern crate brotli;
|
||||||
|
@ -14,7 +12,6 @@ extern crate devtools_traits;
|
||||||
extern crate flate2;
|
extern crate flate2;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate hyper_serde;
|
extern crate hyper_serde;
|
||||||
extern crate immeta;
|
|
||||||
extern crate ipc_channel;
|
extern crate ipc_channel;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
#[macro_use] #[no_link] extern crate matches;
|
#[macro_use] #[no_link] extern crate matches;
|
||||||
|
@ -32,14 +29,12 @@ extern crate serde_derive;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate servo_config;
|
extern crate servo_config;
|
||||||
extern crate servo_url;
|
extern crate servo_url;
|
||||||
extern crate threadpool;
|
|
||||||
extern crate time;
|
extern crate time;
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||||
extern crate tinyfiledialogs;
|
extern crate tinyfiledialogs;
|
||||||
extern crate unicase;
|
extern crate unicase;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
extern crate webrender_traits;
|
|
||||||
extern crate websocket;
|
extern crate websocket;
|
||||||
|
|
||||||
mod blob_loader;
|
mod blob_loader;
|
||||||
|
@ -51,7 +46,6 @@ mod data_loader;
|
||||||
pub mod filemanager_thread;
|
pub mod filemanager_thread;
|
||||||
pub mod hsts;
|
pub mod hsts;
|
||||||
mod http_loader;
|
mod http_loader;
|
||||||
pub mod image_cache_thread;
|
|
||||||
pub mod mime_classifier;
|
pub mod mime_classifier;
|
||||||
pub mod resource_thread;
|
pub mod resource_thread;
|
||||||
mod storage_thread;
|
mod storage_thread;
|
||||||
|
|
|
@ -16,6 +16,7 @@ heapsize_derive = "0.1"
|
||||||
hyper = "0.9.9"
|
hyper = "0.9.9"
|
||||||
hyper_serde = "0.5"
|
hyper_serde = "0.5"
|
||||||
image = "0.12"
|
image = "0.12"
|
||||||
|
immeta = "0.3.1"
|
||||||
ipc-channel = "0.7"
|
ipc-channel = "0.7"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
log = "0.3.5"
|
log = "0.3.5"
|
||||||
|
|
|
@ -2,35 +2,81 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use FetchResponseMsg;
|
||||||
|
use NetworkError;
|
||||||
|
use image::base::{Image, ImageMetadata, PixelFormat, load_from_memory};
|
||||||
use immeta::load_from_buf;
|
use immeta::load_from_buf;
|
||||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
use ipc_channel::ipc::IpcSender;
|
||||||
use ipc_channel::router::ROUTER;
|
|
||||||
use net_traits::{NetworkError, FetchResponseMsg};
|
|
||||||
use net_traits::image::base::{Image, ImageMetadata, PixelFormat, load_from_memory};
|
|
||||||
use net_traits::image_cache_thread::{ImageCacheCommand, ImageCacheThread, ImageState};
|
|
||||||
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, ImageResponse, UsePlaceholder};
|
|
||||||
use net_traits::image_cache_thread::{ImageResponder, PendingImageId, CanRequestImages};
|
|
||||||
use servo_config::resource_files::resources_dir_path;
|
use servo_config::resource_files::resources_dir_path;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::borrow::ToOwned;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
use std::sync::mpsc::channel;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use threadpool::ThreadPool;
|
|
||||||
use webrender_traits;
|
use webrender_traits;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// TODO(gw): Remaining work on image cache:
|
/// TODO(gw): Remaining work on image cache:
|
||||||
/// * Make use of the prefetch support in various parts of the code.
|
/// * Make use of the prefetch support in various parts of the code.
|
||||||
/// * Profile time in GetImageIfAvailable - might be worth caching these results per paint / layout thread.
|
/// * Profile time in GetImageIfAvailable - might be worth caching these
|
||||||
|
/// results per paint / layout thread.
|
||||||
///
|
///
|
||||||
/// MAYBE(Yoric):
|
/// MAYBE(Yoric):
|
||||||
/// * For faster lookups, it might be useful to store the LoadKey in the DOM once we have performed a first load.
|
/// * For faster lookups, it might be useful to store the LoadKey in the
|
||||||
|
/// DOM once we have performed a first load.
|
||||||
|
|
||||||
|
|
||||||
|
// ======================================================================
|
||||||
|
// Helper functions.
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
fn convert_format(format: PixelFormat) -> webrender_traits::ImageFormat {
|
||||||
|
match format {
|
||||||
|
PixelFormat::K8 | PixelFormat::KA8 => {
|
||||||
|
panic!("Not support by webrender yet");
|
||||||
|
}
|
||||||
|
PixelFormat::RGB8 => webrender_traits::ImageFormat::RGB8,
|
||||||
|
PixelFormat::RGBA8 => webrender_traits::ImageFormat::RGBA8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
|
||||||
|
let image = load_from_memory(bytes);
|
||||||
|
DecoderMsg {
|
||||||
|
key: key,
|
||||||
|
image: image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi) -> io::Result<Arc<Image>> {
|
||||||
|
let mut placeholder_path = try!(resources_dir_path());
|
||||||
|
placeholder_path.push("rippy.png");
|
||||||
|
let mut file = try!(File::open(&placeholder_path));
|
||||||
|
let mut image_data = vec![];
|
||||||
|
try!(file.read_to_end(&mut image_data));
|
||||||
|
let mut image = load_from_memory(&image_data).unwrap();
|
||||||
|
let format = convert_format(image.format);
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
bytes.extend_from_slice(&*image.bytes);
|
||||||
|
let descriptor = webrender_traits::ImageDescriptor {
|
||||||
|
width: image.width,
|
||||||
|
height: image.height,
|
||||||
|
stride: None,
|
||||||
|
format: format,
|
||||||
|
offset: 0,
|
||||||
|
is_opaque: is_image_opaque(format, &bytes),
|
||||||
|
};
|
||||||
|
let data = webrender_traits::ImageData::new(bytes);
|
||||||
|
let image_key = webrender_api.generate_image_key();
|
||||||
|
webrender_api.add_image(image_key, descriptor, data, None);
|
||||||
|
image.id = Some(image_key);
|
||||||
|
Ok(Arc::new(image))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(gw): This is a port of the old is_image_opaque code from WR.
|
// TODO(gw): This is a port of the old is_image_opaque code from WR.
|
||||||
// Consider using SIMD to speed this up if it shows in profiles.
|
// Consider using SIMD to speed this up if it shows in profiles.
|
||||||
|
@ -52,80 +98,24 @@ fn is_image_opaque(format: webrender_traits::ImageFormat, bytes: &[u8]) -> bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an image that is either being loaded
|
fn premultiply(data: &mut [u8]) {
|
||||||
/// by the resource thread, or decoded by a worker thread.
|
let length = data.len();
|
||||||
struct PendingLoad {
|
|
||||||
// The bytes loaded so far. Reset to an empty vector once loading
|
|
||||||
// is complete and the buffer has been transmitted to the decoder.
|
|
||||||
bytes: ImageBytes,
|
|
||||||
|
|
||||||
// Image metadata, if available.
|
for i in (0..length).step_by(4) {
|
||||||
metadata: Option<ImageMetadata>,
|
let b = data[i + 0] as u32;
|
||||||
|
let g = data[i + 1] as u32;
|
||||||
|
let r = data[i + 2] as u32;
|
||||||
|
let a = data[i + 3] as u32;
|
||||||
|
|
||||||
// Once loading is complete, the result of the operation.
|
data[i + 0] = (b * a / 255) as u8;
|
||||||
result: Option<Result<(), NetworkError>>,
|
data[i + 1] = (g * a / 255) as u8;
|
||||||
listeners: Vec<ImageResponder>,
|
data[i + 2] = (r * a / 255) as u8;
|
||||||
|
|
||||||
// The url being loaded. Do not forget that this may be several Mb
|
|
||||||
// if we are loading a data: url.
|
|
||||||
url: ServoUrl,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ImageBytes {
|
|
||||||
InProgress(Vec<u8>),
|
|
||||||
Complete(Arc<Vec<u8>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageBytes {
|
|
||||||
fn extend_from_slice(&mut self, data: &[u8]) {
|
|
||||||
match *self {
|
|
||||||
ImageBytes::InProgress(ref mut bytes) => bytes.extend_from_slice(data),
|
|
||||||
ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_complete(&mut self) -> Arc<Vec<u8>> {
|
// ======================================================================
|
||||||
let bytes = {
|
// Aux structs and enums.
|
||||||
let own_bytes = match *self {
|
// ======================================================================
|
||||||
ImageBytes::InProgress(ref mut bytes) => bytes,
|
|
||||||
ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
|
|
||||||
};
|
|
||||||
mem::replace(own_bytes, vec![])
|
|
||||||
};
|
|
||||||
let bytes = Arc::new(bytes);
|
|
||||||
*self = ImageBytes::Complete(bytes.clone());
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_slice(&self) -> &[u8] {
|
|
||||||
match *self {
|
|
||||||
ImageBytes::InProgress(ref bytes) => &bytes,
|
|
||||||
ImageBytes::Complete(ref bytes) => &*bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum LoadResult {
|
|
||||||
Loaded(Image),
|
|
||||||
PlaceholderLoaded(Arc<Image>),
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PendingLoad {
|
|
||||||
fn new(url: ServoUrl) -> PendingLoad {
|
|
||||||
PendingLoad {
|
|
||||||
bytes: ImageBytes::InProgress(vec!()),
|
|
||||||
metadata: None,
|
|
||||||
result: None,
|
|
||||||
listeners: vec!(),
|
|
||||||
url: url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_listener(&mut self, listener: ImageResponder) {
|
|
||||||
self.listeners.push(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Represents all the currently pending loads/decodings. For
|
// Represents all the currently pending loads/decodings. For
|
||||||
// performance reasons, loads are indexed by a dedicated load key.
|
// performance reasons, loads are indexed by a dedicated load key.
|
||||||
|
@ -142,14 +132,6 @@ struct AllPendingLoads {
|
||||||
keygen: LoadKeyGenerator,
|
keygen: LoadKeyGenerator,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of accessing a cache.
|
|
||||||
enum CacheResult<'a> {
|
|
||||||
/// The value was in the cache.
|
|
||||||
Hit(LoadKey, &'a mut PendingLoad),
|
|
||||||
/// The value was not in the cache and needed to be regenerated.
|
|
||||||
Miss(Option<(LoadKey, &'a mut PendingLoad)>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AllPendingLoads {
|
impl AllPendingLoads {
|
||||||
fn new() -> AllPendingLoads {
|
fn new() -> AllPendingLoads {
|
||||||
AllPendingLoads {
|
AllPendingLoads {
|
||||||
|
@ -159,12 +141,6 @@ impl AllPendingLoads {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `true` if there is no currently pending load, `false` otherwise.
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
assert!(self.loads.is_empty() == self.url_to_load_key.is_empty());
|
|
||||||
self.loads.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a PendingLoad from its LoadKey.
|
// get a PendingLoad from its LoadKey.
|
||||||
fn get_by_key_mut(&mut self, key: &LoadKey) -> Option<&mut PendingLoad> {
|
fn get_by_key_mut(&mut self, key: &LoadKey) -> Option<&mut PendingLoad> {
|
||||||
self.loads.get_mut(key)
|
self.loads.get_mut(key)
|
||||||
|
@ -206,6 +182,23 @@ impl AllPendingLoads {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Result of accessing a cache.
|
||||||
|
enum CacheResult<'a> {
|
||||||
|
/// The value was in the cache.
|
||||||
|
Hit(LoadKey, &'a mut PendingLoad),
|
||||||
|
/// The value was not in the cache and needed to be regenerated.
|
||||||
|
Miss(Option<(LoadKey, &'a mut PendingLoad)>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a consumer is in a position to request images or not. This can occur
|
||||||
|
/// when animations are being processed by the layout thread while the script
|
||||||
|
/// thread is executing in parallel.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub enum CanRequestImages {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents an image that has completed loading.
|
/// Represents an image that has completed loading.
|
||||||
/// Images that fail to load (due to network or decode
|
/// Images that fail to load (due to network or decode
|
||||||
/// failure) are still stored here, so that they aren't
|
/// failure) are still stored here, so that they aren't
|
||||||
|
@ -224,6 +217,104 @@ impl CompletedLoad {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Message that the decoder worker threads send to the image cache.
|
||||||
|
struct DecoderMsg {
|
||||||
|
key: LoadKey,
|
||||||
|
image: Option<Image>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ImageBytes {
|
||||||
|
InProgress(Vec<u8>),
|
||||||
|
Complete(Arc<Vec<u8>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageBytes {
|
||||||
|
fn extend_from_slice(&mut self, data: &[u8]) {
|
||||||
|
match *self {
|
||||||
|
ImageBytes::InProgress(ref mut bytes) => bytes.extend_from_slice(data),
|
||||||
|
ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_complete(&mut self) -> Arc<Vec<u8>> {
|
||||||
|
let bytes = {
|
||||||
|
let own_bytes = match *self {
|
||||||
|
ImageBytes::InProgress(ref mut bytes) => bytes,
|
||||||
|
ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
|
||||||
|
};
|
||||||
|
mem::replace(own_bytes, vec![])
|
||||||
|
};
|
||||||
|
let bytes = Arc::new(bytes);
|
||||||
|
*self = ImageBytes::Complete(bytes.clone());
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_slice(&self) -> &[u8] {
|
||||||
|
match *self {
|
||||||
|
ImageBytes::InProgress(ref bytes) => &bytes,
|
||||||
|
ImageBytes::Complete(ref bytes) => &*bytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicating either entire image or just metadata availability
|
||||||
|
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
|
||||||
|
pub enum ImageOrMetadataAvailable {
|
||||||
|
ImageAvailable(Arc<Image>),
|
||||||
|
MetadataAvailable(ImageMetadata),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is optionally passed to the image cache when requesting
|
||||||
|
/// and image, and returned to the specified event loop when the
|
||||||
|
/// image load completes. It is typically used to trigger a reflow
|
||||||
|
/// and/or repaint.
|
||||||
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ImageResponder {
|
||||||
|
id: PendingImageId,
|
||||||
|
sender: IpcSender<PendingImageResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageResponder {
|
||||||
|
pub fn new(sender: IpcSender<PendingImageResponse>, id: PendingImageId) -> ImageResponder {
|
||||||
|
ImageResponder {
|
||||||
|
sender: sender,
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn respond(&self, response: ImageResponse) {
|
||||||
|
debug!("Notifying listener");
|
||||||
|
// This send can fail if thread waiting for this notification has panicked.
|
||||||
|
// That's not a case that's worth warning about.
|
||||||
|
// TODO(#15501): are there cases in which we should perform cleanup?
|
||||||
|
let _ = self.sender.send(PendingImageResponse {
|
||||||
|
response: response,
|
||||||
|
id: self.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The returned image.
|
||||||
|
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
|
||||||
|
pub enum ImageResponse {
|
||||||
|
/// The requested image was loaded.
|
||||||
|
Loaded(Arc<Image>),
|
||||||
|
/// The request image metadata was loaded.
|
||||||
|
MetadataLoaded(ImageMetadata),
|
||||||
|
/// The requested image failed to load, so a placeholder was loaded instead.
|
||||||
|
PlaceholderLoaded(Arc<Image>),
|
||||||
|
/// Neither the requested image nor the placeholder could be loaded.
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The current state of an image in the cache.
|
||||||
|
#[derive(PartialEq, Copy, Clone, Deserialize, Serialize)]
|
||||||
|
pub enum ImageState {
|
||||||
|
Pending(PendingImageId),
|
||||||
|
LoadError,
|
||||||
|
NotRequested(PendingImageId),
|
||||||
|
}
|
||||||
|
|
||||||
// A key used to communicate during loading.
|
// A key used to communicate during loading.
|
||||||
type LoadKey = PendingImageId;
|
type LoadKey = PendingImageId;
|
||||||
|
|
||||||
|
@ -243,253 +334,86 @@ impl LoadKeyGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResourceLoadInfo {
|
enum LoadResult {
|
||||||
action: FetchResponseMsg,
|
Loaded(Image),
|
||||||
key: LoadKey,
|
PlaceholderLoaded(Arc<Image>),
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of the image cache
|
/// The unique id for an image that has previously been requested.
|
||||||
struct ImageCache {
|
#[derive(Copy, Clone, PartialEq, Eq, Deserialize, Serialize, HeapSizeOf, Hash, Debug)]
|
||||||
decoder_sender: Sender<DecoderMsg>,
|
pub struct PendingImageId(pub u64);
|
||||||
|
|
||||||
// Worker threads for decoding images.
|
#[derive(Deserialize, Serialize)]
|
||||||
thread_pool: ThreadPool,
|
pub struct PendingImageResponse {
|
||||||
|
pub response: ImageResponse,
|
||||||
|
pub id: PendingImageId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an image that is either being loaded
|
||||||
|
/// by the resource thread, or decoded by a worker thread.
|
||||||
|
struct PendingLoad {
|
||||||
|
// The bytes loaded so far. Reset to an empty vector once loading
|
||||||
|
// is complete and the buffer has been transmitted to the decoder.
|
||||||
|
bytes: ImageBytes,
|
||||||
|
|
||||||
|
// Image metadata, if available.
|
||||||
|
metadata: Option<ImageMetadata>,
|
||||||
|
|
||||||
|
// Once loading is complete, the result of the operation.
|
||||||
|
result: Option<Result<(), NetworkError>>,
|
||||||
|
listeners: Vec<ImageResponder>,
|
||||||
|
|
||||||
|
// The url being loaded. Do not forget that this may be several Mb
|
||||||
|
// if we are loading a data: url.
|
||||||
|
url: ServoUrl,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingLoad {
|
||||||
|
fn new(url: ServoUrl) -> PendingLoad {
|
||||||
|
PendingLoad {
|
||||||
|
bytes: ImageBytes::InProgress(vec!()),
|
||||||
|
metadata: None,
|
||||||
|
result: None,
|
||||||
|
listeners: vec!(),
|
||||||
|
url: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_listener(&mut self, listener: ImageResponder) {
|
||||||
|
self.listeners.push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Hash, Eq, Deserialize, Serialize)]
|
||||||
|
pub enum UsePlaceholder {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================================================================
|
||||||
|
// ImageCache implementation.
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
pub struct ImageCache {
|
||||||
// Images that are loading over network, or decoding.
|
// Images that are loading over network, or decoding.
|
||||||
pending_loads: AllPendingLoads,
|
pending_loads: Mutex<AllPendingLoads>,
|
||||||
|
|
||||||
// Images that have finished loading (successful or not)
|
// Images that have finished loading (successful or not)
|
||||||
completed_loads: HashMap<ServoUrl, CompletedLoad>,
|
completed_loads: Mutex<HashMap<ServoUrl, CompletedLoad>>,
|
||||||
|
|
||||||
// The placeholder image used when an image fails to load
|
// The placeholder image used when an image fails to load
|
||||||
placeholder_image: Option<Arc<Image>>,
|
placeholder_image: Option<Arc<Image>>,
|
||||||
|
|
||||||
// Webrender API instance.
|
// Webrender API instance.
|
||||||
webrender_api: webrender_traits::RenderApi,
|
webrender_api: Mutex<webrender_traits::RenderApi>,
|
||||||
}
|
|
||||||
|
|
||||||
/// Message that the decoder worker threads send to main image cache thread.
|
|
||||||
struct DecoderMsg {
|
|
||||||
key: LoadKey,
|
|
||||||
image: Option<Image>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Receivers {
|
|
||||||
cmd_receiver: Receiver<ImageCacheCommand>,
|
|
||||||
decoder_receiver: Receiver<DecoderMsg>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Receivers {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
fn recv(&self) -> SelectResult {
|
|
||||||
let cmd_receiver = &self.cmd_receiver;
|
|
||||||
let decoder_receiver = &self.decoder_receiver;
|
|
||||||
select! {
|
|
||||||
msg = cmd_receiver.recv() => {
|
|
||||||
SelectResult::Command(msg.unwrap())
|
|
||||||
},
|
|
||||||
msg = decoder_receiver.recv() => {
|
|
||||||
SelectResult::Decoder(msg.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The types of messages that the main image cache thread receives.
|
|
||||||
enum SelectResult {
|
|
||||||
Command(ImageCacheCommand),
|
|
||||||
Decoder(DecoderMsg),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_format(format: PixelFormat) -> webrender_traits::ImageFormat {
|
|
||||||
match format {
|
|
||||||
PixelFormat::K8 | PixelFormat::KA8 => {
|
|
||||||
panic!("Not support by webrender yet");
|
|
||||||
}
|
|
||||||
PixelFormat::RGB8 => webrender_traits::ImageFormat::RGB8,
|
|
||||||
PixelFormat::RGBA8 => webrender_traits::ImageFormat::RGBA8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi) -> io::Result<Arc<Image>> {
|
|
||||||
let mut placeholder_path = try!(resources_dir_path());
|
|
||||||
placeholder_path.push("rippy.png");
|
|
||||||
let mut file = try!(File::open(&placeholder_path));
|
|
||||||
let mut image_data = vec![];
|
|
||||||
try!(file.read_to_end(&mut image_data));
|
|
||||||
let mut image = load_from_memory(&image_data).unwrap();
|
|
||||||
let format = convert_format(image.format);
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
bytes.extend_from_slice(&*image.bytes);
|
|
||||||
let descriptor = webrender_traits::ImageDescriptor {
|
|
||||||
width: image.width,
|
|
||||||
height: image.height,
|
|
||||||
stride: None,
|
|
||||||
format: format,
|
|
||||||
offset: 0,
|
|
||||||
is_opaque: is_image_opaque(format, &bytes),
|
|
||||||
};
|
|
||||||
let data = webrender_traits::ImageData::new(bytes);
|
|
||||||
let image_key = webrender_api.generate_image_key();
|
|
||||||
webrender_api.add_image(image_key, descriptor, data, None);
|
|
||||||
image.id = Some(image_key);
|
|
||||||
Ok(Arc::new(image))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn premultiply(data: &mut [u8]) {
|
|
||||||
let length = data.len();
|
|
||||||
|
|
||||||
for i in (0..length).step_by(4) {
|
|
||||||
let b = data[i + 0] as u32;
|
|
||||||
let g = data[i + 1] as u32;
|
|
||||||
let r = data[i + 2] as u32;
|
|
||||||
let a = data[i + 3] as u32;
|
|
||||||
|
|
||||||
data[i + 0] = (b * a / 255) as u8;
|
|
||||||
data[i + 1] = (g * a / 255) as u8;
|
|
||||||
data[i + 2] = (r * a / 255) as u8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageCache {
|
impl ImageCache {
|
||||||
fn run(webrender_api: webrender_traits::RenderApi,
|
|
||||||
ipc_command_receiver: IpcReceiver<ImageCacheCommand>) {
|
|
||||||
// Preload the placeholder image, used when images fail to load.
|
|
||||||
let placeholder_image = get_placeholder_image(&webrender_api).ok();
|
|
||||||
|
|
||||||
// Ask the router to proxy messages received over IPC to us.
|
|
||||||
let cmd_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_command_receiver);
|
|
||||||
|
|
||||||
let (decoder_sender, decoder_receiver) = channel();
|
|
||||||
let mut cache = ImageCache {
|
|
||||||
decoder_sender: decoder_sender,
|
|
||||||
thread_pool: ThreadPool::new(4),
|
|
||||||
pending_loads: AllPendingLoads::new(),
|
|
||||||
completed_loads: HashMap::new(),
|
|
||||||
placeholder_image: placeholder_image,
|
|
||||||
webrender_api: webrender_api,
|
|
||||||
};
|
|
||||||
|
|
||||||
let receivers = Receivers {
|
|
||||||
cmd_receiver: cmd_receiver,
|
|
||||||
decoder_receiver: decoder_receiver,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut exit_sender: Option<IpcSender<()>> = None;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match receivers.recv() {
|
|
||||||
SelectResult::Command(cmd) => {
|
|
||||||
exit_sender = cache.handle_cmd(cmd);
|
|
||||||
}
|
|
||||||
SelectResult::Decoder(msg) => {
|
|
||||||
cache.handle_decoder(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can only exit when all pending loads are complete.
|
|
||||||
if let Some(ref exit_sender) = exit_sender {
|
|
||||||
if cache.pending_loads.is_empty() {
|
|
||||||
exit_sender.send(()).unwrap();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle a request from a client
|
|
||||||
fn handle_cmd(&mut self, cmd: ImageCacheCommand) -> Option<IpcSender<()>> {
|
|
||||||
match cmd {
|
|
||||||
ImageCacheCommand::Exit(sender) => {
|
|
||||||
return Some(sender);
|
|
||||||
}
|
|
||||||
ImageCacheCommand::AddListener(id, responder) => {
|
|
||||||
self.add_listener(id, responder);
|
|
||||||
}
|
|
||||||
ImageCacheCommand::GetImageOrMetadataIfAvailable(url,
|
|
||||||
use_placeholder,
|
|
||||||
can_request,
|
|
||||||
consumer) => {
|
|
||||||
let result = self.get_image_or_meta_if_available(url, use_placeholder, can_request);
|
|
||||||
// TODO(#15501): look for opportunities to clean up cache if this send fails.
|
|
||||||
let _ = consumer.send(result);
|
|
||||||
}
|
|
||||||
ImageCacheCommand::StoreDecodeImage(id, data) => {
|
|
||||||
self.handle_progress(ResourceLoadInfo {
|
|
||||||
action: data,
|
|
||||||
key: id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle progress messages from the resource thread
|
|
||||||
fn handle_progress(&mut self, msg: ResourceLoadInfo) {
|
|
||||||
match (msg.action, msg.key) {
|
|
||||||
(FetchResponseMsg::ProcessRequestBody, _) |
|
|
||||||
(FetchResponseMsg::ProcessRequestEOF, _) => return,
|
|
||||||
(FetchResponseMsg::ProcessResponse(_response), _) => {}
|
|
||||||
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
|
|
||||||
debug!("got some data for {:?}", msg.key);
|
|
||||||
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
|
||||||
pending_load.bytes.extend_from_slice(&data);
|
|
||||||
//jmr0 TODO: possibly move to another task?
|
|
||||||
if let None = pending_load.metadata {
|
|
||||||
if let Ok(metadata) = load_from_buf(&pending_load.bytes.as_slice()) {
|
|
||||||
let dimensions = metadata.dimensions();
|
|
||||||
let img_metadata = ImageMetadata { width: dimensions.width,
|
|
||||||
height: dimensions.height };
|
|
||||||
for listener in &pending_load.listeners {
|
|
||||||
listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
|
|
||||||
}
|
|
||||||
pending_load.metadata = Some(img_metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(FetchResponseMsg::ProcessResponseEOF(result), key) => {
|
|
||||||
debug!("received EOF for {:?}", key);
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
|
||||||
pending_load.result = Some(result);
|
|
||||||
let bytes = pending_load.bytes.mark_complete();
|
|
||||||
let sender = self.decoder_sender.clone();
|
|
||||||
debug!("async decoding {} ({:?})", pending_load.url, key);
|
|
||||||
|
|
||||||
self.thread_pool.execute(move || {
|
|
||||||
let msg = decode_bytes_sync(key, &*bytes);
|
|
||||||
sender.send(msg).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
debug!("processing error for {:?}", key);
|
|
||||||
match self.placeholder_image.clone() {
|
|
||||||
Some(placeholder_image) => {
|
|
||||||
self.complete_load(msg.key, LoadResult::PlaceholderLoaded(
|
|
||||||
placeholder_image))
|
|
||||||
}
|
|
||||||
None => self.complete_load(msg.key, LoadResult::None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle a message from one of the decoder worker threads
|
|
||||||
fn handle_decoder(&mut self, msg: DecoderMsg) {
|
|
||||||
let image = match msg.image {
|
|
||||||
None => LoadResult::None,
|
|
||||||
Some(image) => LoadResult::Loaded(image),
|
|
||||||
};
|
|
||||||
self.complete_load(msg.key, image);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change state of a url from pending -> loaded.
|
// Change state of a url from pending -> loaded.
|
||||||
fn complete_load(&mut self, key: LoadKey, mut load_result: LoadResult) {
|
fn complete_load(&self, key: LoadKey, mut load_result: LoadResult) {
|
||||||
let pending_load = match self.pending_loads.remove(&key) {
|
let mut pending_loads = self.pending_loads.lock().unwrap();
|
||||||
|
let pending_load = match pending_loads.remove(&key) {
|
||||||
Some(load) => load,
|
Some(load) => load,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
@ -511,8 +435,9 @@ impl ImageCache {
|
||||||
is_opaque: is_image_opaque(format, &bytes),
|
is_opaque: is_image_opaque(format, &bytes),
|
||||||
};
|
};
|
||||||
let data = webrender_traits::ImageData::new(bytes);
|
let data = webrender_traits::ImageData::new(bytes);
|
||||||
let image_key = self.webrender_api.generate_image_key();
|
let webrender_api = self.webrender_api.lock().unwrap();
|
||||||
self.webrender_api.add_image(image_key, descriptor, data, None);
|
let image_key = webrender_api.generate_image_key();
|
||||||
|
webrender_api.add_image(image_key, descriptor, data, None);
|
||||||
image.id = Some(image_key);
|
image.id = Some(image_key);
|
||||||
}
|
}
|
||||||
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
|
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
|
||||||
|
@ -525,39 +450,20 @@ impl ImageCache {
|
||||||
};
|
};
|
||||||
|
|
||||||
let completed_load = CompletedLoad::new(image_response.clone(), key);
|
let completed_load = CompletedLoad::new(image_response.clone(), key);
|
||||||
self.completed_loads.insert(pending_load.url.into(), completed_load);
|
self.completed_loads.lock().unwrap().insert(pending_load.url.into(), completed_load);
|
||||||
|
|
||||||
for listener in pending_load.listeners {
|
for listener in pending_load.listeners {
|
||||||
listener.respond(image_response.clone());
|
listener.respond(image_response.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a listener for a given image if it is still pending, or notify the
|
|
||||||
/// listener if the image is complete.
|
|
||||||
fn add_listener(&mut self,
|
|
||||||
id: PendingImageId,
|
|
||||||
listener: ImageResponder) {
|
|
||||||
if let Some(load) = self.pending_loads.get_by_key_mut(&id) {
|
|
||||||
if let Some(ref metadata) = load.metadata {
|
|
||||||
listener.respond(ImageResponse::MetadataLoaded(metadata.clone()));
|
|
||||||
}
|
|
||||||
load.add_listener(listener);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Some(load) = self.completed_loads.values().find(|l| l.id == id) {
|
|
||||||
listener.respond(load.image_response.clone());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
warn!("Couldn't find cached entry for listener {:?}", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a completed image if it exists, or None if there is no complete load
|
/// Return a completed image if it exists, or None if there is no complete load
|
||||||
/// or the complete load is not fully decoded or is unavailable.
|
/// or the complete load is not fully decoded or is unavailable.
|
||||||
fn get_completed_image_if_available(&self,
|
fn get_completed_image_if_available(&self,
|
||||||
url: &ServoUrl,
|
url: &ServoUrl,
|
||||||
placeholder: UsePlaceholder)
|
placeholder: UsePlaceholder)
|
||||||
-> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
-> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
||||||
self.completed_loads.get(url).map(|completed_load| {
|
self.completed_loads.lock().unwrap().get(url).map(|completed_load| {
|
||||||
match (&completed_load.image_response, placeholder) {
|
match (&completed_load.image_response, placeholder) {
|
||||||
(&ImageResponse::Loaded(ref image), _) |
|
(&ImageResponse::Loaded(ref image), _) |
|
||||||
(&ImageResponse::PlaceholderLoaded(ref image), UsePlaceholder::Yes) => {
|
(&ImageResponse::PlaceholderLoaded(ref image), UsePlaceholder::Yes) => {
|
||||||
|
@ -572,29 +478,53 @@ impl ImageCache {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return any available metadata or image for the given URL, or an indication that
|
/// Handle a message from one of the decoder worker threads or from a sync
|
||||||
/// the image is not yet available if it is in progress, or else reserve a slot in
|
/// decoding operation.
|
||||||
/// the cache for the URL if the consumer can request images.
|
fn handle_decoder(&self, msg: DecoderMsg) {
|
||||||
fn get_image_or_meta_if_available(&mut self,
|
let image = match msg.image {
|
||||||
|
None => LoadResult::None,
|
||||||
|
Some(image) => LoadResult::Loaded(image),
|
||||||
|
};
|
||||||
|
self.complete_load(msg.key, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
|
||||||
|
pub fn new(webrender_api: webrender_traits::RenderApi) -> Arc<ImageCache> {
|
||||||
|
debug!("New image cache");
|
||||||
|
Arc::new(ImageCache {
|
||||||
|
pending_loads: Mutex::new(AllPendingLoads::new()),
|
||||||
|
completed_loads: Mutex::new(HashMap::new()),
|
||||||
|
placeholder_image: get_placeholder_image(&webrender_api).ok(),
|
||||||
|
webrender_api: Mutex::new(webrender_api),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return any available metadata or image for the given URL,
|
||||||
|
/// or an indication that the image is not yet available if it is in progress,
|
||||||
|
/// or else reserve a slot in the cache for the URL if the consumer can request images.
|
||||||
|
pub fn find_image_or_metadata(&self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
placeholder: UsePlaceholder,
|
use_placeholder: UsePlaceholder,
|
||||||
can_request: CanRequestImages)
|
can_request: CanRequestImages)
|
||||||
-> Result<ImageOrMetadataAvailable, ImageState> {
|
-> Result<ImageOrMetadataAvailable, ImageState> {
|
||||||
if let Some(result) = self.get_completed_image_if_available(&url, placeholder) {
|
debug!("Find image or metadata for {}", url);
|
||||||
|
if let Some(result) = self.get_completed_image_if_available(&url, use_placeholder) {
|
||||||
debug!("{} is available", url);
|
debug!("{} is available", url);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
let decoded = {
|
let decoded = {
|
||||||
let result = self.pending_loads.get_cached(url.clone(), can_request);
|
let mut pending_loads = self.pending_loads.lock().unwrap();
|
||||||
|
let result = pending_loads.get_cached(url.clone(), can_request);
|
||||||
match result {
|
match result {
|
||||||
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
|
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
|
||||||
(&Some(Ok(_)), _) => {
|
(&Some(Ok(_)), _) => {
|
||||||
debug!("sync decoding {} ({:?})", url, key);
|
debug!("Sync decoding {} ({:?})", url, key);
|
||||||
decode_bytes_sync(key, &pl.bytes.as_slice())
|
decode_bytes_sync(key, &pl.bytes.as_slice())
|
||||||
}
|
}
|
||||||
(&None, &Some(ref meta)) => {
|
(&None, &Some(ref meta)) => {
|
||||||
debug!("metadata available for {} ({:?})", url, key);
|
debug!("Metadata available for {} ({:?})", url, key);
|
||||||
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()))
|
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()))
|
||||||
}
|
}
|
||||||
(&Some(Err(_)), _) | (&None, &None) => {
|
(&Some(Err(_)), _) | (&None, &None) => {
|
||||||
|
@ -603,43 +533,104 @@ impl ImageCache {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CacheResult::Miss(Some((key, _pl))) => {
|
CacheResult::Miss(Some((key, _pl))) => {
|
||||||
debug!("should be requesting {} ({:?})", url, key);
|
debug!("Should be requesting {} ({:?})", url, key);
|
||||||
return Err(ImageState::NotRequested(key));
|
return Err(ImageState::NotRequested(key));
|
||||||
}
|
}
|
||||||
CacheResult::Miss(None) => {
|
CacheResult::Miss(None) => {
|
||||||
debug!("couldn't find an entry for {}", url);
|
debug!("Couldn't find an entry for {}", url);
|
||||||
return Err(ImageState::LoadError);
|
return Err(ImageState::LoadError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// In the case where a decode is ongoing (or waiting in a queue) but we have the
|
// In the case where a decode is ongoing (or waiting in a queue) but we
|
||||||
// full response available, we decode the bytes synchronously and ignore the
|
// have the full response available, we decode the bytes synchronously
|
||||||
// async decode when it finishes later.
|
// and ignore the async decode when it finishes later.
|
||||||
// TODO: make this behaviour configurable according to the caller's needs.
|
// TODO: make this behaviour configurable according to the caller's needs.
|
||||||
self.handle_decoder(decoded);
|
self.handle_decoder(decoded);
|
||||||
match self.get_completed_image_if_available(&url, placeholder) {
|
match self.get_completed_image_if_available(&url, use_placeholder) {
|
||||||
Some(result) => result,
|
Some(result) => result,
|
||||||
None => Err(ImageState::LoadError),
|
None => Err(ImageState::LoadError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a new listener for the given pending image id. If the image is already present,
|
||||||
|
/// the responder will still receive the expected response.
|
||||||
|
pub fn add_listener(&self, id: PendingImageId, listener: ImageResponder) {
|
||||||
|
if let Some(load) = self.pending_loads.lock().unwrap().get_by_key_mut(&id) {
|
||||||
|
if let Some(ref metadata) = load.metadata {
|
||||||
|
listener.respond(ImageResponse::MetadataLoaded(metadata.clone()));
|
||||||
|
}
|
||||||
|
load.add_listener(listener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(load) = self.completed_loads.lock().unwrap().values().find(|l| l.id == id) {
|
||||||
|
listener.respond(load.image_response.clone());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
warn!("Couldn't find cached entry for listener {:?}", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new image cache.
|
/// Inform the image cache about a response for a pending request.
|
||||||
pub fn new_image_cache_thread(webrender_api: webrender_traits::RenderApi) -> ImageCacheThread {
|
pub fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg) {
|
||||||
let (ipc_command_sender, ipc_command_receiver) = ipc::channel().unwrap();
|
match (action, id) {
|
||||||
|
(FetchResponseMsg::ProcessRequestBody, _) |
|
||||||
|
(FetchResponseMsg::ProcessRequestEOF, _) => return,
|
||||||
|
(FetchResponseMsg::ProcessResponse(_response), _) => {}
|
||||||
|
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
|
||||||
|
debug!("Got some data for {:?}", id);
|
||||||
|
let mut pending_loads = self.pending_loads.lock().unwrap();
|
||||||
|
let pending_load = pending_loads.get_by_key_mut(&id).unwrap();
|
||||||
|
pending_load.bytes.extend_from_slice(&data);
|
||||||
|
//jmr0 TODO: possibly move to another task?
|
||||||
|
if let None = pending_load.metadata {
|
||||||
|
if let Ok(metadata) = load_from_buf(&pending_load.bytes.as_slice()) {
|
||||||
|
let dimensions = metadata.dimensions();
|
||||||
|
let img_metadata = ImageMetadata { width: dimensions.width,
|
||||||
|
height: dimensions.height };
|
||||||
|
for listener in &pending_load.listeners {
|
||||||
|
listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
|
||||||
|
}
|
||||||
|
pending_load.metadata = Some(img_metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(FetchResponseMsg::ProcessResponseEOF(result), key) => {
|
||||||
|
debug!("Received EOF for {:?}", key);
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
let bytes = {
|
||||||
|
let mut pending_loads = self.pending_loads.lock().unwrap();
|
||||||
|
let pending_load = pending_loads.get_by_key_mut(&id).unwrap();
|
||||||
|
pending_load.result = Some(result);
|
||||||
|
debug!("Async decoding {} ({:?})", pending_load.url, key);
|
||||||
|
pending_load.bytes.mark_complete()
|
||||||
|
};
|
||||||
|
|
||||||
thread::Builder::new().name("ImageCacheThread".to_owned()).spawn(move || {
|
let (tx, rx) = channel();
|
||||||
ImageCache::run(webrender_api, ipc_command_receiver)
|
thread::Builder::new().name(
|
||||||
|
"Image decoding async operation".to_owned()).spawn(move || {
|
||||||
|
let msg = decode_bytes_sync(key, &*bytes);
|
||||||
|
tx.send(msg).unwrap();
|
||||||
}).expect("Thread spawning failed");
|
}).expect("Thread spawning failed");
|
||||||
|
|
||||||
ImageCacheThread::new(ipc_command_sender)
|
if let Some(msg) = rx.recv().ok() {
|
||||||
|
debug!("Image decoded {:?}", key);
|
||||||
|
self.handle_decoder(msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
debug!("Processing error for {:?}", key);
|
||||||
|
match self.placeholder_image.clone() {
|
||||||
|
Some(placeholder_image) => {
|
||||||
|
self.complete_load(id, LoadResult::PlaceholderLoaded(
|
||||||
|
placeholder_image))
|
||||||
|
}
|
||||||
|
None => self.complete_load(id, LoadResult::None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
|
|
||||||
let image = load_from_memory(bytes);
|
|
||||||
DecoderMsg {
|
|
||||||
key: key,
|
|
||||||
image: image
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,169 +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 http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
use FetchResponseMsg;
|
|
||||||
use image::base::{Image, ImageMetadata};
|
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
|
||||||
use servo_url::ServoUrl;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// This is optionally passed to the image cache when requesting
|
|
||||||
/// and image, and returned to the specified event loop when the
|
|
||||||
/// image load completes. It is typically used to trigger a reflow
|
|
||||||
/// and/or repaint.
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct ImageResponder {
|
|
||||||
id: PendingImageId,
|
|
||||||
sender: IpcSender<PendingImageResponse>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct PendingImageResponse {
|
|
||||||
pub response: ImageResponse,
|
|
||||||
pub id: PendingImageId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageResponder {
|
|
||||||
pub fn new(sender: IpcSender<PendingImageResponse>, id: PendingImageId) -> ImageResponder {
|
|
||||||
ImageResponder {
|
|
||||||
sender: sender,
|
|
||||||
id: id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn respond(&self, response: ImageResponse) {
|
|
||||||
// This send can fail if thread waiting for this notification has panicked.
|
|
||||||
// That's not a case that's worth warning about.
|
|
||||||
// TODO(#15501): are there cases in which we should perform cleanup?
|
|
||||||
let _ = self.sender.send(PendingImageResponse {
|
|
||||||
response: response,
|
|
||||||
id: self.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The unique id for an image that has previously been requested.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Deserialize, Serialize, HeapSizeOf, Hash, Debug)]
|
|
||||||
pub struct PendingImageId(pub u64);
|
|
||||||
|
|
||||||
/// The current state of an image in the cache.
|
|
||||||
#[derive(PartialEq, Copy, Clone, Deserialize, Serialize)]
|
|
||||||
pub enum ImageState {
|
|
||||||
Pending(PendingImageId),
|
|
||||||
LoadError,
|
|
||||||
NotRequested(PendingImageId),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The returned image.
|
|
||||||
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
|
|
||||||
pub enum ImageResponse {
|
|
||||||
/// The requested image was loaded.
|
|
||||||
Loaded(Arc<Image>),
|
|
||||||
/// The request image metadata was loaded.
|
|
||||||
MetadataLoaded(ImageMetadata),
|
|
||||||
/// The requested image failed to load, so a placeholder was loaded instead.
|
|
||||||
PlaceholderLoaded(Arc<Image>),
|
|
||||||
/// Neither the requested image nor the placeholder could be loaded.
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Indicating either entire image or just metadata availability
|
|
||||||
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
|
|
||||||
pub enum ImageOrMetadataAvailable {
|
|
||||||
ImageAvailable(Arc<Image>),
|
|
||||||
MetadataAvailable(ImageMetadata),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Commands that the image cache understands.
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub enum ImageCacheCommand {
|
|
||||||
/// Synchronously check the state of an image in the cache. If the image is in a loading
|
|
||||||
/// state and but its metadata has been made available, it will be sent as a response.
|
|
||||||
GetImageOrMetadataIfAvailable(ServoUrl,
|
|
||||||
UsePlaceholder,
|
|
||||||
CanRequestImages,
|
|
||||||
IpcSender<Result<ImageOrMetadataAvailable, ImageState>>),
|
|
||||||
|
|
||||||
/// Add a new listener for the given pending image.
|
|
||||||
AddListener(PendingImageId, ImageResponder),
|
|
||||||
|
|
||||||
/// Instruct the cache to store this data as a newly-complete network request and continue
|
|
||||||
/// decoding the result into pixel data
|
|
||||||
StoreDecodeImage(PendingImageId, FetchResponseMsg),
|
|
||||||
|
|
||||||
/// Clients must wait for a response before shutting down the ResourceThread
|
|
||||||
Exit(IpcSender<()>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Hash, Eq, Deserialize, Serialize)]
|
|
||||||
pub enum UsePlaceholder {
|
|
||||||
No,
|
|
||||||
Yes,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether a consumer is in a position to request images or not. This can occur when
|
|
||||||
/// animations are being processed by the layout thread while the script thread is executing
|
|
||||||
/// in parallel.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Deserialize, Serialize)]
|
|
||||||
pub enum CanRequestImages {
|
|
||||||
No,
|
|
||||||
Yes,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The client side of the image cache thread. This can be safely cloned
|
|
||||||
/// and passed to different threads.
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct ImageCacheThread {
|
|
||||||
chan: IpcSender<ImageCacheCommand>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The public API for the image cache thread.
|
|
||||||
impl ImageCacheThread {
|
|
||||||
/// Construct a new image cache
|
|
||||||
pub fn new(chan: IpcSender<ImageCacheCommand>) -> ImageCacheThread {
|
|
||||||
ImageCacheThread {
|
|
||||||
chan: chan,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current state of an image, returning its metadata if available.
|
|
||||||
/// See ImageCacheCommand::GetImageOrMetadataIfAvailable.
|
|
||||||
///
|
|
||||||
/// FIXME: We shouldn't do IPC for data uris!
|
|
||||||
pub fn find_image_or_metadata(&self,
|
|
||||||
url: ServoUrl,
|
|
||||||
use_placeholder: UsePlaceholder,
|
|
||||||
can_request: CanRequestImages)
|
|
||||||
-> Result<ImageOrMetadataAvailable, ImageState> {
|
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
|
||||||
let msg = ImageCacheCommand::GetImageOrMetadataIfAvailable(url,
|
|
||||||
use_placeholder,
|
|
||||||
can_request,
|
|
||||||
sender);
|
|
||||||
let _ = self.chan.send(msg);
|
|
||||||
try!(receiver.recv().map_err(|_| ImageState::LoadError))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a new listener for the given pending image id. If the image is already present,
|
|
||||||
/// the responder will still receive the expected response.
|
|
||||||
pub fn add_listener(&self, id: PendingImageId, responder: ImageResponder) {
|
|
||||||
let msg = ImageCacheCommand::AddListener(id, responder);
|
|
||||||
self.chan.send(msg).expect("Image cache thread is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inform the image cache about a response for a pending request.
|
|
||||||
pub fn notify_pending_response(&self, id: PendingImageId, data: FetchResponseMsg) {
|
|
||||||
let msg = ImageCacheCommand::StoreDecodeImage(id, data);
|
|
||||||
self.chan.send(msg).expect("Image cache thread is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shutdown the image cache thread.
|
|
||||||
pub fn exit(&self) {
|
|
||||||
// If the image cache is not available when we're trying to shut it down,
|
|
||||||
// that is not worth warning about.
|
|
||||||
let (response_chan, response_port) = ipc::channel().unwrap();
|
|
||||||
let _ = self.chan.send(ImageCacheCommand::Exit(response_chan));
|
|
||||||
let _ = response_port.recv();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,7 @@ extern crate heapsize_derive;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate hyper_serde;
|
extern crate hyper_serde;
|
||||||
extern crate image as piston_image;
|
extern crate image as piston_image;
|
||||||
|
extern crate immeta;
|
||||||
extern crate ipc_channel;
|
extern crate ipc_channel;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
@ -50,7 +51,7 @@ use storage_thread::StorageThreadMsg;
|
||||||
pub mod blob_url_store;
|
pub mod blob_url_store;
|
||||||
pub mod filemanager_thread;
|
pub mod filemanager_thread;
|
||||||
pub mod hosts;
|
pub mod hosts;
|
||||||
pub mod image_cache_thread;
|
pub mod image_cache;
|
||||||
pub mod net_error_list;
|
pub mod net_error_list;
|
||||||
pub mod pub_domains;
|
pub mod pub_domains;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
|
|
|
@ -62,7 +62,7 @@ use msg::constellation_msg::{FrameId, FrameType, PipelineId};
|
||||||
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
|
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
|
||||||
use net_traits::filemanager_thread::RelativePos;
|
use net_traits::filemanager_thread::RelativePos;
|
||||||
use net_traits::image::base::{Image, ImageMetadata};
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache_thread::{ImageCacheThread, PendingImageId};
|
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||||
use net_traits::request::{Request, RequestInit};
|
use net_traits::request::{Request, RequestInit};
|
||||||
use net_traits::response::{Response, ResponseBody};
|
use net_traits::response::{Response, ResponseBody};
|
||||||
use net_traits::response::HttpsState;
|
use net_traits::response::HttpsState;
|
||||||
|
@ -321,7 +321,7 @@ unsafe_no_jsmanaged_fields!(bool, f32, f64, String, AtomicBool, AtomicUsize, Uui
|
||||||
unsafe_no_jsmanaged_fields!(usize, u8, u16, u32, u64);
|
unsafe_no_jsmanaged_fields!(usize, u8, u16, u32, u64);
|
||||||
unsafe_no_jsmanaged_fields!(isize, i8, i16, i32, i64);
|
unsafe_no_jsmanaged_fields!(isize, i8, i16, i32, i64);
|
||||||
unsafe_no_jsmanaged_fields!(ServoUrl, ImmutableOrigin, MutableOrigin);
|
unsafe_no_jsmanaged_fields!(ServoUrl, ImmutableOrigin, MutableOrigin);
|
||||||
unsafe_no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheThread, PendingImageId);
|
unsafe_no_jsmanaged_fields!(Image, ImageMetadata, ImageCache, PendingImageId);
|
||||||
unsafe_no_jsmanaged_fields!(Metadata);
|
unsafe_no_jsmanaged_fields!(Metadata);
|
||||||
unsafe_no_jsmanaged_fields!(NetworkError);
|
unsafe_no_jsmanaged_fields!(NetworkError);
|
||||||
unsafe_no_jsmanaged_fields!(Atom, Prefix, LocalName, Namespace, QualName);
|
unsafe_no_jsmanaged_fields!(Atom, Prefix, LocalName, Namespace, QualName);
|
||||||
|
|
|
@ -40,7 +40,7 @@ use euclid::rect::Rect;
|
||||||
use euclid::size::Size2D;
|
use euclid::size::Size2D;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use net_traits::image::base::PixelFormat;
|
use net_traits::image::base::PixelFormat;
|
||||||
use net_traits::image_cache_thread::ImageResponse;
|
use net_traits::image_cache::ImageResponse;
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use script_traits::ScriptMsg as ConstellationMsg;
|
use script_traits::ScriptMsg as ConstellationMsg;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
|
|
@ -338,12 +338,12 @@ impl<'a> From<&'a WebGLContextAttributes> for GLContextAttributes {
|
||||||
|
|
||||||
pub mod utils {
|
pub mod utils {
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use net_traits::image_cache_thread::{ImageResponse, UsePlaceholder, ImageOrMetadataAvailable};
|
use net_traits::image_cache::{ImageResponse, UsePlaceholder, ImageOrMetadataAvailable};
|
||||||
use net_traits::image_cache_thread::CanRequestImages;
|
use net_traits::image_cache::CanRequestImages;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
|
||||||
pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
|
pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
|
||||||
let image_cache = window.image_cache_thread();
|
let image_cache = window.image_cache();
|
||||||
let response =
|
let response =
|
||||||
image_cache.find_image_or_metadata(url.into(),
|
image_cache.find_image_or_metadata(url.into(),
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
|
|
|
@ -40,9 +40,9 @@ use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use net_traits::{FetchResponseListener, FetchMetadata, NetworkError, FetchResponseMsg};
|
use net_traits::{FetchResponseListener, FetchMetadata, NetworkError, FetchResponseMsg};
|
||||||
use net_traits::image::base::{Image, ImageMetadata};
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache_thread::{ImageResponder, ImageResponse, PendingImageId, ImageState};
|
use net_traits::image_cache::{ImageResponder, ImageResponse, PendingImageId, ImageState};
|
||||||
use net_traits::image_cache_thread::{UsePlaceholder, ImageOrMetadataAvailable, CanRequestImages};
|
use net_traits::image_cache::{UsePlaceholder, ImageOrMetadataAvailable, CanRequestImages};
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
use net_traits::image_cache::ImageCache;
|
||||||
use net_traits::request::{RequestInit, Type as RequestType};
|
use net_traits::request::{RequestInit, Type as RequestType};
|
||||||
use network_listener::{NetworkListener, PreInvoke};
|
use network_listener::{NetworkListener, PreInvoke};
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
@ -120,8 +120,8 @@ impl Runnable for ImageResponseHandlerRunnable {
|
||||||
|
|
||||||
/// The context required for asynchronously loading an external image.
|
/// The context required for asynchronously loading an external image.
|
||||||
struct ImageContext {
|
struct ImageContext {
|
||||||
/// A handle with which to communicate with the image cache.
|
/// Reference to the script thread image cache.
|
||||||
image_cache: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
/// Indicates whether the request failed, and why
|
/// Indicates whether the request failed, and why
|
||||||
status: Result<(), NetworkError>,
|
status: Result<(), NetworkError>,
|
||||||
/// The cache ID for this request.
|
/// The cache ID for this request.
|
||||||
|
@ -186,7 +186,7 @@ impl HTMLImageElement {
|
||||||
Some(LoadBlocker::new(&*document, LoadType::Image(img_url.clone())));
|
Some(LoadBlocker::new(&*document, LoadType::Image(img_url.clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_cache_listener_for_element(image_cache: &ImageCacheThread,
|
fn add_cache_listener_for_element(image_cache: Arc<ImageCache>,
|
||||||
id: PendingImageId,
|
id: PendingImageId,
|
||||||
elem: &HTMLImageElement) {
|
elem: &HTMLImageElement) {
|
||||||
let trusted_node = Trusted::new(elem);
|
let trusted_node = Trusted::new(elem);
|
||||||
|
@ -197,8 +197,9 @@ impl HTMLImageElement {
|
||||||
let wrapper = window.get_runnable_wrapper();
|
let wrapper = window.get_runnable_wrapper();
|
||||||
let generation = elem.generation.get();
|
let generation = elem.generation.get();
|
||||||
ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
|
ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
|
||||||
// Return the image via a message to the script thread, which marks the element
|
debug!("Got image {:?}", message);
|
||||||
// as dirty and triggers a reflow.
|
// Return the image via a message to the script thread, which marks
|
||||||
|
// the element as dirty and triggers a reflow.
|
||||||
let runnable = ImageResponseHandlerRunnable::new(
|
let runnable = ImageResponseHandlerRunnable::new(
|
||||||
trusted_node.clone(), message.to().unwrap(), generation);
|
trusted_node.clone(), message.to().unwrap(), generation);
|
||||||
let _ = task_source.queue_with_wrapper(box runnable, &wrapper);
|
let _ = task_source.queue_with_wrapper(box runnable, &wrapper);
|
||||||
|
@ -208,7 +209,7 @@ impl HTMLImageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let image_cache = window.image_cache_thread();
|
let image_cache = window.image_cache();
|
||||||
let response =
|
let response =
|
||||||
image_cache.find_image_or_metadata(img_url.clone().into(),
|
image_cache.find_image_or_metadata(img_url.clone().into(),
|
||||||
UsePlaceholder::Yes,
|
UsePlaceholder::Yes,
|
||||||
|
@ -223,7 +224,7 @@ impl HTMLImageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ImageState::Pending(id)) => {
|
Err(ImageState::Pending(id)) => {
|
||||||
add_cache_listener_for_element(image_cache, id, self);
|
add_cache_listener_for_element(image_cache.clone(), id, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ImageState::LoadError) => {
|
Err(ImageState::LoadError) => {
|
||||||
|
@ -242,7 +243,7 @@ impl HTMLImageElement {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
|
|
||||||
let context = Arc::new(Mutex::new(ImageContext {
|
let context = Arc::new(Mutex::new(ImageContext {
|
||||||
image_cache: window.image_cache_thread().clone(),
|
image_cache: window.image_cache(),
|
||||||
status: Ok(()),
|
status: Ok(()),
|
||||||
id: id,
|
id: id,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -42,7 +42,7 @@ use js::jsapi::{JSContext, JSObject, Type, Rooted};
|
||||||
use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
|
use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
|
||||||
use js::typedarray::{TypedArray, TypedArrayElement, Float32, Int32};
|
use js::typedarray::{TypedArray, TypedArrayElement, Float32, Int32};
|
||||||
use net_traits::image::base::PixelFormat;
|
use net_traits::image::base::PixelFormat;
|
||||||
use net_traits::image_cache_thread::ImageResponse;
|
use net_traits::image_cache::ImageResponse;
|
||||||
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
||||||
use script_traits::ScriptMsg as ConstellationMsg;
|
use script_traits::ScriptMsg as ConstellationMsg;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
|
@ -62,8 +62,8 @@ use js::rust::Runtime;
|
||||||
use layout_image::fetch_image_for_layout;
|
use layout_image::fetch_image_for_layout;
|
||||||
use msg::constellation_msg::{FrameType, PipelineId};
|
use msg::constellation_msg::{FrameType, PipelineId};
|
||||||
use net_traits::{ResourceThreads, ReferrerPolicy};
|
use net_traits::{ResourceThreads, ReferrerPolicy};
|
||||||
use net_traits::image_cache_thread::{ImageResponder, ImageResponse};
|
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
|
||||||
use net_traits::image_cache_thread::{PendingImageResponse, ImageCacheThread, PendingImageId};
|
use net_traits::image_cache::{PendingImageId, PendingImageResponse};
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use open;
|
use open;
|
||||||
|
@ -167,8 +167,8 @@ pub struct Window {
|
||||||
#[ignore_heap_size_of = "task sources are hard"]
|
#[ignore_heap_size_of = "task sources are hard"]
|
||||||
file_reading_task_source: FileReadingTaskSource,
|
file_reading_task_source: FileReadingTaskSource,
|
||||||
navigator: MutNullableJS<Navigator>,
|
navigator: MutNullableJS<Navigator>,
|
||||||
#[ignore_heap_size_of = "channels are hard"]
|
#[ignore_heap_size_of = "Arc"]
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
#[ignore_heap_size_of = "channels are hard"]
|
#[ignore_heap_size_of = "channels are hard"]
|
||||||
image_cache_chan: Sender<ImageCacheMsg>,
|
image_cache_chan: Sender<ImageCacheMsg>,
|
||||||
browsing_context: MutNullableJS<BrowsingContext>,
|
browsing_context: MutNullableJS<BrowsingContext>,
|
||||||
|
@ -315,8 +315,8 @@ impl Window {
|
||||||
(box SendableMainThreadScriptChan(tx), box rx)
|
(box SendableMainThreadScriptChan(tx), box rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image_cache_thread(&self) -> &ImageCacheThread {
|
pub fn image_cache(&self) -> Arc<ImageCache> {
|
||||||
&self.image_cache_thread
|
self.image_cache.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This can panic if it is called after the browsing context has been discarded
|
/// This can panic if it is called after the browsing context has been discarded
|
||||||
|
@ -1227,7 +1227,7 @@ impl Window {
|
||||||
let node = from_untrusted_node_address(js_runtime.rt(), image.node);
|
let node = from_untrusted_node_address(js_runtime.rt(), image.node);
|
||||||
|
|
||||||
if let PendingImageState::Unrequested(ref url) = image.state {
|
if let PendingImageState::Unrequested(ref url) = image.state {
|
||||||
fetch_image_for_layout(url.clone(), &*node, id, self.image_cache_thread.clone());
|
fetch_image_for_layout(url.clone(), &*node, id, self.image_cache.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut images = self.pending_layout_images.borrow_mut();
|
let mut images = self.pending_layout_images.borrow_mut();
|
||||||
|
@ -1239,7 +1239,7 @@ impl Window {
|
||||||
ROUTER.add_route(responder_listener.to_opaque(), box move |message| {
|
ROUTER.add_route(responder_listener.to_opaque(), box move |message| {
|
||||||
let _ = image_cache_chan.send((pipeline, message.to().unwrap()));
|
let _ = image_cache_chan.send((pipeline, message.to().unwrap()));
|
||||||
});
|
});
|
||||||
self.image_cache_thread.add_listener(id, ImageResponder::new(responder, id));
|
self.image_cache.add_listener(id, ImageResponder::new(responder, id));
|
||||||
nodes.push(JS::from_ref(&*node));
|
nodes.push(JS::from_ref(&*node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1703,7 +1703,7 @@ impl Window {
|
||||||
history_task_source: HistoryTraversalTaskSource,
|
history_task_source: HistoryTraversalTaskSource,
|
||||||
file_task_source: FileReadingTaskSource,
|
file_task_source: FileReadingTaskSource,
|
||||||
image_cache_chan: Sender<ImageCacheMsg>,
|
image_cache_chan: Sender<ImageCacheMsg>,
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
resource_threads: ResourceThreads,
|
resource_threads: ResourceThreads,
|
||||||
bluetooth_thread: IpcSender<BluetoothRequest>,
|
bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||||
mem_profiler_chan: MemProfilerChan,
|
mem_profiler_chan: MemProfilerChan,
|
||||||
|
@ -1747,8 +1747,8 @@ impl Window {
|
||||||
history_traversal_task_source: history_task_source,
|
history_traversal_task_source: history_task_source,
|
||||||
file_reading_task_source: file_task_source,
|
file_reading_task_source: file_task_source,
|
||||||
image_cache_chan: image_cache_chan,
|
image_cache_chan: image_cache_chan,
|
||||||
|
image_cache: image_cache.clone(),
|
||||||
navigator: Default::default(),
|
navigator: Default::default(),
|
||||||
image_cache_thread: image_cache_thread,
|
|
||||||
history: Default::default(),
|
history: Default::default(),
|
||||||
browsing_context: Default::default(),
|
browsing_context: Default::default(),
|
||||||
document: Default::default(),
|
document: Default::default(),
|
||||||
|
|
|
@ -12,7 +12,7 @@ use dom::node::{Node, document_from_node};
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use net_traits::{FetchResponseMsg, FetchResponseListener, FetchMetadata, NetworkError};
|
use net_traits::{FetchResponseMsg, FetchResponseListener, FetchMetadata, NetworkError};
|
||||||
use net_traits::image_cache_thread::{ImageCacheThread, PendingImageId};
|
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||||
use net_traits::request::{Type as RequestType, RequestInit as FetchRequestInit};
|
use net_traits::request::{Type as RequestType, RequestInit as FetchRequestInit};
|
||||||
use network_listener::{NetworkListener, PreInvoke};
|
use network_listener::{NetworkListener, PreInvoke};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -20,7 +20,7 @@ use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
struct LayoutImageContext {
|
struct LayoutImageContext {
|
||||||
id: PendingImageId,
|
id: PendingImageId,
|
||||||
cache: ImageCacheThread,
|
cache: Arc<ImageCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchResponseListener for LayoutImageContext {
|
impl FetchResponseListener for LayoutImageContext {
|
||||||
|
@ -49,7 +49,7 @@ impl PreInvoke for LayoutImageContext {}
|
||||||
pub fn fetch_image_for_layout(url: ServoUrl,
|
pub fn fetch_image_for_layout(url: ServoUrl,
|
||||||
node: &Node,
|
node: &Node,
|
||||||
id: PendingImageId,
|
id: PendingImageId,
|
||||||
cache: ImageCacheThread) {
|
cache: Arc<ImageCache>) {
|
||||||
let context = Arc::new(Mutex::new(LayoutImageContext {
|
let context = Arc::new(Mutex::new(LayoutImageContext {
|
||||||
id: id,
|
id: id,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
|
|
|
@ -73,7 +73,7 @@ use microtask::{MicrotaskQueue, Microtask};
|
||||||
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
|
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
|
||||||
use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener};
|
use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener};
|
||||||
use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads};
|
use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads};
|
||||||
use net_traits::image_cache_thread::{PendingImageResponse, ImageCacheThread};
|
use net_traits::image_cache::{ImageCache, PendingImageResponse};
|
||||||
use net_traits::request::{CredentialsMode, Destination, RequestInit};
|
use net_traits::request::{CredentialsMode, Destination, RequestInit};
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use network_listener::NetworkListener;
|
use network_listener::NetworkListener;
|
||||||
|
@ -233,7 +233,7 @@ enum MixedMessage {
|
||||||
FromScript(MainThreadScriptMsg),
|
FromScript(MainThreadScriptMsg),
|
||||||
FromDevtools(DevtoolScriptControlMsg),
|
FromDevtools(DevtoolScriptControlMsg),
|
||||||
FromImageCache((PipelineId, PendingImageResponse)),
|
FromImageCache((PipelineId, PendingImageResponse)),
|
||||||
FromScheduler(TimerEvent)
|
FromScheduler(TimerEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages used to control the script event loop
|
/// Messages used to control the script event loop
|
||||||
|
@ -408,8 +408,8 @@ pub struct ScriptThread {
|
||||||
registration_map: DOMRefCell<HashMap<ServoUrl, JS<ServiceWorkerRegistration>>>,
|
registration_map: DOMRefCell<HashMap<ServoUrl, JS<ServiceWorkerRegistration>>>,
|
||||||
/// A job queue for Service Workers keyed by their scope url
|
/// A job queue for Service Workers keyed by their scope url
|
||||||
job_queue_map: Rc<JobQueue>,
|
job_queue_map: Rc<JobQueue>,
|
||||||
/// A handle to the image cache thread.
|
/// Image cache for this script thread.
|
||||||
image_cache_thread: ImageCacheThread,
|
image_cache: Arc<ImageCache>,
|
||||||
/// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if
|
/// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if
|
||||||
/// there are many iframes.
|
/// there are many iframes.
|
||||||
resource_threads: ResourceThreads,
|
resource_threads: ResourceThreads,
|
||||||
|
@ -450,7 +450,6 @@ pub struct ScriptThread {
|
||||||
|
|
||||||
/// The channel on which the image cache can send messages to ourself.
|
/// The channel on which the image cache can send messages to ourself.
|
||||||
image_cache_channel: Sender<ImageCacheMsg>,
|
image_cache_channel: Sender<ImageCacheMsg>,
|
||||||
|
|
||||||
/// For providing contact with the time profiler.
|
/// For providing contact with the time profiler.
|
||||||
time_profiler_chan: time::ProfilerChan,
|
time_profiler_chan: time::ProfilerChan,
|
||||||
|
|
||||||
|
@ -685,7 +684,7 @@ impl ScriptThread {
|
||||||
registration_map: DOMRefCell::new(HashMap::new()),
|
registration_map: DOMRefCell::new(HashMap::new()),
|
||||||
job_queue_map: Rc::new(JobQueue::new()),
|
job_queue_map: Rc::new(JobQueue::new()),
|
||||||
|
|
||||||
image_cache_thread: state.image_cache_thread,
|
image_cache: state.image_cache.clone(),
|
||||||
image_cache_channel: image_cache_channel,
|
image_cache_channel: image_cache_channel,
|
||||||
image_cache_port: image_cache_port,
|
image_cache_port: image_cache_port,
|
||||||
|
|
||||||
|
@ -1267,7 +1266,7 @@ impl ScriptThread {
|
||||||
pipeline_port: pipeline_port,
|
pipeline_port: pipeline_port,
|
||||||
constellation_chan: self.layout_to_constellation_chan.clone(),
|
constellation_chan: self.layout_to_constellation_chan.clone(),
|
||||||
script_chan: self.control_chan.clone(),
|
script_chan: self.control_chan.clone(),
|
||||||
image_cache_thread: self.image_cache_thread.clone(),
|
image_cache: self.image_cache.clone(),
|
||||||
content_process_shutdown_chan: content_process_shutdown_chan,
|
content_process_shutdown_chan: content_process_shutdown_chan,
|
||||||
layout_threads: layout_threads,
|
layout_threads: layout_threads,
|
||||||
});
|
});
|
||||||
|
@ -1756,7 +1755,7 @@ impl ScriptThread {
|
||||||
HistoryTraversalTaskSource(history_sender.clone()),
|
HistoryTraversalTaskSource(history_sender.clone()),
|
||||||
self.file_reading_task_source.clone(),
|
self.file_reading_task_source.clone(),
|
||||||
self.image_cache_channel.clone(),
|
self.image_cache_channel.clone(),
|
||||||
self.image_cache_thread.clone(),
|
self.image_cache.clone(),
|
||||||
self.resource_threads.clone(),
|
self.resource_threads.clone(),
|
||||||
self.bluetooth_thread.clone(),
|
self.bluetooth_thread.clone(),
|
||||||
self.mem_profiler_chan.clone(),
|
self.mem_profiler_chan.clone(),
|
||||||
|
|
|
@ -43,7 +43,7 @@ use canvas_traits::CanvasMsg;
|
||||||
use core::nonzero::NonZero;
|
use core::nonzero::NonZero;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use net_traits::image_cache_thread::PendingImageId;
|
use net_traits::image_cache::PendingImageId;
|
||||||
use script_traits::UntrustedNodeAddress;
|
use script_traits::UntrustedNodeAddress;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::sync::atomic::AtomicIsize;
|
use std::sync::atomic::AtomicIsize;
|
||||||
|
|
|
@ -9,7 +9,7 @@ use euclid::rect::Rect;
|
||||||
use gfx_traits::Epoch;
|
use gfx_traits::Epoch;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
use net_traits::image_cache::ImageCache;
|
||||||
use profile_traits::mem::ReportsChan;
|
use profile_traits::mem::ReportsChan;
|
||||||
use rpc::LayoutRPC;
|
use rpc::LayoutRPC;
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
|
||||||
|
@ -143,7 +143,7 @@ pub struct NewLayoutThreadInfo {
|
||||||
pub pipeline_port: IpcReceiver<LayoutControlMsg>,
|
pub pipeline_port: IpcReceiver<LayoutControlMsg>,
|
||||||
pub constellation_chan: IpcSender<ConstellationMsg>,
|
pub constellation_chan: IpcSender<ConstellationMsg>,
|
||||||
pub script_chan: IpcSender<ConstellationControlMsg>,
|
pub script_chan: IpcSender<ConstellationControlMsg>,
|
||||||
pub image_cache_thread: ImageCacheThread,
|
pub image_cache: Arc<ImageCache>,
|
||||||
pub content_process_shutdown_chan: Option<IpcSender<()>>,
|
pub content_process_shutdown_chan: Option<IpcSender<()>>,
|
||||||
pub layout_threads: usize,
|
pub layout_threads: usize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ use msg::constellation_msg::{FrameId, FrameType, Key, KeyModifiers, KeyState};
|
||||||
use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection};
|
use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection};
|
||||||
use net_traits::{ReferrerPolicy, ResourceThreads};
|
use net_traits::{ReferrerPolicy, ResourceThreads};
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
use net_traits::image_cache_thread::ImageCacheThread;
|
use net_traits::image_cache::ImageCache;
|
||||||
use net_traits::response::HttpsState;
|
use net_traits::response::HttpsState;
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use profile_traits::mem;
|
use profile_traits::mem;
|
||||||
|
@ -67,6 +67,7 @@ use servo_url::ImmutableOrigin;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use style_traits::{CSSPixel, UnsafeNode};
|
use style_traits::{CSSPixel, UnsafeNode};
|
||||||
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||||
|
@ -483,8 +484,8 @@ pub struct InitialScriptState {
|
||||||
pub resource_threads: ResourceThreads,
|
pub resource_threads: ResourceThreads,
|
||||||
/// A channel to the bluetooth thread.
|
/// A channel to the bluetooth thread.
|
||||||
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||||
/// A channel to the image cache thread.
|
/// The image cache for this script thread.
|
||||||
pub image_cache_thread: ImageCacheThread,
|
pub image_cache: Arc<ImageCache>,
|
||||||
/// A channel to the time profiler thread.
|
/// A channel to the time profiler thread.
|
||||||
pub time_profiler_chan: profile_traits::time::ProfilerChan,
|
pub time_profiler_chan: profile_traits::time::ProfilerChan,
|
||||||
/// A channel to the memory profiler thread.
|
/// A channel to the memory profiler thread.
|
||||||
|
|
|
@ -82,7 +82,6 @@ use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use log::{Log, LogMetadata, LogRecord};
|
use log::{Log, LogMetadata, LogRecord};
|
||||||
use net::image_cache_thread::new_image_cache_thread;
|
|
||||||
use net::resource_thread::new_resource_threads;
|
use net::resource_thread::new_resource_threads;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
use profile::mem as profile_mem;
|
use profile::mem as profile_mem;
|
||||||
|
@ -290,7 +289,6 @@ fn create_constellation(user_agent: Cow<'static, str>,
|
||||||
devtools_chan.clone(),
|
devtools_chan.clone(),
|
||||||
time_profiler_chan.clone(),
|
time_profiler_chan.clone(),
|
||||||
config_dir);
|
config_dir);
|
||||||
let image_cache_thread = new_image_cache_thread(webrender_api_sender.create_api());
|
|
||||||
let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
|
let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
|
||||||
Some(webrender_api_sender.create_api()));
|
Some(webrender_api_sender.create_api()));
|
||||||
|
|
||||||
|
@ -301,7 +299,6 @@ fn create_constellation(user_agent: Cow<'static, str>,
|
||||||
debugger_chan: debugger_chan,
|
debugger_chan: debugger_chan,
|
||||||
devtools_chan: devtools_chan,
|
devtools_chan: devtools_chan,
|
||||||
bluetooth_thread: bluetooth_thread,
|
bluetooth_thread: bluetooth_thread,
|
||||||
image_cache_thread: image_cache_thread,
|
|
||||||
font_cache_thread: font_cache_thread,
|
font_cache_thread: font_cache_thread,
|
||||||
public_resource_threads: public_resource_threads,
|
public_resource_threads: public_resource_threads,
|
||||||
private_resource_threads: private_resource_threads,
|
private_resource_threads: private_resource_threads,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue