diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-01-31 01:18:41 +0530 |
---|---|---|
committer | bors-servo <lbergstrom+bors@mozilla.com> | 2016-01-31 01:18:41 +0530 |
commit | 46b3eb653579a40632f91497a3d48f1d7fbd40cc (patch) | |
tree | e45b6e5fcc181c7c8eb9647c1ba8f27f857bd86f | |
parent | f8bdda499ef8c13f65cbc20215bd36dbc42b6439 (diff) | |
parent | 167ffa7a95ae0068d2b4f900d2207436a1799675 (diff) | |
download | servo-46b3eb653579a40632f91497a3d48f1d7fbd40cc.tar.gz servo-46b3eb653579a40632f91497a3d48f1d7fbd40cc.zip |
Auto merge of #9208 - jmr0:master-img, r=asajeffrey
Use image metadata to lay out images
Fixes #7047.
cc: @jdm
<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9208)
<!-- Reviewable:end -->
-rw-r--r-- | components/layout/context.rs | 51 | ||||
-rw-r--r-- | components/layout/fragment.rs | 38 | ||||
-rw-r--r-- | components/msg/constellation_msg.rs | 6 | ||||
-rw-r--r-- | components/net/Cargo.toml | 1 | ||||
-rw-r--r-- | components/net/image_cache_thread.rs | 114 | ||||
-rw-r--r-- | components/net/lib.rs | 1 | ||||
-rw-r--r-- | components/net_traits/image/base.rs | 2 | ||||
-rw-r--r-- | components/net_traits/image_cache_thread.rs | 47 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 4 | ||||
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 2 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 33 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 3 | ||||
-rw-r--r-- | components/servo/Cargo.lock | 10 | ||||
-rw-r--r-- | ports/cef/Cargo.lock | 10 | ||||
-rw-r--r-- | ports/gonk/Cargo.lock | 10 |
15 files changed, 269 insertions, 63 deletions
diff --git a/components/layout/context.rs b/components/layout/context.rs index 83f2df1412a..53bb036d09c 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -17,7 +17,7 @@ use gfx_traits::LayerId; use ipc_channel::ipc::{self, IpcSender}; use net_traits::image::base::Image; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState}; -use net_traits::image_cache_thread::{UsePlaceholder}; +use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; use std::cell::{RefCell, RefMut}; use std::collections::HashMap; use std::collections::hash_state::DefaultState; @@ -155,7 +155,7 @@ impl<'a> LayoutContext<'a> { match sync_rx.recv().unwrap().image_response { ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => Some(image), - ImageResponse::None => None, + ImageResponse::None | ImageResponse::MetadataLoaded(_) => None, } } // Not yet requested, async mode - request image from the cache @@ -172,4 +172,51 @@ impl<'a> LayoutContext<'a> { } } } + + pub fn get_or_request_image_or_meta(&self, url: Url, use_placeholder: UsePlaceholder) + -> Option<ImageOrMetadataAvailable> { + // See if the image is already available + let result = self.shared.image_cache_thread.find_image_or_metadata(url.clone(), + use_placeholder); + + match result { + Ok(image_or_metadata) => Some(image_or_metadata), + Err(state) => { + // If we are emitting an output file, then we need to block on + // image load or we risk emitting an output file missing the image. + let is_sync = opts::get().output_file.is_some() || + opts::get().exit_after_load; + + match (state, is_sync) { + // Image failed to load, so just return nothing + (ImageState::LoadError, _) => None, + // Not loaded, test mode - load the image synchronously + (_, true) => { + let (sync_tx, sync_rx) = ipc::channel().unwrap(); + self.shared.image_cache_thread.request_image(url, + ImageCacheChan(sync_tx), + None); + match sync_rx.recv().unwrap().image_response { + ImageResponse::Loaded(image) | + ImageResponse::PlaceholderLoaded(image) => + Some(ImageOrMetadataAvailable::ImageAvailable(image)), + ImageResponse::None | ImageResponse::MetadataLoaded(_) => + None, + } + } + // Not yet requested, async mode - request image or metadata from the cache + (ImageState::NotRequested, false) => { + let sender = self.shared.image_cache_sender.lock().unwrap().clone(); + self.shared.image_cache_thread.request_image_and_metadata(url, sender, None); + None + } + // Image has been requested, is still pending. Return no image + // for this paint loop. When the image loads it will trigger + // a reflow and/or repaint. + (ImageState::Pending, false) => None, + } + } + } + } + } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 9fa563ce20d..45ffac1c659 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -25,8 +25,8 @@ use ipc_channel::ipc::IpcSender; use layout_debug; use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified}; use msg::constellation_msg::PipelineId; -use net_traits::image::base::Image; -use net_traits::image_cache_thread::UsePlaceholder; +use net_traits::image::base::{Image, ImageMetadata}; +use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; use rustc_serialize::{Encodable, Encoder}; use script::dom::htmlcanvaselement::HTMLCanvasData; use std::borrow::ToOwned; @@ -344,6 +344,7 @@ pub struct ImageFragmentInfo { /// The image held within this fragment. pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub image: Option<Arc<Image>>, + pub metadata: Option<ImageMetadata>, } impl ImageFragmentInfo { @@ -361,26 +362,39 @@ impl ImageFragmentInfo { .map(Au::from_px) } - let image = url.and_then(|url| { - layout_context.get_or_request_image(url, UsePlaceholder::Yes) + let image_or_metadata = url.and_then(|url| { + layout_context.get_or_request_image_or_meta(url, UsePlaceholder::Yes) }); + let (image, metadata) = match image_or_metadata { + Some(ImageOrMetadataAvailable::ImageAvailable(i)) => { + (Some(i.clone()), Some(ImageMetadata { height: i.height, width: i.width } )) + } + Some(ImageOrMetadataAvailable::MetadataAvailable(m)) => { + (None, Some(m)) + } + None => { + (None, None) + } + }; + ImageFragmentInfo { replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, convert_length(node, &atom!("width")), convert_length(node, &atom!("height"))), image: image, + metadata: metadata, } } /// Returns the original inline-size of the image. pub fn image_inline_size(&mut self) -> Au { - match self.image { - Some(ref image) => { + match self.metadata { + Some(ref metadata) => { Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical { - image.height + metadata.height } else { - image.width + metadata.width } as i32) } None => Au(0) @@ -389,12 +403,12 @@ impl ImageFragmentInfo { /// Returns the original block-size of the image. pub fn image_block_size(&mut self) -> Au { - match self.image { - Some(ref image) => { + match self.metadata { + Some(ref metadata) => { Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical { - image.width + metadata.width } else { - image.height + metadata.height } as i32) } None => Au(0) diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index ba971992a93..2ce14039a15 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -216,6 +216,12 @@ pub enum PixelFormat { RGBA8, // RGB + alpha, 8 bits per channel } +#[derive(Clone, Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)] +pub struct ImageMetadata { + pub width: u32, + pub height: u32, +} + #[derive(Deserialize, Serialize, HeapSizeOf)] pub struct Image { pub width: u32, diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 3381bc2a131..608cbee0791 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -40,3 +40,4 @@ flate2 = "0.2.0" uuid = "0.1.16" url = "0.5.2" websocket = "0.14.0" +immeta = "0.2" diff --git a/components/net/image_cache_thread.rs b/components/net/image_cache_thread.rs index 98075ab3443..e7d960068e0 100644 --- a/components/net/image_cache_thread.rs +++ b/components/net/image_cache_thread.rs @@ -2,12 +2,13 @@ * 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 immeta::load_from_buf; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; -use net_traits::image::base::{Image, load_from_memory}; +use net_traits::image::base::{Image, ImageMetadata, load_from_memory}; use net_traits::image_cache_thread::ImageResponder; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState}; -use net_traits::image_cache_thread::{ImageCacheResult, ImageResponse, UsePlaceholder}; +use net_traits::image_cache_thread::{ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, UsePlaceholder}; use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadData, ResourceThread}; use net_traits::{ResponseAction, LoadContext}; use std::borrow::ToOwned; @@ -38,6 +39,9 @@ struct PendingLoad { // is complete and the buffer has been transmitted to the decoder. bytes: Vec<u8>, + // Image metadata, if available. + metadata: Option<ImageMetadata>, + // Once loading is complete, the result of the operation. result: Option<Result<(), String>>, listeners: Vec<ImageListener>, @@ -51,6 +55,7 @@ impl PendingLoad { fn new(url: Arc<Url>) -> PendingLoad { PendingLoad { bytes: vec!(), + metadata: None, result: None, listeners: vec!(), url: url, @@ -165,6 +170,7 @@ impl CompletedLoad { struct ImageListener { sender: ImageCacheChan, responder: Option<ImageResponder>, + send_metadata_msg: bool, } // A key used to communicate during loading. @@ -188,17 +194,24 @@ impl LoadKeyGenerator { } impl ImageListener { - fn new(sender: ImageCacheChan, responder: Option<ImageResponder>) -> ImageListener { + fn new(sender: ImageCacheChan, responder: Option<ImageResponder>, send_metadata_msg: bool) -> ImageListener { ImageListener { sender: sender, responder: responder, + send_metadata_msg: send_metadata_msg, } } - fn notify(self, image_response: ImageResponse) { + fn notify(&self, image_response: ImageResponse) { + if !self.send_metadata_msg { + if let ImageResponse::MetadataLoaded(_) = image_response { + return; + } + } + let ImageCacheChan(ref sender) = self.sender; let msg = ImageCacheResult { - responder: self.responder, + responder: self.responder.clone(), image_response: image_response, }; sender.send(msg).ok(); @@ -310,27 +323,17 @@ impl ImageCache { return Some(sender); } ImageCacheCommand::RequestImage(url, result_chan, responder) => { - self.request_image(url, result_chan, responder); + self.request_image(url, result_chan, responder, false); + } + ImageCacheCommand::RequestImageAndMetadata(url, result_chan, responder) => { + self.request_image(url, result_chan, responder, true); } ImageCacheCommand::GetImageIfAvailable(url, use_placeholder, consumer) => { - let result = match self.completed_loads.get(&url) { - Some(completed_load) => { - match (completed_load.image_response.clone(), use_placeholder) { - (ImageResponse::Loaded(image), _) | - (ImageResponse::PlaceholderLoaded(image), UsePlaceholder::Yes) => { - Ok(image) - } - (ImageResponse::PlaceholderLoaded(_), UsePlaceholder::No) | - (ImageResponse::None, _) => { - Err(ImageState::LoadError) - } - } - } - None => { - self.pending_loads.get_by_url(&url). - map_or(Err(ImageState::NotRequested), |_| Err(ImageState::Pending)) - } - }; + let result = self.get_image_if_available(url, use_placeholder); + consumer.send(result).unwrap(); + } + ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, consumer) => { + let result = self.get_image_or_meta_if_available(url, use_placeholder); consumer.send(result).unwrap(); } }; @@ -345,13 +348,24 @@ impl ImageCache { (ResponseAction::DataAvailable(data), _) => { 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) { + let dimensions = metadata.dimensions(); + let img_metadata = ImageMetadata { width: dimensions.width, + height: dimensions.height }; + pending_load.metadata = Some(img_metadata.clone()); + for listener in &pending_load.listeners { + listener.notify(ImageResponse::MetadataLoaded(img_metadata.clone()).clone()); + } + } + } } (ResponseAction::ResponseComplete(result), key) => { match result { Ok(()) => { let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap(); pending_load.result = Some(result); - let bytes = mem::replace(&mut pending_load.bytes, vec!()); let sender = self.decoder_sender.clone(); @@ -401,12 +415,15 @@ impl ImageCache { // Request an image from the cache. If the image hasn't been // loaded/decoded yet, it will be loaded/decoded in the - // background. + // background. If send_metadata_msg is set, the channel will be notified + // that image metadata is available, possibly before the image has finished + // loading. fn request_image(&mut self, url: Url, result_chan: ImageCacheChan, - responder: Option<ImageResponder>) { - let image_listener = ImageListener::new(result_chan, responder); + responder: Option<ImageResponder>, + send_metadata_msg: bool) { + let image_listener = ImageListener::new(result_chan, responder, send_metadata_msg); // Let's avoid copying url everywhere. let ref_url = Arc::new(url); @@ -449,6 +466,47 @@ impl ImageCache { } } } + + fn get_image_if_available(&mut self, + url: Url, + placeholder: UsePlaceholder, ) + -> Result<Arc<Image>, ImageState> { + let img_or_metadata = self.get_image_or_meta_if_available(url, placeholder); + match img_or_metadata { + Ok(ImageOrMetadataAvailable::ImageAvailable(image)) => Ok(image), + Ok(ImageOrMetadataAvailable::MetadataAvailable(_)) => Err(ImageState::Pending), + Err(err) => Err(err), + } + } + + fn get_image_or_meta_if_available(&mut self, + url: Url, + placeholder: UsePlaceholder) + -> Result<ImageOrMetadataAvailable, ImageState> { + match self.completed_loads.get(&url) { + Some(completed_load) => { + match (completed_load.image_response.clone(), placeholder) { + (ImageResponse::Loaded(image), _) | + (ImageResponse::PlaceholderLoaded(image), UsePlaceholder::Yes) => { + Ok(ImageOrMetadataAvailable::ImageAvailable(image)) + } + (ImageResponse::PlaceholderLoaded(_), UsePlaceholder::No) | + (ImageResponse::None, _) | + (ImageResponse::MetadataLoaded(_), _) => { + Err(ImageState::LoadError) + } + } + } + None => { + self.pending_loads.get_by_url(&url).as_ref(). + map_or(Err(ImageState::NotRequested), |pl| pl.metadata.as_ref(). + map_or(Err(ImageState::Pending), |meta| + Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone())) + ) + ) + } + } + } } /// Create a new image cache. diff --git a/components/net/lib.rs b/components/net/lib.rs index 0c434475177..143672962cb 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -14,6 +14,7 @@ extern crate cookie as cookie_rs; extern crate devtools_traits; extern crate flate2; extern crate hyper; +extern crate immeta; extern crate ipc_channel; #[macro_use] extern crate log; diff --git a/components/net_traits/image/base.rs b/components/net_traits/image/base.rs index 65fc9eeaa15..7faf439e34b 100644 --- a/components/net_traits/image/base.rs +++ b/components/net_traits/image/base.rs @@ -7,7 +7,7 @@ use piston_image::{self, DynamicImage, GenericImage, ImageFormat}; use stb_image::image as stb_image2; use util::vec::byte_swap; -pub use msg::constellation_msg::{Image, PixelFormat}; +pub use msg::constellation_msg::{Image, ImageMetadata, PixelFormat}; // FIXME: Images must not be copied every frame. Instead we should atomically // reference count them. diff --git a/components/net_traits/image_cache_thread.rs b/components/net_traits/image_cache_thread.rs index 85ef01be4f6..2381efbb997 100644 --- a/components/net_traits/image_cache_thread.rs +++ b/components/net_traits/image_cache_thread.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use ipc_channel::ipc::{self, IpcSender}; -use msg::constellation_msg::Image; +use msg::constellation_msg::{Image, ImageMetadata}; use std::sync::Arc; use url::Url; use util::mem::HeapSizeOf; @@ -12,7 +12,7 @@ use util::mem::HeapSizeOf; /// 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(Deserialize, Serialize)] +#[derive(Clone, Deserialize, Serialize)] pub struct ImageResponder { sender: IpcSender<ImageResponse>, } @@ -42,13 +42,22 @@ pub enum ImageState { 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 } -/// Channel for sending commands to the image cache. +/// Indicating either entire image or just metadata availability +#[derive(Clone, Deserialize, Serialize, HeapSizeOf)] +pub enum ImageOrMetadataAvailable { + ImageAvailable(Arc<Image>), + MetadataAvailable(ImageMetadata), +} + +/// Channel used by the image cache to send results. #[derive(Clone, Deserialize, Serialize)] pub struct ImageCacheChan(pub IpcSender<ImageCacheResult>); @@ -68,12 +77,22 @@ pub enum ImageCacheCommand { /// that is passed to the result channel. RequestImage(Url, ImageCacheChan, Option<ImageResponder>), + /// Requests an image and a "metadata-ready" notification message asynchronously from the + /// cache. The cache will make an effort to send metadata before the image is completely + /// loaded. Supply a channel to receive the results, and optionally an image responder + /// that is passed to the result channel. + RequestImageAndMetadata(Url, ImageCacheChan, Option<ImageResponder>), + /// Synchronously check the state of an image in the cache. /// TODO(gw): Profile this on some real world sites and see /// if it's worth caching the results of this locally in each /// layout / paint thread. GetImageIfAvailable(Url, UsePlaceholder, IpcSender<Result<Arc<Image>, ImageState>>), + /// 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(Url, UsePlaceholder, IpcSender<Result<ImageOrMetadataAvailable, ImageState>>), + /// Clients must wait for a response before shutting down the ResourceThread Exit(IpcSender<()>), } @@ -101,7 +120,7 @@ impl ImageCacheThread { } } - /// Asynchronously request and image. See ImageCacheCommand::RequestImage. + /// Asynchronously request an image. See ImageCacheCommand::RequestImage. pub fn request_image(&self, url: Url, result_chan: ImageCacheChan, @@ -110,6 +129,16 @@ impl ImageCacheThread { self.chan.send(msg).unwrap(); } + /// Asynchronously request an image and metadata. + /// See ImageCacheCommand::RequestImageAndMetadata + pub fn request_image_and_metadata(&self, + url: Url, + result_chan: ImageCacheChan, + responder: Option<ImageResponder>) { + let msg = ImageCacheCommand::RequestImageAndMetadata(url, result_chan, responder); + self.chan.send(msg).unwrap(); + } + /// Get the current state of an image. See ImageCacheCommand::GetImageIfAvailable. pub fn find_image(&self, url: Url, use_placeholder: UsePlaceholder) -> Result<Arc<Image>, ImageState> { @@ -119,6 +148,16 @@ impl ImageCacheThread { receiver.recv().unwrap() } + /// Get the current state of an image, returning its metadata if available. + /// See ImageCacheCommand::GetImageOrMetadataIfAvailable. + pub fn find_image_or_metadata(&self, url: Url, use_placeholder: UsePlaceholder) + -> Result<ImageOrMetadataAvailable, ImageState> { + let (sender, receiver) = ipc::channel().unwrap(); + let msg = ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, sender); + self.chan.send(msg).unwrap(); + receiver.recv().unwrap() + } + /// Shutdown the image cache thread. pub fn exit(&self) { let (response_chan, response_port) = ipc::channel().unwrap(); diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 31dcc99daaa..dec38c26565 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -58,7 +58,7 @@ use libc; use msg::constellation_msg::ConstellationChan; use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData}; use net_traits::Metadata; -use net_traits::image::base::Image; +use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::storage_thread::StorageType; use profile_traits::mem::ProfilerChan as MemProfilerChan; @@ -265,7 +265,7 @@ no_jsmanaged_fields!(Receiver<T>); no_jsmanaged_fields!(Rect<T>); no_jsmanaged_fields!(Size2D<T>); no_jsmanaged_fields!(Arc<T>); -no_jsmanaged_fields!(Image, ImageCacheChan, ImageCacheThread); +no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheChan, ImageCacheThread); no_jsmanaged_fields!(Metadata); no_jsmanaged_fields!(Atom, Namespace, QualName); no_jsmanaged_fields!(Trusted<T: Reflectable>); diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 346ecb9680d..f4f1e2c51a7 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -444,7 +444,7 @@ impl CanvasRenderingContext2D { let img = match self.request_image_from_cache(url) { ImageResponse::Loaded(img) => img, - ImageResponse::PlaceholderLoaded(_) | ImageResponse::None => { + ImageResponse::PlaceholderLoaded(_) | ImageResponse::None | ImageResponse::MetadataLoaded(_) => { return None; } }; diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 6474d360a75..a6b124939f0 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -21,7 +21,7 @@ use dom::node::{Node, NodeDamage, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; use ipc_channel::ipc; use ipc_channel::router::ROUTER; -use net_traits::image::base::Image; +use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageResponder, ImageResponse}; use script_thread::ScriptThreadEventCategory::UpdateReplacedElement; use script_thread::{CommonScriptMsg, Runnable, ScriptChan}; @@ -35,6 +35,7 @@ pub struct HTMLImageElement { htmlelement: HTMLElement, url: DOMRefCell<Option<Url>>, image: DOMRefCell<Option<Arc<Image>>>, + metadata: DOMRefCell<Option<ImageMetadata>>, } impl HTMLImageElement { @@ -64,12 +65,17 @@ impl Runnable for ImageResponseHandlerRunnable { // Update the image field let element = self.element.root(); let element_ref = element.r(); - *element_ref.image.borrow_mut() = match self.image { + let (image, metadata, trigger_image_load) = match self.image { ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => { - Some(image) + (Some(image.clone()), Some(ImageMetadata { height: image.height, width: image.width } ), true) } - ImageResponse::None => None, + ImageResponse::MetadataLoaded(meta) => { + (None, Some(meta), false) + } + ImageResponse::None => (None, None, true) }; + *element_ref.image.borrow_mut() = image; + *element_ref.metadata.borrow_mut() = metadata; // Mark the node dirty let document = document_from_node(&*element); @@ -77,7 +83,9 @@ impl Runnable for ImageResponseHandlerRunnable { // Fire image.onload let window = window_from_node(document.r()); - element.upcast::<EventTarget>().fire_simple_event("load", GlobalRef::Window(window.r())); + if trigger_image_load { + element.upcast::<EventTarget>().fire_simple_event("load", GlobalRef::Window(window.r())); + } // Trigger reflow window.add_pending_reflow(); } @@ -116,7 +124,7 @@ impl HTMLImageElement { UpdateReplacedElement, runnable)); }); - image_cache.request_image(img_url, + image_cache.request_image_and_metadata(img_url, window.image_cache_chan(), Some(ImageResponder::new(responder_sender))); } @@ -128,6 +136,7 @@ impl HTMLImageElement { htmlelement: HTMLElement::new_inherited(localName, prefix, document), url: DOMRefCell::new(None), image: DOMRefCell::new(None), + metadata: DOMRefCell::new(None), } } @@ -218,20 +227,20 @@ impl HTMLImageElementMethods for HTMLImageElement { // https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth fn NaturalWidth(&self) -> u32 { - let image = self.image.borrow(); + let metadata = self.metadata.borrow(); - match *image { - Some(ref image) => image.width, + match *metadata { + Some(ref metadata) => metadata.width, None => 0, } } // https://html.spec.whatwg.org/multipage/#dom-img-naturalheight fn NaturalHeight(&self) -> u32 { - let image = self.image.borrow(); + let metadata = self.metadata.borrow(); - match *image { - Some(ref image) => image.height, + match *metadata { + Some(ref metadata) => metadata.height, None => 0, } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 5d6199664b7..51d9f3a12c8 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -1138,7 +1138,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { let img = match canvas_utils::request_image_from_cache(window.r(), img_url) { ImageResponse::Loaded(img) => img, - ImageResponse::PlaceholderLoaded(_) | ImageResponse::None + ImageResponse::PlaceholderLoaded(_) | ImageResponse::None | + ImageResponse::MetadataLoaded(_) => return, }; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index c4f1d112d71..57069e9b9ea 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -854,6 +854,15 @@ dependencies = [ ] [[package]] +name = "immeta" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "inflate" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1151,6 +1160,7 @@ dependencies = [ "devtools_traits 0.0.1", "flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "immeta 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", "log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 49fa2d65ec1..6e3ab36d065 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -813,6 +813,15 @@ dependencies = [ ] [[package]] +name = "immeta" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "inflate" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1110,6 +1119,7 @@ dependencies = [ "devtools_traits 0.0.1", "flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "immeta 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", "log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 19df79d24a7..927004350de 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -784,6 +784,15 @@ dependencies = [ ] [[package]] +name = "immeta" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "inflate" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1081,6 +1090,7 @@ dependencies = [ "devtools_traits 0.0.1", "flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "immeta 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", "log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", |