mirror of
https://github.com/servo/servo.git
synced 2025-06-20 07:08:59 +01:00
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 -->
This commit is contained in:
commit
46b3eb6535
15 changed files with 269 additions and 63 deletions
|
@ -17,7 +17,7 @@ use gfx_traits::LayerId;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState};
|
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::cell::{RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_state::DefaultState;
|
use std::collections::hash_state::DefaultState;
|
||||||
|
@ -155,7 +155,7 @@ impl<'a> LayoutContext<'a> {
|
||||||
match sync_rx.recv().unwrap().image_response {
|
match sync_rx.recv().unwrap().image_response {
|
||||||
ImageResponse::Loaded(image) |
|
ImageResponse::Loaded(image) |
|
||||||
ImageResponse::PlaceholderLoaded(image) => Some(image),
|
ImageResponse::PlaceholderLoaded(image) => Some(image),
|
||||||
ImageResponse::None => None,
|
ImageResponse::None | ImageResponse::MetadataLoaded(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Not yet requested, async mode - request image from the cache
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ use ipc_channel::ipc::IpcSender;
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified};
|
use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified};
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache_thread::UsePlaceholder;
|
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
|
||||||
use rustc_serialize::{Encodable, Encoder};
|
use rustc_serialize::{Encodable, Encoder};
|
||||||
use script::dom::htmlcanvaselement::HTMLCanvasData;
|
use script::dom::htmlcanvaselement::HTMLCanvasData;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -344,6 +344,7 @@ pub struct ImageFragmentInfo {
|
||||||
/// The image held within this fragment.
|
/// The image held within this fragment.
|
||||||
pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
|
pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
|
||||||
pub image: Option<Arc<Image>>,
|
pub image: Option<Arc<Image>>,
|
||||||
|
pub metadata: Option<ImageMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageFragmentInfo {
|
impl ImageFragmentInfo {
|
||||||
|
@ -361,26 +362,39 @@ impl ImageFragmentInfo {
|
||||||
.map(Au::from_px)
|
.map(Au::from_px)
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = url.and_then(|url| {
|
let image_or_metadata = url.and_then(|url| {
|
||||||
layout_context.get_or_request_image(url, UsePlaceholder::Yes)
|
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 {
|
ImageFragmentInfo {
|
||||||
replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node,
|
replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node,
|
||||||
convert_length(node, &atom!("width")),
|
convert_length(node, &atom!("width")),
|
||||||
convert_length(node, &atom!("height"))),
|
convert_length(node, &atom!("height"))),
|
||||||
image: image,
|
image: image,
|
||||||
|
metadata: metadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the original inline-size of the image.
|
/// Returns the original inline-size of the image.
|
||||||
pub fn image_inline_size(&mut self) -> Au {
|
pub fn image_inline_size(&mut self) -> Au {
|
||||||
match self.image {
|
match self.metadata {
|
||||||
Some(ref image) => {
|
Some(ref metadata) => {
|
||||||
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
||||||
image.height
|
metadata.height
|
||||||
} else {
|
} else {
|
||||||
image.width
|
metadata.width
|
||||||
} as i32)
|
} as i32)
|
||||||
}
|
}
|
||||||
None => Au(0)
|
None => Au(0)
|
||||||
|
@ -389,12 +403,12 @@ impl ImageFragmentInfo {
|
||||||
|
|
||||||
/// Returns the original block-size of the image.
|
/// Returns the original block-size of the image.
|
||||||
pub fn image_block_size(&mut self) -> Au {
|
pub fn image_block_size(&mut self) -> Au {
|
||||||
match self.image {
|
match self.metadata {
|
||||||
Some(ref image) => {
|
Some(ref metadata) => {
|
||||||
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical {
|
||||||
image.width
|
metadata.width
|
||||||
} else {
|
} else {
|
||||||
image.height
|
metadata.height
|
||||||
} as i32)
|
} as i32)
|
||||||
}
|
}
|
||||||
None => Au(0)
|
None => Au(0)
|
||||||
|
|
|
@ -216,6 +216,12 @@ pub enum PixelFormat {
|
||||||
RGBA8, // RGB + alpha, 8 bits per channel
|
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)]
|
#[derive(Deserialize, Serialize, HeapSizeOf)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
|
|
|
@ -40,3 +40,4 @@ flate2 = "0.2.0"
|
||||||
uuid = "0.1.16"
|
uuid = "0.1.16"
|
||||||
url = "0.5.2"
|
url = "0.5.2"
|
||||||
websocket = "0.14.0"
|
websocket = "0.14.0"
|
||||||
|
immeta = "0.2"
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
* 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 immeta::load_from_buf;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
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::ImageResponder;
|
||||||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState};
|
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::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadData, ResourceThread};
|
||||||
use net_traits::{ResponseAction, LoadContext};
|
use net_traits::{ResponseAction, LoadContext};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -38,6 +39,9 @@ struct PendingLoad {
|
||||||
// is complete and the buffer has been transmitted to the decoder.
|
// is complete and the buffer has been transmitted to the decoder.
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
|
|
||||||
|
// Image metadata, if available.
|
||||||
|
metadata: Option<ImageMetadata>,
|
||||||
|
|
||||||
// Once loading is complete, the result of the operation.
|
// Once loading is complete, the result of the operation.
|
||||||
result: Option<Result<(), String>>,
|
result: Option<Result<(), String>>,
|
||||||
listeners: Vec<ImageListener>,
|
listeners: Vec<ImageListener>,
|
||||||
|
@ -51,6 +55,7 @@ impl PendingLoad {
|
||||||
fn new(url: Arc<Url>) -> PendingLoad {
|
fn new(url: Arc<Url>) -> PendingLoad {
|
||||||
PendingLoad {
|
PendingLoad {
|
||||||
bytes: vec!(),
|
bytes: vec!(),
|
||||||
|
metadata: None,
|
||||||
result: None,
|
result: None,
|
||||||
listeners: vec!(),
|
listeners: vec!(),
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -165,6 +170,7 @@ impl CompletedLoad {
|
||||||
struct ImageListener {
|
struct ImageListener {
|
||||||
sender: ImageCacheChan,
|
sender: ImageCacheChan,
|
||||||
responder: Option<ImageResponder>,
|
responder: Option<ImageResponder>,
|
||||||
|
send_metadata_msg: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A key used to communicate during loading.
|
// A key used to communicate during loading.
|
||||||
|
@ -188,17 +194,24 @@ impl LoadKeyGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageListener {
|
impl ImageListener {
|
||||||
fn new(sender: ImageCacheChan, responder: Option<ImageResponder>) -> ImageListener {
|
fn new(sender: ImageCacheChan, responder: Option<ImageResponder>, send_metadata_msg: bool) -> ImageListener {
|
||||||
ImageListener {
|
ImageListener {
|
||||||
sender: sender,
|
sender: sender,
|
||||||
responder: responder,
|
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 ImageCacheChan(ref sender) = self.sender;
|
||||||
let msg = ImageCacheResult {
|
let msg = ImageCacheResult {
|
||||||
responder: self.responder,
|
responder: self.responder.clone(),
|
||||||
image_response: image_response,
|
image_response: image_response,
|
||||||
};
|
};
|
||||||
sender.send(msg).ok();
|
sender.send(msg).ok();
|
||||||
|
@ -310,27 +323,17 @@ impl ImageCache {
|
||||||
return Some(sender);
|
return Some(sender);
|
||||||
}
|
}
|
||||||
ImageCacheCommand::RequestImage(url, result_chan, responder) => {
|
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) => {
|
ImageCacheCommand::GetImageIfAvailable(url, use_placeholder, consumer) => {
|
||||||
let result = match self.completed_loads.get(&url) {
|
let result = self.get_image_if_available(url, use_placeholder);
|
||||||
Some(completed_load) => {
|
consumer.send(result).unwrap();
|
||||||
match (completed_load.image_response.clone(), use_placeholder) {
|
}
|
||||||
(ImageResponse::Loaded(image), _) |
|
ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, consumer) => {
|
||||||
(ImageResponse::PlaceholderLoaded(image), UsePlaceholder::Yes) => {
|
let result = self.get_image_or_meta_if_available(url, use_placeholder);
|
||||||
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))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
consumer.send(result).unwrap();
|
consumer.send(result).unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -345,13 +348,24 @@ impl ImageCache {
|
||||||
(ResponseAction::DataAvailable(data), _) => {
|
(ResponseAction::DataAvailable(data), _) => {
|
||||||
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
||||||
pending_load.bytes.extend_from_slice(&data);
|
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) => {
|
(ResponseAction::ResponseComplete(result), key) => {
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
||||||
pending_load.result = Some(result);
|
pending_load.result = Some(result);
|
||||||
|
|
||||||
let bytes = mem::replace(&mut pending_load.bytes, vec!());
|
let bytes = mem::replace(&mut pending_load.bytes, vec!());
|
||||||
let sender = self.decoder_sender.clone();
|
let sender = self.decoder_sender.clone();
|
||||||
|
|
||||||
|
@ -401,12 +415,15 @@ impl ImageCache {
|
||||||
|
|
||||||
// Request an image from the cache. If the image hasn't been
|
// Request an image from the cache. If the image hasn't been
|
||||||
// loaded/decoded yet, it will be loaded/decoded in the
|
// 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,
|
fn request_image(&mut self,
|
||||||
url: Url,
|
url: Url,
|
||||||
result_chan: ImageCacheChan,
|
result_chan: ImageCacheChan,
|
||||||
responder: Option<ImageResponder>) {
|
responder: Option<ImageResponder>,
|
||||||
let image_listener = ImageListener::new(result_chan, responder);
|
send_metadata_msg: bool) {
|
||||||
|
let image_listener = ImageListener::new(result_chan, responder, send_metadata_msg);
|
||||||
// Let's avoid copying url everywhere.
|
// Let's avoid copying url everywhere.
|
||||||
let ref_url = Arc::new(url);
|
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.
|
/// Create a new image cache.
|
||||||
|
|
|
@ -14,6 +14,7 @@ extern crate cookie as cookie_rs;
|
||||||
extern crate devtools_traits;
|
extern crate devtools_traits;
|
||||||
extern crate flate2;
|
extern crate flate2;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate immeta;
|
||||||
extern crate ipc_channel;
|
extern crate ipc_channel;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use piston_image::{self, DynamicImage, GenericImage, ImageFormat};
|
||||||
use stb_image::image as stb_image2;
|
use stb_image::image as stb_image2;
|
||||||
use util::vec::byte_swap;
|
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
|
// FIXME: Images must not be copied every frame. Instead we should atomically
|
||||||
// reference count them.
|
// reference count them.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* 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 ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use msg::constellation_msg::Image;
|
use msg::constellation_msg::{Image, ImageMetadata};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::mem::HeapSizeOf;
|
use util::mem::HeapSizeOf;
|
||||||
|
@ -12,7 +12,7 @@ use util::mem::HeapSizeOf;
|
||||||
/// and image, and returned to the specified event loop when the
|
/// and image, and returned to the specified event loop when the
|
||||||
/// image load completes. It is typically used to trigger a reflow
|
/// image load completes. It is typically used to trigger a reflow
|
||||||
/// and/or repaint.
|
/// and/or repaint.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct ImageResponder {
|
pub struct ImageResponder {
|
||||||
sender: IpcSender<ImageResponse>,
|
sender: IpcSender<ImageResponse>,
|
||||||
}
|
}
|
||||||
|
@ -42,13 +42,22 @@ pub enum ImageState {
|
||||||
pub enum ImageResponse {
|
pub enum ImageResponse {
|
||||||
/// The requested image was loaded.
|
/// The requested image was loaded.
|
||||||
Loaded(Arc<Image>),
|
Loaded(Arc<Image>),
|
||||||
|
/// The request image metadata was loaded.
|
||||||
|
MetadataLoaded(ImageMetadata),
|
||||||
/// The requested image failed to load, so a placeholder was loaded instead.
|
/// The requested image failed to load, so a placeholder was loaded instead.
|
||||||
PlaceholderLoaded(Arc<Image>),
|
PlaceholderLoaded(Arc<Image>),
|
||||||
/// Neither the requested image nor the placeholder could be loaded.
|
/// Neither the requested image nor the placeholder could be loaded.
|
||||||
None
|
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)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct ImageCacheChan(pub IpcSender<ImageCacheResult>);
|
pub struct ImageCacheChan(pub IpcSender<ImageCacheResult>);
|
||||||
|
|
||||||
|
@ -68,12 +77,22 @@ pub enum ImageCacheCommand {
|
||||||
/// that is passed to the result channel.
|
/// that is passed to the result channel.
|
||||||
RequestImage(Url, ImageCacheChan, Option<ImageResponder>),
|
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.
|
/// Synchronously check the state of an image in the cache.
|
||||||
/// TODO(gw): Profile this on some real world sites and see
|
/// TODO(gw): Profile this on some real world sites and see
|
||||||
/// if it's worth caching the results of this locally in each
|
/// if it's worth caching the results of this locally in each
|
||||||
/// layout / paint thread.
|
/// layout / paint thread.
|
||||||
GetImageIfAvailable(Url, UsePlaceholder, IpcSender<Result<Arc<Image>, ImageState>>),
|
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
|
/// Clients must wait for a response before shutting down the ResourceThread
|
||||||
Exit(IpcSender<()>),
|
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,
|
pub fn request_image(&self,
|
||||||
url: Url,
|
url: Url,
|
||||||
result_chan: ImageCacheChan,
|
result_chan: ImageCacheChan,
|
||||||
|
@ -110,6 +129,16 @@ impl ImageCacheThread {
|
||||||
self.chan.send(msg).unwrap();
|
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.
|
/// Get the current state of an image. See ImageCacheCommand::GetImageIfAvailable.
|
||||||
pub fn find_image(&self, url: Url, use_placeholder: UsePlaceholder)
|
pub fn find_image(&self, url: Url, use_placeholder: UsePlaceholder)
|
||||||
-> Result<Arc<Image>, ImageState> {
|
-> Result<Arc<Image>, ImageState> {
|
||||||
|
@ -119,6 +148,16 @@ impl ImageCacheThread {
|
||||||
receiver.recv().unwrap()
|
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.
|
/// Shutdown the image cache thread.
|
||||||
pub fn exit(&self) {
|
pub fn exit(&self) {
|
||||||
let (response_chan, response_port) = ipc::channel().unwrap();
|
let (response_chan, response_port) = ipc::channel().unwrap();
|
||||||
|
|
|
@ -58,7 +58,7 @@ use libc;
|
||||||
use msg::constellation_msg::ConstellationChan;
|
use msg::constellation_msg::ConstellationChan;
|
||||||
use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData};
|
use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData};
|
||||||
use net_traits::Metadata;
|
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::image_cache_thread::{ImageCacheChan, ImageCacheThread};
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||||
|
@ -265,7 +265,7 @@ no_jsmanaged_fields!(Receiver<T>);
|
||||||
no_jsmanaged_fields!(Rect<T>);
|
no_jsmanaged_fields!(Rect<T>);
|
||||||
no_jsmanaged_fields!(Size2D<T>);
|
no_jsmanaged_fields!(Size2D<T>);
|
||||||
no_jsmanaged_fields!(Arc<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!(Metadata);
|
||||||
no_jsmanaged_fields!(Atom, Namespace, QualName);
|
no_jsmanaged_fields!(Atom, Namespace, QualName);
|
||||||
no_jsmanaged_fields!(Trusted<T: Reflectable>);
|
no_jsmanaged_fields!(Trusted<T: Reflectable>);
|
||||||
|
|
|
@ -444,7 +444,7 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
let img = match self.request_image_from_cache(url) {
|
let img = match self.request_image_from_cache(url) {
|
||||||
ImageResponse::Loaded(img) => img,
|
ImageResponse::Loaded(img) => img,
|
||||||
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None => {
|
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None | ImageResponse::MetadataLoaded(_) => {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@ use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
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 net_traits::image_cache_thread::{ImageResponder, ImageResponse};
|
||||||
use script_thread::ScriptThreadEventCategory::UpdateReplacedElement;
|
use script_thread::ScriptThreadEventCategory::UpdateReplacedElement;
|
||||||
use script_thread::{CommonScriptMsg, Runnable, ScriptChan};
|
use script_thread::{CommonScriptMsg, Runnable, ScriptChan};
|
||||||
|
@ -35,6 +35,7 @@ pub struct HTMLImageElement {
|
||||||
htmlelement: HTMLElement,
|
htmlelement: HTMLElement,
|
||||||
url: DOMRefCell<Option<Url>>,
|
url: DOMRefCell<Option<Url>>,
|
||||||
image: DOMRefCell<Option<Arc<Image>>>,
|
image: DOMRefCell<Option<Arc<Image>>>,
|
||||||
|
metadata: DOMRefCell<Option<ImageMetadata>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HTMLImageElement {
|
impl HTMLImageElement {
|
||||||
|
@ -64,12 +65,17 @@ impl Runnable for ImageResponseHandlerRunnable {
|
||||||
// Update the image field
|
// Update the image field
|
||||||
let element = self.element.root();
|
let element = self.element.root();
|
||||||
let element_ref = element.r();
|
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) => {
|
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
|
// Mark the node dirty
|
||||||
let document = document_from_node(&*element);
|
let document = document_from_node(&*element);
|
||||||
|
@ -77,7 +83,9 @@ impl Runnable for ImageResponseHandlerRunnable {
|
||||||
|
|
||||||
// Fire image.onload
|
// Fire image.onload
|
||||||
let window = window_from_node(document.r());
|
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
|
// Trigger reflow
|
||||||
window.add_pending_reflow();
|
window.add_pending_reflow();
|
||||||
}
|
}
|
||||||
|
@ -116,7 +124,7 @@ impl HTMLImageElement {
|
||||||
UpdateReplacedElement, runnable));
|
UpdateReplacedElement, runnable));
|
||||||
});
|
});
|
||||||
|
|
||||||
image_cache.request_image(img_url,
|
image_cache.request_image_and_metadata(img_url,
|
||||||
window.image_cache_chan(),
|
window.image_cache_chan(),
|
||||||
Some(ImageResponder::new(responder_sender)));
|
Some(ImageResponder::new(responder_sender)));
|
||||||
}
|
}
|
||||||
|
@ -128,6 +136,7 @@ impl HTMLImageElement {
|
||||||
htmlelement: HTMLElement::new_inherited(localName, prefix, document),
|
htmlelement: HTMLElement::new_inherited(localName, prefix, document),
|
||||||
url: DOMRefCell::new(None),
|
url: DOMRefCell::new(None),
|
||||||
image: 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
|
// https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth
|
||||||
fn NaturalWidth(&self) -> u32 {
|
fn NaturalWidth(&self) -> u32 {
|
||||||
let image = self.image.borrow();
|
let metadata = self.metadata.borrow();
|
||||||
|
|
||||||
match *image {
|
match *metadata {
|
||||||
Some(ref image) => image.width,
|
Some(ref metadata) => metadata.width,
|
||||||
None => 0,
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
|
// https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
|
||||||
fn NaturalHeight(&self) -> u32 {
|
fn NaturalHeight(&self) -> u32 {
|
||||||
let image = self.image.borrow();
|
let metadata = self.metadata.borrow();
|
||||||
|
|
||||||
match *image {
|
match *metadata {
|
||||||
Some(ref image) => image.height,
|
Some(ref metadata) => metadata.height,
|
||||||
None => 0,
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1138,7 +1138,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
|
|
||||||
let img = match canvas_utils::request_image_from_cache(window.r(), img_url) {
|
let img = match canvas_utils::request_image_from_cache(window.r(), img_url) {
|
||||||
ImageResponse::Loaded(img) => img,
|
ImageResponse::Loaded(img) => img,
|
||||||
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None
|
ImageResponse::PlaceholderLoaded(_) | ImageResponse::None |
|
||||||
|
ImageResponse::MetadataLoaded(_)
|
||||||
=> return,
|
=> return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
10
components/servo/Cargo.lock
generated
10
components/servo/Cargo.lock
generated
|
@ -853,6 +853,15 @@ dependencies = [
|
||||||
"png 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"png 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "inflate"
|
name = "inflate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1151,6 +1160,7 @@ dependencies = [
|
||||||
"devtools_traits 0.0.1",
|
"devtools_traits 0.0.1",
|
||||||
"flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"mime_guess 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
10
ports/cef/Cargo.lock
generated
10
ports/cef/Cargo.lock
generated
|
@ -812,6 +812,15 @@ dependencies = [
|
||||||
"png 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"png 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "inflate"
|
name = "inflate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1110,6 +1119,7 @@ dependencies = [
|
||||||
"devtools_traits 0.0.1",
|
"devtools_traits 0.0.1",
|
||||||
"flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"mime_guess 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
10
ports/gonk/Cargo.lock
generated
10
ports/gonk/Cargo.lock
generated
|
@ -783,6 +783,15 @@ dependencies = [
|
||||||
"png 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"png 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "inflate"
|
name = "inflate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1081,6 +1090,7 @@ dependencies = [
|
||||||
"devtools_traits 0.0.1",
|
"devtools_traits 0.0.1",
|
||||||
"flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"mime_guess 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue