diff --git a/src/servo/image/base.rs b/src/servo/image/base.rs index 1bc22afceca..ca51cb57156 100644 --- a/src/servo/image/base.rs +++ b/src/servo/image/base.rs @@ -2,6 +2,7 @@ export Image; export load; export load_from_memory; +export test_image_bin; import stb_image::image::{image, load, load_from_memory}; @@ -9,3 +10,7 @@ import stb_image::image::{image, load, load_from_memory}; // reference count them. type Image = image; + +fn test_image_bin() -> ~[u8] { + #include_bin("test.jpeg") +} \ No newline at end of file diff --git a/src/servo/image/test.jpeg b/src/servo/image/test.jpeg new file mode 100644 index 00000000000..3314a53600b Binary files /dev/null and b/src/servo/image/test.jpeg differ diff --git a/src/servo/resource/image_cache_task.rs b/src/servo/resource/image_cache_task.rs index a130f883fed..f164c5e71cb 100644 --- a/src/servo/resource/image_cache_task.rs +++ b/src/servo/resource/image_cache_task.rs @@ -3,13 +3,15 @@ export ImageResponseMsg, ImageReady, ImageNotReady; export ImageCacheTask; export image_cache_task; -import image::base::{Image, load_from_memory}; +import image::base::{Image, load_from_memory, test_image_bin}; import std::net::url::url; import util::url::{make_url, UrlMap, url_map}; import comm::{chan, port}; import task::spawn_listener; import resource::resource_task; import resource_task::ResourceTask; +import core::arc::arc; +import clone_arc = core::arc::clone; enum Msg { Prefetch(url), @@ -18,7 +20,7 @@ enum Msg { } enum ImageResponseMsg { - ImageReady(Image), + ImageReady(arc<~Image>), ImageNotReady } @@ -29,7 +31,8 @@ fn image_cache_task(resource_task: ResourceTask) -> ImageCacheTask { ImageCache { resource_task: resource_task, from_client: from_client, - prefetch_map: url_map() + prefetch_map: url_map(), + image_map: url_map() }.run(); } } @@ -37,12 +40,13 @@ fn image_cache_task(resource_task: ResourceTask) -> ImageCacheTask { struct ImageCache { resource_task: ResourceTask; from_client: port; - prefetch_map: UrlMap; + prefetch_map: UrlMap<@PrefetchData>; + image_map: UrlMap<@arc<~Image>>; } struct PrefetchData { - response_port: @port; - data: @mut ~[u8]; + response_port: port; + mut data: ~[u8]; } impl ImageCache { @@ -59,18 +63,53 @@ impl ImageCache { let response_port = port(); self.resource_task.send(resource_task::Load(url, response_port.chan())); - let prefetch_data = PrefetchData { - response_port: @response_port, - data: @mut ~[] + let prefetch_data = @PrefetchData { + response_port: response_port, + data: ~[] }; self.prefetch_map.insert(url, prefetch_data); } GetImage(url, response) => { - if self.prefetch_map.contains_key(url) { - response.send(ImageNotReady); - } else { - fail ~"got a request for image data without prefetch"; + match self.prefetch_map.find(url) { + some(prefetch_data) => { + + let mut image_sent = false; + + while prefetch_data.response_port.peek() { + match prefetch_data.response_port.recv() { + resource_task::Payload(data) => { + prefetch_data.data += data; + } + resource_task::Done(result::ok(*)) => { + // We've got the entire image binary + let mut data = ~[]; + data <-> prefetch_data.data; + // FIXME: Need to do this in parallel + let image = @arc(~load_from_memory(data)); + response.send(ImageReady(clone_arc(image))); + self.prefetch_map.remove(url); + self.image_map.insert(url, image); + image_sent = true; + break; + } + resource_task::Done(result::err(*)) => { + fail ~"FIXME: what happens now?" + } + } + } + + if !image_sent { + response.send(ImageNotReady); + } + } + none => { + // FIXME: Probably faster to hit this map before the prefetch map + match self.image_map.find(url) { + some(image) => response.send(ImageReady(clone_arc(image))), + none => fail ~"got a request for image data without prefetch" + } + } } } Exit => break @@ -198,4 +237,90 @@ fn should_return_image_not_ready_if_data_has_not_arrived() { assert response_port.recv() == ImageNotReady; image_cache_task.send(Exit); mock_resource_task.send(resource_task::Exit); -} \ No newline at end of file +} + +#[test] +fn should_return_decoded_image_data_if_data_has_arrived() { + + let image_bin_sent = port(); + let image_bin_sent_chan = image_bin_sent.chan(); + + let mock_resource_task = do spawn_listener |from_client| { + + // infer me + let from_client: port = from_client; + + loop { + match from_client.recv() { + resource_task::Load(_, response) => { + response.send(resource_task::Payload(test_image_bin())); + response.send(resource_task::Done(result::ok(()))); + image_bin_sent_chan.send(()); + } + resource_task::Exit => break + } + } + }; + + let image_cache_task = image_cache_task(mock_resource_task); + let url = make_url(~"file", none); + + image_cache_task.send(Prefetch(url)); + + // Wait until our mock resource task has sent the image to the image cache + image_bin_sent.recv(); + + let response_port = port(); + image_cache_task.send(GetImage(url, response_port.chan())); + match response_port.recv() { + ImageReady(_) => (), + _ => fail + } + + image_cache_task.send(Exit); + mock_resource_task.send(resource_task::Exit); +} + +#[test] +fn should_return_decoded_image_data_for_multiple_requests() { + + let image_bin_sent = port(); + let image_bin_sent_chan = image_bin_sent.chan(); + + let mock_resource_task = do spawn_listener |from_client| { + + // infer me + let from_client: port = from_client; + + loop { + match from_client.recv() { + resource_task::Load(_, response) => { + response.send(resource_task::Payload(test_image_bin())); + response.send(resource_task::Done(result::ok(()))); + image_bin_sent_chan.send(()); + } + resource_task::Exit => break + } + } + }; + + let image_cache_task = image_cache_task(mock_resource_task); + let url = make_url(~"file", none); + + image_cache_task.send(Prefetch(url)); + + // Wait until our mock resource task has sent the image to the image cache + image_bin_sent.recv(); + + for iter::repeat(2) { + let response_port = port(); + image_cache_task.send(GetImage(url, response_port.chan())); + match response_port.recv() { + ImageReady(_) => (), + _ => fail + } + } + + image_cache_task.send(Exit); + mock_resource_task.send(resource_task::Exit); +}