diff --git a/Cargo.lock b/Cargo.lock index a8f3e89cafb..d686ee3f913 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4718,6 +4718,7 @@ dependencies = [ "base", "content-security-policy", "cookie 0.18.1", + "crossbeam-channel", "embedder_traits", "headers", "http", diff --git a/components/constellation/network_listener.rs b/components/constellation/network_listener.rs index f36411c01d7..12907262ede 100644 --- a/components/constellation/network_listener.rs +++ b/components/constellation/network_listener.rs @@ -13,7 +13,7 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use log::warn; use net::http_loader::{set_default_accept_language, DOCUMENT_ACCEPT_HEADER_VALUE}; -use net_traits::request::{Referrer, RequestBuilder}; +use net_traits::request::{Referrer, RequestBuilder, RequestId}; use net_traits::response::ResponseInit; use net_traits::{ CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseMsg, IpcSend, NetworkError, @@ -91,7 +91,9 @@ impl NetworkListener { Box::new(move |message| { let msg = message.to(); match msg { - Ok(FetchResponseMsg::ProcessResponse(res)) => listener.check_redirect(res), + Ok(FetchResponseMsg::ProcessResponse(request_id, res)) => { + listener.check_redirect(request_id, res) + }, Ok(msg_) => listener.send(msg_), Err(e) => warn!("Error while receiving network listener message: {}", e), }; @@ -103,7 +105,11 @@ impl NetworkListener { } } - fn check_redirect(&mut self, message: Result) { + fn check_redirect( + &mut self, + request_id: RequestId, + message: Result, + ) { match message { Ok(res_metadata) => { let metadata = match res_metadata { @@ -160,13 +166,16 @@ impl NetworkListener { _ => { // Response should be processed by script thread. self.should_send = true; - self.send(FetchResponseMsg::ProcessResponse(Ok(res_metadata))); + self.send(FetchResponseMsg::ProcessResponse( + request_id, + Ok(res_metadata), + )); }, }; }, Err(e) => { self.should_send = true; - self.send(FetchResponseMsg::ProcessResponse(Err(e))) + self.send(FetchResponseMsg::ProcessResponse(request_id, Err(e))) }, }; } diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs index 0c4a5c419b5..4105d8fa81d 100644 --- a/components/fonts/font_context.rs +++ b/components/fonts/font_context.rs @@ -658,22 +658,24 @@ impl RemoteWebFontDownloader { let core_resource_thread_clone = state.core_resource_thread.clone(); fetch_async( - request, &core_resource_thread_clone, - move |response_message| match downloader.handle_web_font_fetch_message(response_message) - { - DownloaderResponseResult::InProcess => {}, - DownloaderResponseResult::Finished => { - if !downloader.process_downloaded_font_and_signal_completion(&state) { - downloader - .font_context - .process_next_web_font_source(state.clone()) - } - }, - DownloaderResponseResult::Failure => downloader - .font_context - .process_next_web_font_source(state.clone()), - }, + request, + None, + Box::new(move |response_message| { + match downloader.handle_web_font_fetch_message(response_message) { + DownloaderResponseResult::InProcess => {}, + DownloaderResponseResult::Finished => { + if !downloader.process_downloaded_font_and_signal_completion(&state) { + downloader + .font_context + .process_next_web_font_source(state.clone()) + } + }, + DownloaderResponseResult::Failure => downloader + .font_context + .process_next_web_font_source(state.clone()), + } + }), ) } @@ -747,10 +749,10 @@ impl RemoteWebFontDownloader { response_message: FetchResponseMsg, ) -> DownloaderResponseResult { match response_message { - FetchResponseMsg::ProcessRequestBody | FetchResponseMsg::ProcessRequestEOF => { + FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => { DownloaderResponseResult::InProcess }, - FetchResponseMsg::ProcessResponse(meta_result) => { + FetchResponseMsg::ProcessResponse(_, meta_result) => { trace!( "@font-face {} metadata ok={:?}", self.web_font_family_name, @@ -759,7 +761,7 @@ impl RemoteWebFontDownloader { *self.response_valid.lock() = meta_result.is_ok(); DownloaderResponseResult::InProcess }, - FetchResponseMsg::ProcessResponseChunk(new_bytes) => { + FetchResponseMsg::ProcessResponseChunk(_, new_bytes) => { trace!( "@font-face {} chunk={:?}", self.web_font_family_name, @@ -770,7 +772,7 @@ impl RemoteWebFontDownloader { } DownloaderResponseResult::InProcess }, - FetchResponseMsg::ProcessResponseEOF(response) => { + FetchResponseMsg::ProcessResponseEOF(_, response) => { trace!( "@font-face {} EOF={:?}", self.web_font_family_name, diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index f5abcd3003c..7d64b57436d 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -465,7 +465,7 @@ pub async fn main_fetch( let mut response_loaded = false; let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() { // Step 19.1. - wait_for_response(&mut response, target, done_chan).await; + wait_for_response(request, &mut response, target, done_chan).await; response_loaded = true; // Step 19.2. @@ -487,12 +487,12 @@ pub async fn main_fetch( if request.synchronous { // process_response is not supposed to be used // by sync fetch, but we overload it here for simplicity - target.process_response(&response); + target.process_response(request, &response); if !response_loaded { - wait_for_response(&mut response, target, done_chan).await; + wait_for_response(request, &mut response, target, done_chan).await; } // overloaded similarly to process_response - target.process_response_eof(&response); + target.process_response_eof(request, &response); return response; } @@ -507,15 +507,15 @@ pub async fn main_fetch( } // Step 22. - target.process_response(&response); + target.process_response(request, &response); // Step 23. if !response_loaded { - wait_for_response(&mut response, target, done_chan).await; + wait_for_response(request, &mut response, target, done_chan).await; } // Step 24. - target.process_response_eof(&response); + target.process_response_eof(request, &response); if let Ok(http_cache) = context.state.http_cache.write() { http_cache.update_awaiting_consumers(request, &response); @@ -527,6 +527,7 @@ pub async fn main_fetch( } async fn wait_for_response( + request: &Request, response: &mut Response, target: Target<'_>, done_chan: &mut DoneChannel, @@ -535,7 +536,7 @@ async fn wait_for_response( loop { match ch.1.recv().await { Some(Data::Payload(vec)) => { - target.process_response_chunk(vec); + target.process_response_chunk(request, vec); }, Some(Data::Done) => { break; @@ -555,7 +556,7 @@ async fn wait_for_response( // in case there was no channel to wait for, the body was // obtained synchronously via scheme_fetch for data/file/about/etc // We should still send the body across as a chunk - target.process_response_chunk(vec.clone()); + target.process_response_chunk(request, vec.clone()); } else { assert_eq!(*body, ResponseBody::Empty) } diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index dfaa6e4e07e..5df64cf2a67 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -581,9 +581,9 @@ impl ImageCache for ImageCacheImpl { /// Inform the image cache about a response for a pending request. fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg) { match (action, id) { - (FetchResponseMsg::ProcessRequestBody, _) | - (FetchResponseMsg::ProcessRequestEOF, _) => (), - (FetchResponseMsg::ProcessResponse(response), _) => { + (FetchResponseMsg::ProcessRequestBody(..), _) | + (FetchResponseMsg::ProcessRequestEOF(..), _) => (), + (FetchResponseMsg::ProcessResponse(_, response), _) => { debug!("Received {:?} for {:?}", response.as_ref().map(|_| ()), id); let mut store = self.store.lock().unwrap(); let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap(); @@ -608,7 +608,7 @@ impl ImageCache for ImageCacheImpl { pending_load.final_url = final_url; pending_load.cors_status = cors_status; }, - (FetchResponseMsg::ProcessResponseChunk(data), _) => { + (FetchResponseMsg::ProcessResponseChunk(_, data), _) => { debug!("Got some data for {:?}", id); let mut store = self.store.lock().unwrap(); let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap(); @@ -627,7 +627,7 @@ impl ImageCache for ImageCacheImpl { pending_load.metadata = Some(img_metadata); } }, - (FetchResponseMsg::ProcessResponseEOF(result), key) => { + (FetchResponseMsg::ProcessResponseEOF(_, result), key) => { debug!("Received EOF for {:?}", key); match result { Ok(_) => { diff --git a/components/net/tests/data_loader.rs b/components/net/tests/data_loader.rs index 198fe19709c..034e75bbd82 100644 --- a/components/net/tests/data_loader.rs +++ b/components/net/tests/data_loader.rs @@ -7,8 +7,8 @@ use std::ops::Deref; use headers::{ContentType, HeaderMapExt}; use hyper_serde::Serde; use mime::{self, Mime}; -use net_traits::request::{Origin, Referrer, Request}; -use net_traits::response::{HttpsState, ResponseBody}; +use net_traits::request::Referrer; +use net_traits::response::ResponseBody; use net_traits::{FetchMetadata, FilteredMetadata, NetworkError}; use servo_url::ServoUrl; @@ -21,15 +21,13 @@ fn assert_parse( charset: Option<&str>, data: Option<&[u8]>, ) { + use net_traits::request::RequestBuilder; + let url = ServoUrl::parse(url).unwrap(); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .pipeline_id(None) + .build(); let response = fetch(&mut request, None); diff --git a/components/net/tests/fetch.rs b/components/net/tests/fetch.rs index bc0d19a5b9a..81321770a4e 100644 --- a/components/net/tests/fetch.rs +++ b/components/net/tests/fetch.rs @@ -33,15 +33,15 @@ use net::test::HttpState; use net_traits::filemanager_thread::FileTokenCheck; use net_traits::http_status::HttpStatus; use net_traits::request::{ - Destination, Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, + Destination, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, }; -use net_traits::response::{CacheState, HttpsState, Response, ResponseBody, ResponseType}; +use net_traits::response::{CacheState, Response, ResponseBody, ResponseType}; use net_traits::{ FetchTaskTarget, IncludeSubdomains, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, }; use servo_arc::Arc as ServoArc; -use servo_url::{ImmutableOrigin, ServoUrl}; +use servo_url::ServoUrl; use tokio_test::block_on; use uuid::Uuid; @@ -61,14 +61,9 @@ fn test_fetch_response_is_not_network_error() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -80,14 +75,9 @@ fn test_fetch_response_is_not_network_error() { #[test] fn test_fetch_on_bad_port_is_network_error() { let url = ServoUrl::parse("http://www.example.org:6667").unwrap(); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); assert!(fetch_response.is_network_error()); let fetch_error = fetch_response.get_network_error().unwrap(); @@ -105,14 +95,9 @@ fn test_fetch_response_body_matches_const_message() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -130,14 +115,9 @@ fn test_fetch_response_body_matches_const_message() { #[test] fn test_fetch_aboutblank() { let url = ServoUrl::parse("about:blank").unwrap(); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); // We should see an opaque-filtered response. @@ -168,12 +148,12 @@ fn test_fetch_blob() { impl FetchTaskTarget for FetchResponseCollector { fn process_request_body(&mut self, _: &Request) {} fn process_request_eof(&mut self, _: &Request) {} - fn process_response(&mut self, _: &Response) {} - fn process_response_chunk(&mut self, chunk: Vec) { + fn process_response(&mut self, _: &Request, _: &Response) {} + fn process_response_chunk(&mut self, _: &Request, chunk: Vec) { self.buffer.extend_from_slice(chunk.as_slice()); } /// Fired when the response is fully fetched - fn process_response_eof(&mut self, response: &Response) { + fn process_response_eof(&mut self, _: &Request, response: &Response) { assert_eq!(self.buffer, self.expected); let _ = self.sender.send(response.clone()); } @@ -200,13 +180,9 @@ fn test_fetch_blob() { ); let url = ServoUrl::parse(&format!("blob:{}{}", origin.as_str(), id.simple())).unwrap(); - let mut request = Request::new( - url, - Some(Origin::Origin(origin.origin())), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(origin.origin()) + .build(); let (sender, receiver) = unbounded(); @@ -246,14 +222,9 @@ fn test_file() { .unwrap(); let url = ServoUrl::from_file_path(path.clone()).unwrap(); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let pool = CoreResourceThreadPool::new(1); let pool_handle = Arc::new(pool); @@ -294,14 +265,9 @@ fn test_file() { #[test] fn test_fetch_ftp() { let url = ServoUrl::parse("ftp://not-supported").unwrap(); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); assert!(fetch_response.is_network_error()); } @@ -309,14 +275,9 @@ fn test_fetch_ftp() { #[test] fn test_fetch_bogus_scheme() { let url = ServoUrl::parse("bogus://whatever").unwrap(); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); assert!(fetch_response.is_network_error()); } @@ -360,15 +321,7 @@ fn test_cors_preflight_fetch() { let (server, url) = make_server(handler); let target_url = url.clone().join("a.html").unwrap(); - - let origin = Origin::Origin(ImmutableOrigin::new_opaque()); - let mut request = Request::new( - url.clone(), - Some(origin), - Referrer::ReferrerUrl(target_url), - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url, Referrer::ReferrerUrl(target_url)).build(); request.referrer_policy = Some(ReferrerPolicy::Origin); request.use_cors_preflight = true; request.mode = RequestMode::CorsMode; @@ -418,14 +371,7 @@ fn test_cors_preflight_cache_fetch() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(ImmutableOrigin::new_opaque()); - let mut request = Request::new( - url.clone(), - Some(origin.clone()), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); request.use_cors_preflight = true; request.mode = RequestMode::CorsMode; let mut wrapped_request0 = request.clone(); @@ -485,14 +431,7 @@ fn test_cors_preflight_fetch_network_error() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(ImmutableOrigin::new_opaque()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); request.method = Method::from_bytes(b"CHICKEN").unwrap(); request.use_cors_preflight = true; request.mode = RequestMode::CorsMode; @@ -519,14 +458,9 @@ fn test_fetch_response_is_basic_filtered() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -587,14 +521,7 @@ fn test_fetch_response_is_cors_filtered() { let (server, url) = make_server(handler); // an origin mis-match will stop it from defaulting to a basic filtered response - let origin = Origin::Origin(ImmutableOrigin::new_opaque()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); request.mode = RequestMode::CorsMode; let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -626,14 +553,7 @@ fn test_fetch_response_is_opaque_filtered() { let (server, url) = make_server(handler); // an origin mis-match will fall through to an Opaque filtered response - let origin = Origin::Origin(ImmutableOrigin::new_opaque()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -679,14 +599,9 @@ fn test_fetch_response_is_opaque_redirect_filtered() { let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); request.redirect_mode = RedirectMode::Manual; let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -718,14 +633,9 @@ fn test_fetch_with_local_urls_only() { let (server, server_url) = make_server(handler); let do_fetch = |url: ServoUrl| { - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); // Set the flag. request.local_urls_only = true; @@ -786,14 +696,9 @@ fn test_fetch_with_hsts() { HstsEntry::new("localhost".to_owned(), IncludeSubdomains::NotIncluded, None).unwrap(), ); } - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); // Set the flag. request.local_urls_only = false; let response = fetch_with_context(&mut request, &mut context); @@ -935,14 +840,9 @@ fn test_fetch_with_sri_network_error() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); // To calulate hash use : // echo -n "alert('Hello, Network Error');" | openssl dgst -sha384 -binary | openssl base64 -A request.integrity_metadata = @@ -964,14 +864,9 @@ fn test_fetch_with_sri_sucess() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); // To calulate hash use : // echo -n "alert('Hello, Network Error');" | openssl dgst -sha384 -binary | openssl base64 -A request.integrity_metadata = @@ -1009,15 +904,10 @@ fn test_fetch_blocked_nosniff() { let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); - request.destination = destination; + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .destination(destination) + .build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -1059,14 +949,9 @@ fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); fetch_response @@ -1152,15 +1037,10 @@ fn test_fetch_redirect_updates_method_runner( let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); - request.method = method; + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .method(method) + .build(); let _ = fetch(&mut request, None); let _ = server.close(); @@ -1240,15 +1120,9 @@ fn test_fetch_async_returns_complete_response() { }; let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); - + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -1264,15 +1138,7 @@ fn test_opaque_filtered_fetch_async_returns_complete_response() { let (server, url) = make_server(handler); // an origin mis-match will fall through to an Opaque filtered response - let origin = Origin::Origin(ImmutableOrigin::new_opaque()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); - + let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build(); let fetch_response = fetch(&mut request, None); let _ = server.close(); @@ -1304,16 +1170,10 @@ fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() { }; let (server, url) = make_server(handler); - - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url, - Some(origin), - Referrer::NoReferrer, - None, - HttpsState::None, - ); - request.redirect_mode = RedirectMode::Manual; + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .redirect_mode(RedirectMode::Manual) + .build(); let fetch_response = fetch(&mut request, None); @@ -1333,14 +1193,11 @@ fn test_fetch_with_devtools() { let (server, url) = make_server(handler); - let origin = Origin::Origin(url.origin()); - let mut request = Request::new( - url.clone(), - Some(origin), - Referrer::NoReferrer, - Some(TEST_PIPELINE_ID), - HttpsState::None, - ); + let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .origin(url.origin()) + .redirect_mode(RedirectMode::Manual) + .pipeline_id(Some(TEST_PIPELINE_ID)) + .build(); let (devtools_chan, devtools_port) = unbounded(); diff --git a/components/net/tests/http_cache.rs b/components/net/tests/http_cache.rs index 9900295c891..478e452213b 100644 --- a/components/net/tests/http_cache.rs +++ b/components/net/tests/http_cache.rs @@ -6,8 +6,8 @@ use base::id::TEST_PIPELINE_ID; use http::header::{HeaderValue, EXPIRES}; use http::StatusCode; use net::http_cache::HttpCache; -use net_traits::request::{Origin, Referrer, Request}; -use net_traits::response::{HttpsState, Response, ResponseBody}; +use net_traits::request::{Referrer, RequestBuilder}; +use net_traits::response::{Response, ResponseBody}; use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_url::ServoUrl; use tokio::sync::mpsc::unbounded_channel as unbounded; @@ -20,13 +20,10 @@ fn test_refreshing_resource_sets_done_chan_the_appropriate_value() { ResponseBody::Done(vec![]), ]; let url = ServoUrl::parse("https://servo.org").unwrap(); - let request = Request::new( - url.clone(), - Some(Origin::Origin(url.clone().origin())), - Referrer::NoReferrer, - Some(TEST_PIPELINE_ID), - HttpsState::None, - ); + let request = RequestBuilder::new(url.clone(), Referrer::NoReferrer) + .pipeline_id(Some(TEST_PIPELINE_ID)) + .origin(url.origin()) + .build(); let timing = ResourceFetchTiming::new(ResourceTimingType::Navigation); let mut response = Response::new(url.clone(), timing); // Expires header makes the response cacheable. diff --git a/components/net/tests/http_loader.rs b/components/net/tests/http_loader.rs index fac0101baba..bfa87f5df20 100644 --- a/components/net/tests/http_loader.rs +++ b/components/net/tests/http_loader.rs @@ -1426,12 +1426,12 @@ fn test_fetch_compressed_response_update_count() { impl FetchTaskTarget for FetchResponseCollector { fn process_request_body(&mut self, _: &Request) {} fn process_request_eof(&mut self, _: &Request) {} - fn process_response(&mut self, _: &Response) {} - fn process_response_chunk(&mut self, _: Vec) { + fn process_response(&mut self, _: &Request, _: &Response) {} + fn process_response_chunk(&mut self, _: &Request, _: Vec) { self.update_count += 1; } /// Fired when the response is fully fetched - fn process_response_eof(&mut self, _: &Response) { + fn process_response_eof(&mut self, _: &Request, _: &Response) { let _ = self.sender.send(self.update_count); } } diff --git a/components/net/tests/main.rs b/components/net/tests/main.rs index ae86c749d10..010898c2f83 100644 --- a/components/net/tests/main.rs +++ b/components/net/tests/main.rs @@ -121,10 +121,10 @@ fn new_fetch_context( impl FetchTaskTarget for FetchResponseCollector { fn process_request_body(&mut self, _: &Request) {} fn process_request_eof(&mut self, _: &Request) {} - fn process_response(&mut self, _: &Response) {} - fn process_response_chunk(&mut self, _: Vec) {} + fn process_response(&mut self, _: &Request, _: &Response) {} + fn process_response_chunk(&mut self, _: &Request, _: Vec) {} /// Fired when the response is fully fetched - fn process_response_eof(&mut self, response: &Response) { + fn process_response_eof(&mut self, _: &Request, response: &Response) { let _ = self.sender.send(response.clone()); } } diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index 0b2a0ab8139..dd9d74403ef 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -6,9 +6,9 @@ //! //! -use ipc_channel::ipc::IpcSender; +use ipc_channel::ipc; use net_traits::request::RequestBuilder; -use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseMsg, IpcSend, ResourceThreads}; +use net_traits::{fetch_async, BoxedFetchCallback, ResourceThreads}; use servo_url::ServoUrl; use crate::dom::bindings::root::Dom; @@ -111,33 +111,37 @@ impl DocumentLoader { self.blocking_loads.push(load); } - /// Initiate a new fetch. - pub fn fetch_async( + /// Initiate a new fetch given a response callback. + pub fn fetch_async_with_callback( &mut self, load: LoadType, request: RequestBuilder, - fetch_target: IpcSender, + callback: BoxedFetchCallback, ) { self.add_blocking_load(load); - self.fetch_async_background(request, fetch_target); + self.fetch_async_background(request, callback, None); } /// Initiate a new fetch that does not block the document load event. pub fn fetch_async_background( &mut self, request: RequestBuilder, - fetch_target: IpcSender, + callback: BoxedFetchCallback, + cancel_override: Option>, ) { - let mut canceller = FetchCanceller::new(); - let cancel_receiver = canceller.initialize(); - self.cancellers.push(canceller); - self.resource_threads - .sender() - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(fetch_target, Some(cancel_receiver)), - )) - .unwrap(); + let canceller = cancel_override.unwrap_or_else(|| { + let mut canceller = FetchCanceller::new(); + let cancel_receiver = canceller.initialize(); + self.cancellers.push(canceller); + cancel_receiver + }); + + fetch_async( + &self.resource_threads.core_thread, + request, + Some(canceller), + callback, + ); } /// Mark an in-progress network request complete. diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 1ff44280888..2d497339da6 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -11,7 +11,7 @@ use std::default::Default; use std::mem; use std::rc::Rc; use std::slice::from_ref; -use std::sync::LazyLock; +use std::sync::{LazyLock, Mutex}; use std::time::{Duration, Instant}; use base::cross_process_instant::CrossProcessInstant; @@ -28,7 +28,7 @@ use encoding_rs::{Encoding, UTF_8}; use euclid::default::{Point2D, Rect, Size2D}; use html5ever::{local_name, namespace_url, ns, LocalName, Namespace, QualName}; use hyper_serde::Serde; -use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::ipc; use js::rust::{HandleObject, HandleValue}; use keyboard_types::{Code, Key, KeyState}; use metrics::{ @@ -41,7 +41,7 @@ use net_traits::request::RequestBuilder; use net_traits::response::HttpsState; use net_traits::CookieSource::NonHTTP; use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl}; -use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy}; +use net_traits::{FetchResponseListener, IpcSend, ReferrerPolicy}; use num_traits::ToPrimitive; use percent_encoding::percent_decode; use profile_traits::ipc as profile_ipc; @@ -178,6 +178,7 @@ use crate::dom::wheelevent::WheelEvent; use crate::dom::window::{ReflowReason, Window}; use crate::dom::windowproxy::WindowProxy; use crate::fetch::FetchCanceller; +use crate::network_listener::{NetworkListener, PreInvoke}; use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptThreadEventCategory}; use crate::script_thread::{MainThreadScriptMsg, ScriptThread}; @@ -2132,16 +2133,53 @@ impl Document { } } - pub fn fetch_async( + /// Add the CSP list and HTTPS state to a given request. + /// + /// TODO: Can this hapen for all requests that go through the document? + pub(crate) fn prepare_request(&self, request: RequestBuilder) -> RequestBuilder { + request + .csp_list(self.get_csp_list().map(|list| list.clone())) + .https_state(self.https_state.get()) + } + + pub(crate) fn fetch( &self, load: LoadType, - mut request: RequestBuilder, - fetch_target: IpcSender, + request: RequestBuilder, + listener: Listener, ) { - request.csp_list = self.get_csp_list().map(|x| x.clone()); - request.https_state = self.https_state.get(); - let mut loader = self.loader.borrow_mut(); - loader.fetch_async(load, request, fetch_target); + let (task_source, canceller) = self + .window() + .task_manager() + .networking_task_source_with_canceller(); + let callback = NetworkListener { + context: std::sync::Arc::new(Mutex::new(listener)), + task_source, + canceller: Some(canceller), + } + .to_callback(); + self.loader_mut() + .fetch_async_with_callback(load, request, callback); + } + + pub(crate) fn fetch_background( + &self, + request: RequestBuilder, + listener: Listener, + cancel_override: Option>, + ) { + let (task_source, canceller) = self + .window() + .task_manager() + .networking_task_source_with_canceller(); + let callback = NetworkListener { + context: std::sync::Arc::new(Mutex::new(listener)), + task_source, + canceller: Some(canceller), + } + .to_callback(); + self.loader_mut() + .fetch_async_background(request, callback, cancel_override); } // https://html.spec.whatwg.org/multipage/#the-end diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 96b0f497512..4e8f0d03db1 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -17,7 +17,7 @@ use js::conversions::ToJSValConvertible; use js::jsval::UndefinedValue; use js::rust::HandleObject; use mime::{self, Mime}; -use net_traits::request::{CacheMode, CorsSettings, Destination, RequestBuilder}; +use net_traits::request::{CacheMode, CorsSettings, Destination, RequestBuilder, RequestId}; use net_traits::{ CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, FilteredMetadata, NetworkError, ResourceFetchTiming, ResourceTimingType, @@ -336,15 +336,15 @@ impl EventSourceContext { } impl FetchResponseListener for EventSourceContext { - fn process_request_body(&mut self) { + fn process_request_body(&mut self, _: RequestId) { // TODO } - fn process_request_eof(&mut self) { + fn process_request_eof(&mut self, _: RequestId) { // TODO } - fn process_response(&mut self, metadata: Result) { + fn process_response(&mut self, _: RequestId, metadata: Result) { match metadata { Ok(fm) => { let meta = match fm { @@ -378,7 +378,7 @@ impl FetchResponseListener for EventSourceContext { } } - fn process_response_chunk(&mut self, chunk: Vec) { + fn process_response_chunk(&mut self, _: RequestId, chunk: Vec) { let mut input = &*chunk; if let Some(mut incomplete) = self.incomplete_utf8.take() { match incomplete.try_complete(input) { @@ -417,7 +417,11 @@ impl FetchResponseListener for EventSourceContext { } } - fn process_response_eof(&mut self, _response: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + _response: Result, + ) { if self.incomplete_utf8.take().is_some() { self.parse("\u{FFFD}".chars()); } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index e98e73e5da6..90b4459dc28 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -9,7 +9,7 @@ use std::collections::{HashMap, VecDeque}; use std::ops::Index; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::thread::JoinHandle; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::{mem, ptr}; @@ -45,9 +45,12 @@ use net_traits::filemanager_thread::{ FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos, }; use net_traits::image_cache::ImageCache; -use net_traits::request::Referrer; +use net_traits::request::{Referrer, RequestBuilder}; use net_traits::response::HttpsState; -use net_traits::{CoreResourceMsg, CoreResourceThread, IpcSend, ResourceThreads}; +use net_traits::{ + fetch_async, CoreResourceMsg, CoreResourceThread, FetchResponseListener, IpcSend, + ResourceThreads, +}; use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_time}; use script_traits::serializable::{BlobData, BlobImpl, FileBlob}; use script_traits::transferable::MessagePortImpl; @@ -117,6 +120,7 @@ use crate::dom::window::Window; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::workletglobalscope::WorkletGlobalScope; use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask}; +use crate::network_listener::{NetworkListener, PreInvoke}; use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; use crate::script_module::{DynamicModuleList, ModuleScript, ModuleTree, ScriptFetchOptions}; use crate::script_runtime::{ @@ -3391,6 +3395,38 @@ impl GlobalScope { Ok(message_clone.get()) } + + pub(crate) fn fetch( + &self, + request_builder: RequestBuilder, + context: Arc>, + task_source: NetworkingTaskSource, + cancellation_sender: Option>, + ) { + let canceller = Some(self.task_canceller(TaskSourceName::Networking)); + let network_listener = NetworkListener { + context, + task_source, + canceller, + }; + self.fetch_with_network_listener(request_builder, network_listener, cancellation_sender); + } + + pub(crate) fn fetch_with_network_listener< + Listener: FetchResponseListener + PreInvoke + Send + 'static, + >( + &self, + request_builder: RequestBuilder, + network_listener: NetworkListener, + cancellation_receiver: Option>, + ) { + fetch_async( + &self.core_resource_thread(), + request_builder, + cancellation_receiver, + network_listener.to_callback(), + ); + } } /// Returns the Rust global scope from a JS global object. diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index aac070b94c5..af503ea2aa8 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -6,7 +6,7 @@ use std::cell::Cell; use std::collections::HashSet; use std::default::Default; use std::rc::Rc; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::{char, mem}; use app_units::{Au, AU_PER_PX}; @@ -26,7 +26,9 @@ use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, PendingImageId, PendingImageResponse, UsePlaceholder, }; -use net_traits::request::{CorsSettings, Destination, Initiator, Referrer, RequestBuilder}; +use net_traits::request::{ + CorsSettings, Destination, Initiator, Referrer, RequestBuilder, RequestId, +}; use net_traits::{ FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, @@ -92,7 +94,7 @@ use crate::dom::window::Window; use crate::fetch::create_a_potential_cors_request; use crate::image_listener::{generate_cache_listener_for_element, ImageCacheListener}; use crate::microtask::{Microtask, MicrotaskRunnable}; -use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{self, PreInvoke, ResourceTimingListener}; use crate::realms::enter_realm; use crate::script_runtime::CanGc; use crate::script_thread::ScriptThread; @@ -218,13 +220,19 @@ struct ImageContext { } impl FetchResponseListener for ImageContext { - fn process_request_body(&mut self) {} - fn process_request_eof(&mut self) {} + fn process_request_body(&mut self, _: RequestId) {} + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, metadata: Result) { + fn process_response( + &mut self, + request_id: RequestId, + metadata: Result, + ) { debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url); - self.image_cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponse(metadata.clone())); + self.image_cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponse(request_id, metadata.clone()), + ); let metadata = metadata.ok().map(|meta| match meta { FetchMetadata::Unfiltered(m) => m, @@ -262,16 +270,24 @@ impl FetchResponseListener for ImageContext { }; } - fn process_response_chunk(&mut self, payload: Vec) { + fn process_response_chunk(&mut self, request_id: RequestId, payload: Vec) { if self.status.is_ok() { - self.image_cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseChunk(payload)); + self.image_cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponseChunk(request_id, payload), + ); } } - fn process_response_eof(&mut self, response: Result) { - self.image_cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); + fn process_response_eof( + &mut self, + request_id: RequestId, + response: Result, + ) { + self.image_cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponseEOF(request_id, response), + ); } fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { @@ -377,7 +393,7 @@ impl HTMLImageElement { let document = document_from_node(self); let window = window_from_node(self); - let context = Arc::new(Mutex::new(ImageContext { + let context = ImageContext { image_cache: window.image_cache(), status: Ok(()), id, @@ -385,24 +401,7 @@ impl HTMLImageElement { doc: Trusted::new(&document), resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), url: img_url.clone(), - })); - - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let (task_source, canceller) = document - .window() - .task_manager() - .networking_task_source_with_canceller(); - let listener = NetworkListener { - context, - task_source, - canceller: Some(canceller), }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); let request = image_fetch_request( img_url.clone(), @@ -420,9 +419,7 @@ impl HTMLImageElement { // This is a background load because the load blocker already fulfills the // purpose of delaying the document's load event. - document - .loader_mut() - .fetch_async_background(request, action_sender); + document.fetch_background(request, context, None); } // Steps common to when an image has been loaded. diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 566d7896f02..30b8b7665f9 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -5,19 +5,18 @@ use std::borrow::{Borrow, ToOwned}; use std::cell::Cell; use std::default::Default; -use std::sync; use cssparser::{Parser as CssParser, ParserInput}; use dom_struct::dom_struct; use embedder_traits::EmbedderMsg; use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; -use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use js::rust::HandleObject; -use net_traits::request::{CorsSettings, Destination, Initiator, Referrer, RequestBuilder}; +use net_traits::request::{ + CorsSettings, Destination, Initiator, Referrer, RequestBuilder, RequestId, +}; use net_traits::{ - CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, IpcSend, NetworkError, - ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, + FetchMetadata, FetchResponseListener, NetworkError, ReferrerPolicy, ResourceFetchTiming, + ResourceTimingType, }; use servo_arc::Arc; use servo_atoms::Atom; @@ -56,7 +55,7 @@ use crate::dom::stylesheet::StyleSheet as DOMStyleSheet; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::create_a_potential_cors_request; use crate::links::LinkRelations; -use crate::network_listener::{submit_timing, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{submit_timing, PreInvoke, ResourceTimingListener}; use crate::stylesheet_loader::{StylesheetContextSource, StylesheetLoader, StylesheetOwner}; #[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)] @@ -374,42 +373,14 @@ impl HTMLLinkElement { // (Step 7, firing load/error events is handled in the FetchResponseListener impl for PrefetchContext) // Step 8. The user agent should fetch request, with processResponseConsumeBody set to processPrefetchResponse. - let (action_sender, action_receiver) = ipc::channel().unwrap(); let document = self.upcast::().owner_doc(); - let window = document.window(); - - let (task_source, canceller) = window - .task_manager() - .networking_task_source_with_canceller(); - - let fetch_context = sync::Arc::new(sync::Mutex::new(PrefetchContext { + let fetch_context = PrefetchContext { url, link: Trusted::new(self), resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), - })); - - let listener = NetworkListener { - context: fetch_context, - task_source, - canceller: Some(canceller), }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); - - window - .upcast::() - .resource_threads() - .sender() - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(action_sender, None), - )) - .unwrap(); + document.fetch_background(request, fetch_context, None); } /// @@ -713,20 +684,28 @@ struct PrefetchContext { } impl FetchResponseListener for PrefetchContext { - fn process_request_body(&mut self) {} + fn process_request_body(&mut self, _: RequestId) {} - fn process_request_eof(&mut self) {} + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, fetch_metadata: Result) { + fn process_response( + &mut self, + _: RequestId, + fetch_metadata: Result, + ) { _ = fetch_metadata; } - fn process_response_chunk(&mut self, chunk: Vec) { + fn process_response_chunk(&mut self, _: RequestId, chunk: Vec) { _ = chunk; } // Step 7 of `fetch and process the linked resource` in https://html.spec.whatwg.org/multipage/#link-type-prefetch - fn process_response_eof(&mut self, response: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + response: Result, + ) { if response.is_err() { // Step 1. If response is a network error, fire an event named error at el. self.link diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 234122bfe0c..d37fd854ac0 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -21,10 +21,10 @@ use ipc_channel::ipc::{self, IpcSharedMemory}; use ipc_channel::router::ROUTER; use js::jsapi::JSAutoRealm; use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward, WindowGLContext}; -use net_traits::request::Destination; +use net_traits::request::{Destination, RequestId}; use net_traits::{ - CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata, NetworkError, - ResourceFetchTiming, ResourceTimingType, + FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming, + ResourceTimingType, }; use pixels::Image; use script_layout_interface::HTMLMediaData; @@ -101,7 +101,7 @@ use crate::dom::videotracklist::VideoTrackList; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::{create_a_potential_cors_request, FetchCanceller}; use crate::microtask::{Microtask, MicrotaskRunnable}; -use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{self, PreInvoke, ResourceTimingListener}; use crate::realms::{enter_realm, InRealm}; use crate::script_runtime::CanGc; use crate::script_thread::ScriptThread; @@ -890,36 +890,12 @@ impl HTMLMediaElement { } let (fetch_context, cancel_receiver) = HTMLMediaElementFetchContext::new(); *current_fetch_context = Some(fetch_context); - let fetch_listener = Arc::new(Mutex::new(HTMLMediaElementFetchListener::new( - self, - url.clone(), - offset.unwrap_or(0), - seek_lock, - ))); - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let window = window_from_node(self); - let (task_source, canceller) = window - .task_manager() - .networking_task_source_with_canceller(); - let network_listener = NetworkListener { - context: fetch_listener, - task_source, - canceller: Some(canceller), - }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - network_listener.notify_fetch(message.to().unwrap()); - }), - ); - let global = self.global(); - global - .core_resource_thread() - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(action_sender, Some(cancel_receiver)), - )) - .unwrap(); + let listener = + HTMLMediaElementFetchListener::new(self, url.clone(), offset.unwrap_or(0), seek_lock); + + // TODO: If this is supposed to to be a "fetch" as defined in the specification + // this should probably be integrated into the Document's list of cancellable fetches. + document_from_node(self).fetch_background(request, listener, Some(cancel_receiver)); } // https://html.spec.whatwg.org/multipage/#concept-media-load-resource @@ -2681,11 +2657,11 @@ struct HTMLMediaElementFetchListener { // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list impl FetchResponseListener for HTMLMediaElementFetchListener { - fn process_request_body(&mut self) {} + fn process_request_body(&mut self, _: RequestId) {} - fn process_request_eof(&mut self) {} + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, metadata: Result) { + fn process_response(&mut self, _: RequestId, metadata: Result) { let elem = self.elem.root(); if elem.generation_id.get() != self.generation_id || elem.player.borrow().is_none() { @@ -2759,7 +2735,7 @@ impl FetchResponseListener for HTMLMediaElementFetchListener { } } - fn process_response_chunk(&mut self, payload: Vec) { + fn process_response_chunk(&mut self, _: RequestId, payload: Vec) { let elem = self.elem.root(); // If an error was received previously or if we triggered a new fetch request, // we skip processing the payload. @@ -2818,7 +2794,11 @@ impl FetchResponseListener for HTMLMediaElementFetchListener { } // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list - fn process_response_eof(&mut self, status: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + status: Result, + ) { trace!("process response eof"); if let Some(seek_lock) = self.seek_lock.take() { seek_lock.unlock(/* successful seek */ false); diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 491d7718546..7685a91cab5 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -18,12 +18,11 @@ use dom_struct::dom_struct; use encoding_rs::Encoding; use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; use js::rust::{transform_str_to_source_text, CompileOptionsWrapper, HandleObject, Stencil}; use net_traits::http_status::HttpStatus; use net_traits::request::{ - CorsSettings, CredentialsMode, Destination, ParserMetadata, RequestBuilder, + CorsSettings, CredentialsMode, Destination, ParserMetadata, RequestBuilder, RequestId, }; use net_traits::{ FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming, @@ -351,11 +350,13 @@ struct ClassicContext { } impl FetchResponseListener for ClassicContext { - fn process_request_body(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here? + // TODO(KiChjang): Perhaps add custom steps to perform fetch here? + fn process_request_body(&mut self, _: RequestId) {} - fn process_request_eof(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here? + // TODO(KiChjang): Perhaps add custom steps to perform fetch here? + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, metadata: Result) { + fn process_response(&mut self, _: RequestId, metadata: Result) { self.metadata = metadata.ok().map(|meta| match meta { FetchMetadata::Unfiltered(m) => m, FetchMetadata::Filtered { unsafe_, .. } => unsafe_, @@ -383,7 +384,7 @@ impl FetchResponseListener for ClassicContext { }; } - fn process_response_chunk(&mut self, mut chunk: Vec) { + fn process_response_chunk(&mut self, _: RequestId, mut chunk: Vec) { if self.status.is_ok() { self.data.append(&mut chunk); } @@ -392,7 +393,11 @@ impl FetchResponseListener for ClassicContext { /// /// step 4-9 #[allow(unsafe_code)] - fn process_response_eof(&mut self, response: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + response: Result, + ) { let (source_text, final_url) = match (response.as_ref(), self.status.as_ref()) { (Err(err), _) | (_, Err(err)) => { // Step 6, response is an error. @@ -538,9 +543,8 @@ fn fetch_a_classic_script( options: ScriptFetchOptions, character_encoding: &'static Encoding, ) { - let doc = document_from_node(script); - // Step 1, 2. + let doc = document_from_node(script); let request = script_fetch_request( url.clone(), cors_setting, @@ -548,10 +552,11 @@ fn fetch_a_classic_script( script.global().pipeline_id(), options.clone(), ); + let request = doc.prepare_request(request); // TODO: Step 3, Add custom steps to perform fetch - let context = Arc::new(Mutex::new(ClassicContext { + let context = ClassicContext { elem: Trusted::new(script), kind, character_encoding, @@ -561,26 +566,8 @@ fn fetch_a_classic_script( status: Ok(()), fetch_options: options, resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), - })); - - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let (task_source, canceller) = doc - .window() - .task_manager() - .networking_task_source_with_canceller(); - let listener = NetworkListener { - context, - task_source, - canceller: Some(canceller), }; - - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); - doc.fetch_async(LoadType::Script(url), request, action_sender); + doc.fetch(LoadType::Script(url), request, context); } impl HTMLScriptElement { diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index fa9f9c86f94..3211a7c9e37 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -3,22 +3,21 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use dom_struct::dom_struct; use euclid::default::Size2D; use html5ever::{local_name, LocalName, Prefix}; use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use js::rust::HandleObject; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder, }; -use net_traits::request::{CredentialsMode, Destination, RequestBuilder}; +use net_traits::request::{CredentialsMode, Destination, RequestBuilder, RequestId}; use net_traits::{ - CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, - NetworkError, ResourceFetchTiming, ResourceTimingType, + FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ResourceFetchTiming, + ResourceTimingType, }; use servo_media::player::video::VideoFrame; use servo_url::ServoUrl; @@ -41,7 +40,7 @@ use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::FetchCanceller; use crate::image_listener::{generate_cache_listener_for_element, ImageCacheListener}; -use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{self, PreInvoke, ResourceTimingListener}; use crate::script_runtime::CanGc; const DEFAULT_WIDTH: u32 = 300; @@ -213,34 +212,11 @@ impl HTMLVideoElement { LoadType::Image(poster_url.clone()), )); - let window = window_from_node(self); - let context = Arc::new(Mutex::new(PosterFrameFetchContext::new( - self, poster_url, id, - ))); + let context = PosterFrameFetchContext::new(self, poster_url, id); - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let (task_source, canceller) = window - .task_manager() - .networking_task_source_with_canceller(); - let listener = NetworkListener { - context, - task_source, - canceller: Some(canceller), - }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); - let global = self.global(); - global - .core_resource_thread() - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(action_sender, Some(cancel_receiver)), - )) - .unwrap(); + // TODO: If this is supposed to to be a "fetch" as defined in the specification + // this should probably be integrated into the Document's list of cancellable fetches. + document_from_node(self).fetch_background(request, context, Some(cancel_receiver)); } } @@ -326,12 +302,18 @@ struct PosterFrameFetchContext { } impl FetchResponseListener for PosterFrameFetchContext { - fn process_request_body(&mut self) {} - fn process_request_eof(&mut self) {} + fn process_request_body(&mut self, _: RequestId) {} + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, metadata: Result) { - self.image_cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponse(metadata.clone())); + fn process_response( + &mut self, + request_id: RequestId, + metadata: Result, + ) { + self.image_cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponse(request_id, metadata.clone()), + ); let metadata = metadata.ok().map(|meta| match meta { FetchMetadata::Unfiltered(m) => m, @@ -352,19 +334,27 @@ impl FetchResponseListener for PosterFrameFetchContext { } } - fn process_response_chunk(&mut self, payload: Vec) { + fn process_response_chunk(&mut self, request_id: RequestId, payload: Vec) { if self.cancelled { // An error was received previously, skip processing the payload. return; } - self.image_cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseChunk(payload)); + self.image_cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponseChunk(request_id, payload), + ); } - fn process_response_eof(&mut self, response: Result) { - self.image_cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); + fn process_response_eof( + &mut self, + request_id: RequestId, + response: Result, + ) { + self.image_cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponseEOF(request_id, response), + ); } fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs index c9757f6f4d6..034522d2982 100644 --- a/components/script/dom/request.rs +++ b/components/script/dom/request.rs @@ -17,7 +17,7 @@ use net_traits::fetch::headers::is_forbidden_method; use net_traits::request::{ CacheMode as NetTraitsRequestCache, CredentialsMode as NetTraitsRequestCredentials, Destination as NetTraitsRequestDestination, Origin, RedirectMode as NetTraitsRequestRedirect, - Referrer as NetTraitsRequestReferrer, Request as NetTraitsRequest, + Referrer as NetTraitsRequestReferrer, Request as NetTraitsRequest, RequestBuilder, RequestMode as NetTraitsRequestMode, Window, }; use net_traits::ReferrerPolicy as MsgReferrerPolicy; @@ -107,11 +107,11 @@ impl Request { } fn net_request_from_global(global: &GlobalScope, url: ServoUrl) -> NetTraitsRequest { - let origin = Origin::Origin(global.get_url().origin()); - let https_state = global.get_https_state(); - let pipeline_id = global.pipeline_id(); - let referrer = global.get_referrer(); - NetTraitsRequest::new(url, Some(origin), referrer, Some(pipeline_id), https_state) + RequestBuilder::new(url, global.get_referrer()) + .origin(global.get_url().origin()) + .pipeline_id(Some(global.pipeline_id())) + .https_state(global.get_https_state()) + .build() } // https://fetch.spec.whatwg.org/#concept-method-normalize diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 86fc08499fe..bd21a90c91b 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -21,6 +21,7 @@ use html5ever::tree_builder::{ElementFlags, NextParserState, NodeOrText, QuirksM use html5ever::{local_name, namespace_url, ns, Attribute, ExpandedName, LocalName, QualName}; use hyper_serde::Serde; use mime::{self, Mime}; +use net_traits::request::RequestId; use net_traits::{ FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming, ResourceTimingType, @@ -759,11 +760,11 @@ impl ParserContext { } impl FetchResponseListener for ParserContext { - fn process_request_body(&mut self) {} + fn process_request_body(&mut self, _: RequestId) {} - fn process_request_eof(&mut self) {} + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, meta_result: Result) { + fn process_response(&mut self, _: RequestId, meta_result: Result) { let (metadata, error) = match meta_result { Ok(meta) => ( Some(match meta { @@ -914,7 +915,7 @@ impl FetchResponseListener for ParserContext { } } - fn process_response_chunk(&mut self, payload: Vec) { + fn process_response_chunk(&mut self, _: RequestId, payload: Vec) { if self.is_synthesized_document { return; } @@ -932,7 +933,11 @@ impl FetchResponseListener for ParserContext { // This method is called via script_thread::handle_fetch_eof, so we must call // submit_resource_timing in this function // Resource listeners are called via net_traits::Action::process, which handles submission for them - fn process_response_eof(&mut self, status: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + status: Result, + ) { let parser = match self.parser.as_ref() { Some(parser) => parser.root(), None => return, diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index b381c3fc50b..c04e1e97fa2 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -19,7 +19,6 @@ use http::header::{self, HeaderMap, HeaderName, HeaderValue}; use http::Method; use hyper_serde::Serde; use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use js::jsapi::{Heap, JS_ClearPendingException}; use js::jsval::{JSVal, NullValue, UndefinedValue}; use js::rust::wrappers::JS_ParseJSON; @@ -27,11 +26,12 @@ use js::rust::HandleObject; use js::typedarray::{ArrayBuffer, ArrayBufferU8}; use mime::{self, Mime, Name}; use net_traits::http_status::HttpStatus; -use net_traits::request::{CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode}; -use net_traits::CoreResourceMsg::Fetch; +use net_traits::request::{ + CredentialsMode, Destination, Referrer, RequestBuilder, RequestId, RequestMode, +}; use net_traits::{ - trim_http_whitespace, FetchChannels, FetchMetadata, FetchResponseListener, FilteredMetadata, - NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, + trim_http_whitespace, FetchMetadata, FetchResponseListener, FilteredMetadata, NetworkError, + ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, }; use script_traits::serializable::BlobImpl; use script_traits::DocumentActivity; @@ -71,10 +71,9 @@ use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; use crate::dom::xmlhttprequestupload::XMLHttpRequestUpload; use crate::fetch::FetchCanceller; -use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{self, PreInvoke, ResourceTimingListener}; use crate::script_runtime::{CanGc, JSContext}; use crate::task_source::networking::NetworkingTaskSource; -use crate::task_source::TaskSourceName; use crate::timers::{OneshotTimerCallback, OneshotTimerHandle}; #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)] @@ -244,15 +243,19 @@ impl XMLHttpRequest { cancellation_chan: ipc::IpcReceiver<()>, ) { impl FetchResponseListener for XHRContext { - fn process_request_body(&mut self) { + fn process_request_body(&mut self, _: RequestId) { // todo } - fn process_request_eof(&mut self) { + fn process_request_eof(&mut self, _: RequestId) { // todo } - fn process_response(&mut self, metadata: Result) { + fn process_response( + &mut self, + _: RequestId, + metadata: Result, + ) { let xhr = self.xhr.root(); let rv = xhr.process_headers_available(self.gen_id, metadata, CanGc::note()); if rv.is_err() { @@ -260,7 +263,7 @@ impl XMLHttpRequest { } } - fn process_response_chunk(&mut self, chunk: Vec) { + fn process_response_chunk(&mut self, _: RequestId, chunk: Vec) { self.xhr .root() .process_data_available(self.gen_id, chunk, CanGc::note()); @@ -268,6 +271,7 @@ impl XMLHttpRequest { fn process_response_eof( &mut self, + _: RequestId, response: Result, ) { let rv = self.xhr.root().process_response_complete( @@ -307,26 +311,7 @@ impl XMLHttpRequest { } } - let (action_sender, action_receiver) = ipc::channel().unwrap(); - - let listener = NetworkListener { - context, - task_source, - canceller: Some(global.task_canceller(TaskSourceName::Networking)), - }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); - global - .core_resource_thread() - .send(Fetch( - init, - FetchChannels::ResponseMsg(action_sender, Some(cancellation_chan)), - )) - .unwrap(); + global.fetch(init, context, task_source, Some(cancellation_chan)); } } diff --git a/components/script/fetch.rs b/components/script/fetch.rs index 6037e3c0965..107e59b8b4d 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -6,12 +6,10 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use net_traits::request::{ CorsSettings, CredentialsMode, Destination, Referrer, Request as NetTraitsRequest, - RequestBuilder, RequestMode, ServiceWorkersMode, + RequestBuilder, RequestId, RequestMode, ServiceWorkersMode, }; -use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch; use net_traits::{ CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming, @@ -37,12 +35,9 @@ use crate::dom::promise::Promise; use crate::dom::request::Request; use crate::dom::response::Response; use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; -use crate::network_listener::{ - self, submit_timing_data, NetworkListener, PreInvoke, ResourceTimingListener, -}; +use crate::network_listener::{self, submit_timing_data, PreInvoke, ResourceTimingListener}; use crate::realms::{enter_realm, InRealm}; use crate::script_runtime::CanGc; -use crate::task_source::TaskSourceName; struct FetchContext { fetch_promise: Option, @@ -105,6 +100,7 @@ impl Drop for FetchCanceller { fn request_init_from_request(request: NetTraitsRequest) -> RequestBuilder { RequestBuilder { + id: request.id, method: request.method.clone(), url: request.url(), headers: request.headers.clone(), @@ -147,8 +143,6 @@ pub fn Fetch( comp: InRealm, can_gc: CanGc, ) -> Rc { - let core_resource_thread = global.core_resource_thread(); - // Step 1. Let p be a new promise. let promise = Promise::new_in_current_realm(comp); @@ -190,30 +184,18 @@ pub fn Fetch( // Step 12. Set controller to the result of calling fetch given request and // processResponse given response being these steps: [..] - let (action_sender, action_receiver) = ipc::channel().unwrap(); let fetch_context = Arc::new(Mutex::new(FetchContext { fetch_promise: Some(TrustedPromise::new(promise.clone())), response_object: Trusted::new(&*response), resource_timing: ResourceFetchTiming::new(timing_type), })); - let listener = NetworkListener { - context: fetch_context, - task_source: global.networking_task_source(), - canceller: Some(global.task_canceller(TaskSourceName::Networking)), - }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), + global.fetch( + request_init, + fetch_context, + global.networking_task_source(), + None, ); - core_resource_thread - .send(NetTraitsFetch( - request_init, - FetchChannels::ResponseMsg(action_sender, None), - )) - .unwrap(); // Step 13. Return p. promise @@ -222,16 +204,20 @@ pub fn Fetch( impl PreInvoke for FetchContext {} impl FetchResponseListener for FetchContext { - fn process_request_body(&mut self) { + fn process_request_body(&mut self, _: RequestId) { // TODO } - fn process_request_eof(&mut self) { + fn process_request_eof(&mut self, _: RequestId) { // TODO } #[allow(crown::unrooted_must_root)] - fn process_response(&mut self, fetch_metadata: Result) { + fn process_response( + &mut self, + _: RequestId, + fetch_metadata: Result, + ) { let promise = self .fetch_promise .take() @@ -285,12 +271,16 @@ impl FetchResponseListener for FetchContext { self.fetch_promise = Some(TrustedPromise::new(promise)); } - fn process_response_chunk(&mut self, chunk: Vec) { + fn process_response_chunk(&mut self, _: RequestId, chunk: Vec) { let response = self.response_object.root(); response.stream_chunk(chunk); } - fn process_response_eof(&mut self, _response: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + _response: Result, + ) { let response = self.response_object.root(); let _ac = enter_realm(&*response); response.finish(); @@ -354,23 +344,25 @@ pub fn load_whole_resource( let mut metadata = None; loop { match action_receiver.recv().unwrap() { - FetchResponseMsg::ProcessRequestBody | FetchResponseMsg::ProcessRequestEOF => (), - FetchResponseMsg::ProcessResponse(Ok(m)) => { + FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => { + () + }, + FetchResponseMsg::ProcessResponse(_, Ok(m)) => { metadata = Some(match m { FetchMetadata::Unfiltered(m) => m, FetchMetadata::Filtered { unsafe_, .. } => unsafe_, }) }, - FetchResponseMsg::ProcessResponseChunk(data) => buf.extend_from_slice(&data), - FetchResponseMsg::ProcessResponseEOF(Ok(_)) => { + FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data), + FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => { let metadata = metadata.unwrap(); if let Some(timing) = &metadata.timing { submit_timing_data(global, url, InitiatorType::Other, timing); } return Ok((metadata, buf)); }, - FetchResponseMsg::ProcessResponse(Err(e)) | - FetchResponseMsg::ProcessResponseEOF(Err(e)) => return Err(e), + FetchResponseMsg::ProcessResponse(_, Err(e)) | + FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e), } } } diff --git a/components/script/layout_image.rs b/components/script/layout_image.rs index 83454dd8bba..f869be39686 100644 --- a/components/script/layout_image.rs +++ b/components/script/layout_image.rs @@ -6,12 +6,10 @@ //! to be responsible for them because there's no guarantee that the responsible nodes will still //! exist in the future if layout holds on to them during asynchronous operations. -use std::sync::{Arc, Mutex}; +use std::sync::Arc; -use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use net_traits::image_cache::{ImageCache, PendingImageId}; -use net_traits::request::{Destination, RequestBuilder as FetchRequestInit}; +use net_traits::request::{Destination, RequestBuilder as FetchRequestInit, RequestId}; use net_traits::{ FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ResourceFetchTiming, ResourceTimingType, @@ -25,7 +23,7 @@ use crate::dom::document::Document; use crate::dom::globalscope::GlobalScope; use crate::dom::node::{document_from_node, Node}; use crate::dom::performanceresourcetiming::InitiatorType; -use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{self, PreInvoke, ResourceTimingListener}; struct LayoutImageContext { id: PendingImageId, @@ -36,21 +34,35 @@ struct LayoutImageContext { } impl FetchResponseListener for LayoutImageContext { - fn process_request_body(&mut self) {} - fn process_request_eof(&mut self) {} - fn process_response(&mut self, metadata: Result) { - self.cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponse(metadata)); + fn process_request_body(&mut self, _: RequestId) {} + fn process_request_eof(&mut self, _: RequestId) {} + fn process_response( + &mut self, + request_id: RequestId, + metadata: Result, + ) { + self.cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponse(request_id, metadata), + ); } - fn process_response_chunk(&mut self, payload: Vec) { - self.cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseChunk(payload)); + fn process_response_chunk(&mut self, request_id: RequestId, payload: Vec) { + self.cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponseChunk(request_id, payload), + ); } - fn process_response_eof(&mut self, response: Result) { - self.cache - .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); + fn process_response_eof( + &mut self, + request_id: RequestId, + response: Result, + ) { + self.cache.notify_pending_response( + self.id, + FetchResponseMsg::ProcessResponseEOF(request_id, response), + ); } fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { @@ -85,33 +97,13 @@ pub fn fetch_image_for_layout( cache: Arc, ) { let document = document_from_node(node); - - let context = Arc::new(Mutex::new(LayoutImageContext { + let context = LayoutImageContext { id, cache, resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), doc: Trusted::new(&document), url: url.clone(), - })); - - let document = document_from_node(node); - - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let (task_source, canceller) = document - .window() - .task_manager() - .networking_task_source_with_canceller(); - let listener = NetworkListener { - context, - task_source, - canceller: Some(canceller), }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); let request = FetchRequestInit::new(url, document.global().get_referrer()) .origin(document.origin().immutable().clone()) @@ -119,7 +111,5 @@ pub fn fetch_image_for_layout( .pipeline_id(Some(document.global().pipeline_id())); // Layout image loads do not delay the document load event. - document - .loader_mut() - .fetch_async_background(request, action_sender); + document.fetch_background(request, context, None); } diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs index ce7baea2f1a..b2b64bc2614 100644 --- a/components/script/network_listener.rs +++ b/components/script/network_listener.rs @@ -5,7 +5,8 @@ use std::sync::{Arc, Mutex}; use net_traits::{ - Action, FetchResponseListener, FetchResponseMsg, ResourceFetchTiming, ResourceTimingType, + Action, BoxedFetchCallback, FetchResponseListener, FetchResponseMsg, ResourceFetchTiming, + ResourceTimingType, }; use servo_url::ServoUrl; @@ -89,6 +90,10 @@ impl NetworkListen pub fn notify_fetch(&self, action: FetchResponseMsg) { self.notify(action); } + + pub fn to_callback(self) -> BoxedFetchCallback { + Box::new(move |response_msg| self.notify_fetch(response_msg)) + } } /// A gating mechanism that runs before invoking the task on the target thread. diff --git a/components/script/script_module.rs b/components/script/script_module.rs index 89d9d3f0284..886d06f6c8e 100644 --- a/components/script/script_module.rs +++ b/components/script/script_module.rs @@ -15,8 +15,6 @@ use encoding_rs::UTF_8; use html5ever::local_name; use hyper_serde::Serde; use indexmap::IndexSet; -use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use js::jsapi::{ CompileModule1, ExceptionStackBehavior, FinishDynamicModuleImport, GetModuleRequestSpecifier, GetModuleResolveHook, GetRequestedModuleSpecifier, GetRequestedModulesCount, @@ -36,11 +34,11 @@ use js::rust::{ use mime::Mime; use net_traits::http_status::HttpStatus; use net_traits::request::{ - CredentialsMode, Destination, ParserMetadata, Referrer, RequestBuilder, RequestMode, + CredentialsMode, Destination, ParserMetadata, Referrer, RequestBuilder, RequestId, RequestMode, }; use net_traits::{ - CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, IpcSend, Metadata, - NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, + FetchMetadata, FetchResponseListener, Metadata, NetworkError, ReferrerPolicy, + ResourceFetchTiming, ResourceTimingType, }; use servo_url::ServoUrl; use url::ParseError as UrlParseError; @@ -1046,11 +1044,13 @@ struct ModuleContext { } impl FetchResponseListener for ModuleContext { - fn process_request_body(&mut self) {} // TODO(cybai): Perhaps add custom steps to perform fetch here? + // TODO(cybai): Perhaps add custom steps to perform fetch here? + fn process_request_body(&mut self, _: RequestId) {} - fn process_request_eof(&mut self) {} // TODO(cybai): Perhaps add custom steps to perform fetch here? + // TODO(cybai): Perhaps add custom steps to perform fetch here? + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, metadata: Result) { + fn process_response(&mut self, _: RequestId, metadata: Result) { self.metadata = metadata.ok().map(|meta| match meta { FetchMetadata::Unfiltered(m) => m, FetchMetadata::Filtered { unsafe_, .. } => unsafe_, @@ -1078,7 +1078,7 @@ impl FetchResponseListener for ModuleContext { }; } - fn process_response_chunk(&mut self, mut chunk: Vec) { + fn process_response_chunk(&mut self, _: RequestId, mut chunk: Vec) { if self.status.is_ok() { self.data.append(&mut chunk); } @@ -1087,7 +1087,11 @@ impl FetchResponseListener for ModuleContext { /// /// Step 9-12 #[allow(unsafe_code)] - fn process_response_eof(&mut self, response: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + response: Result, + ) { let global = self.owner.global(); if let Some(window) = global.downcast::() { @@ -1669,35 +1673,24 @@ fn fetch_single_module_script( resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); - let (action_sender, action_receiver) = ipc::channel().unwrap(); let task_source = global.networking_task_source(); let canceller = global.task_canceller(TaskSourceName::Networking); - let listener = NetworkListener { + let network_listener = NetworkListener { context, task_source, canceller: Some(canceller), }; - - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); - match document { - Some(doc) => doc.fetch_async(LoadType::Script(url), request, action_sender), - None => { - global - .resource_threads() - .sender() - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(action_sender, None), - )) - .unwrap(); + Some(document) => { + let request = document.prepare_request(request); + document.loader_mut().fetch_async_with_callback( + LoadType::Script(url), + request, + network_listener.to_callback(), + ); }, + None => global.fetch_with_network_listener(request, network_listener, None), } } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 8b5f0200b54..840fb0a4535 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -65,7 +65,7 @@ use metrics::{PaintTimeMetrics, MAX_TASK_NS}; use mime::{self, Mime}; use net_traits::image_cache::{ImageCache, PendingImageResponse}; use net_traits::request::{ - CredentialsMode, Destination, RedirectMode, RequestBuilder, RequestMode, + CredentialsMode, Destination, RedirectMode, RequestBuilder, RequestId, RequestMode, }; use net_traits::storage_thread::StorageType; use net_traits::{ @@ -2295,15 +2295,17 @@ impl ScriptThread { ConstellationControlMsg::StopDelayingLoadEventsMode(pipeline_id) => { self.handle_stop_delaying_load_events_mode(pipeline_id) }, - ConstellationControlMsg::NavigationResponse(id, fetch_data) => { + ConstellationControlMsg::NavigationResponse(pipeline_id, fetch_data) => { match fetch_data { - FetchResponseMsg::ProcessResponse(metadata) => { - self.handle_fetch_metadata(id, metadata) + FetchResponseMsg::ProcessResponse(request_id, metadata) => { + self.handle_fetch_metadata(pipeline_id, request_id, metadata) }, - FetchResponseMsg::ProcessResponseChunk(chunk) => { - self.handle_fetch_chunk(id, chunk) + FetchResponseMsg::ProcessResponseChunk(request_id, chunk) => { + self.handle_fetch_chunk(pipeline_id, request_id, chunk) + }, + FetchResponseMsg::ProcessResponseEOF(request_id, eof) => { + self.handle_fetch_eof(pipeline_id, request_id, eof) }, - FetchResponseMsg::ProcessResponseEOF(eof) => self.handle_fetch_eof(id, eof), _ => unreachable!(), }; }, @@ -4106,6 +4108,7 @@ impl ScriptThread { fn handle_fetch_metadata( &self, id: PipelineId, + request_id: RequestId, fetch_metadata: Result, ) { match fetch_metadata { @@ -4121,21 +4124,26 @@ impl ScriptThread { .iter_mut() .find(|&&mut (pipeline_id, _)| pipeline_id == id); if let Some(&mut (_, ref mut ctxt)) = parser { - ctxt.process_response(fetch_metadata); + ctxt.process_response(request_id, fetch_metadata); } } - fn handle_fetch_chunk(&self, id: PipelineId, chunk: Vec) { + fn handle_fetch_chunk(&self, pipeline_id: PipelineId, request_id: RequestId, chunk: Vec) { let mut incomplete_parser_contexts = self.incomplete_parser_contexts.0.borrow_mut(); let parser = incomplete_parser_contexts .iter_mut() - .find(|&&mut (pipeline_id, _)| pipeline_id == id); + .find(|&&mut (parser_pipeline_id, _)| parser_pipeline_id == pipeline_id); if let Some(&mut (_, ref mut ctxt)) = parser { - ctxt.process_response_chunk(chunk); + ctxt.process_response_chunk(request_id, chunk); } } - fn handle_fetch_eof(&self, id: PipelineId, eof: Result) { + fn handle_fetch_eof( + &self, + id: PipelineId, + request_id: RequestId, + eof: Result, + ) { let idx = self .incomplete_parser_contexts .0 @@ -4145,7 +4153,7 @@ impl ScriptThread { if let Some(idx) = idx { let (_, mut ctxt) = self.incomplete_parser_contexts.0.borrow_mut().remove(idx); - ctxt.process_response_eof(eof); + ctxt.process_response_eof(request_id, eof); } } @@ -4177,9 +4185,13 @@ impl ScriptThread { None => vec![], }; - context.process_response(Ok(FetchMetadata::Unfiltered(meta))); - context.process_response_chunk(chunk); - context.process_response_eof(Ok(ResourceFetchTiming::new(ResourceTimingType::None))); + let dummy_request_id = RequestId::new(); + context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta))); + context.process_response_chunk(dummy_request_id, chunk); + context.process_response_eof( + dummy_request_id, + Ok(ResourceFetchTiming::new(ResourceTimingType::None)), + ); } /// Synchronously parse a srcdoc document from a giving HTML string. @@ -4197,9 +4209,13 @@ impl ScriptThread { let chunk = load_data.srcdoc.into_bytes(); - context.process_response(Ok(FetchMetadata::Unfiltered(meta))); - context.process_response_chunk(chunk); - context.process_response_eof(Ok(ResourceFetchTiming::new(ResourceTimingType::None))); + let dummy_request_id = RequestId::new(); + context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta))); + context.process_response_chunk(dummy_request_id, chunk); + context.process_response_eof( + dummy_request_id, + Ok(ResourceFetchTiming::new(ResourceTimingType::None)), + ); } fn handle_css_error_reporting( diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 6b418b9a1e6..8c30a8340fc 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -3,15 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::sync::atomic::AtomicBool; -use std::sync::Mutex; use base::id::PipelineId; use cssparser::SourceLocation; use encoding_rs::UTF_8; -use ipc_channel::ipc; -use ipc_channel::router::ROUTER; use mime::{self, Mime}; -use net_traits::request::{CorsSettings, Destination, Referrer, RequestBuilder}; +use net_traits::request::{CorsSettings, Destination, Referrer, RequestBuilder, RequestId}; use net_traits::{ FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, @@ -43,7 +40,7 @@ use crate::dom::node::{containing_shadow_root, document_from_node, window_from_n use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::shadowroot::ShadowRoot; use crate::fetch::create_a_potential_cors_request; -use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use crate::network_listener::{self, PreInvoke, ResourceTimingListener}; use crate::script_runtime::CanGc; pub trait StylesheetOwner { @@ -94,11 +91,11 @@ pub struct StylesheetContext { impl PreInvoke for StylesheetContext {} impl FetchResponseListener for StylesheetContext { - fn process_request_body(&mut self) {} + fn process_request_body(&mut self, _: RequestId) {} - fn process_request_eof(&mut self) {} + fn process_request_eof(&mut self, _: RequestId) {} - fn process_response(&mut self, metadata: Result) { + fn process_response(&mut self, _: RequestId, metadata: Result) { if let Ok(FetchMetadata::Filtered { filtered: FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_), .. @@ -113,11 +110,15 @@ impl FetchResponseListener for StylesheetContext { }); } - fn process_response_chunk(&mut self, mut payload: Vec) { + fn process_response_chunk(&mut self, _: RequestId, mut payload: Vec) { self.data.append(&mut payload); } - fn process_response_eof(&mut self, status: Result) { + fn process_response_eof( + &mut self, + _: RequestId, + status: Result, + ) { let elem = self.elem.root(); let document = self.document.root(); let mut successful = false; @@ -279,7 +280,7 @@ impl<'a> StylesheetLoader<'a> { .elem .downcast::() .map(HTMLLinkElement::get_request_generation_id); - let context = ::std::sync::Arc::new(Mutex::new(StylesheetContext { + let context = StylesheetContext { elem: Trusted::new(self.elem), source, url: url.clone(), @@ -290,24 +291,7 @@ impl<'a> StylesheetLoader<'a> { origin_clean: true, request_generation_id: gen, resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), - })); - - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let (task_source, canceller) = document - .window() - .task_manager() - .networking_task_source_with_canceller(); - let listener = NetworkListener { - context, - task_source, - canceller: Some(canceller), }; - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| { - listener.notify_fetch(message.to().unwrap()); - }), - ); let owner = self .elem @@ -331,8 +315,9 @@ impl<'a> StylesheetLoader<'a> { referrer_policy, integrity_metadata, ); + let request = document.prepare_request(request); - document.fetch_async(LoadType::Stylesheet(url), request, action_sender); + document.fetch(LoadType::Stylesheet(url), request, context); } } diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml index bdfe7a326f2..d4b7abbbc18 100644 --- a/components/shared/net/Cargo.toml +++ b/components/shared/net/Cargo.toml @@ -17,6 +17,7 @@ doctest = false base = { workspace = true } content-security-policy = { workspace = true } cookie = { workspace = true } +crossbeam-channel = { workspace = true } embedder_traits = { workspace = true } headers = { workspace = true } http = { workspace = true } diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 03c7adc08e2..bd02b26748b 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -4,12 +4,15 @@ #![deny(unsafe_code)] +use std::collections::HashMap; use std::fmt::Display; -use std::sync::LazyLock; +use std::sync::{LazyLock, OnceLock}; +use std::thread; use base::cross_process_instant::CrossProcessInstant; use base::id::HistoryStateId; use cookie::Cookie; +use crossbeam_channel::{unbounded, Receiver, Sender}; use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; use http::{Error as HttpError, HeaderMap, StatusCode}; use hyper::Error as HyperError; @@ -20,6 +23,7 @@ use ipc_channel::Error as IpcError; use malloc_size_of::malloc_size_of_is_0; use malloc_size_of_derive::MallocSizeOf; use mime::Mime; +use request::RequestId; use rustls::Certificate; use serde::{Deserialize, Serialize}; use servo_rand::RngCore; @@ -177,12 +181,24 @@ impl From for ReferrerPolicyHeader { #[derive(Debug, Deserialize, Serialize)] pub enum FetchResponseMsg { // todo: should have fields for transmitted/total bytes - ProcessRequestBody, - ProcessRequestEOF, + ProcessRequestBody(RequestId), + ProcessRequestEOF(RequestId), // todo: send more info about the response (or perhaps the entire Response) - ProcessResponse(Result), - ProcessResponseChunk(Vec), - ProcessResponseEOF(Result), + ProcessResponse(RequestId, Result), + ProcessResponseChunk(RequestId, Vec), + ProcessResponseEOF(RequestId, Result), +} + +impl FetchResponseMsg { + fn request_id(&self) -> RequestId { + match self { + FetchResponseMsg::ProcessRequestBody(id) | + FetchResponseMsg::ProcessRequestEOF(id) | + FetchResponseMsg::ProcessResponse(id, ..) | + FetchResponseMsg::ProcessResponseChunk(id, ..) | + FetchResponseMsg::ProcessResponseEOF(id, ..) => *id, + } + } } pub trait FetchTaskTarget { @@ -199,15 +215,15 @@ pub trait FetchTaskTarget { /// /// /// Fired when headers are received - fn process_response(&mut self, response: &Response); + fn process_response(&mut self, request: &Request, response: &Response); /// Fired when a chunk of response content is received - fn process_response_chunk(&mut self, chunk: Vec); + fn process_response_chunk(&mut self, request: &Request, chunk: Vec); /// /// /// Fired when the response is fully fetched - fn process_response_eof(&mut self, response: &Response); + fn process_response_eof(&mut self, request: &Request, response: &Response); } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -228,43 +244,52 @@ pub enum FetchMetadata { } pub trait FetchResponseListener { - fn process_request_body(&mut self); - fn process_request_eof(&mut self); - fn process_response(&mut self, metadata: Result); - fn process_response_chunk(&mut self, chunk: Vec); - fn process_response_eof(&mut self, response: Result); + fn process_request_body(&mut self, request_id: RequestId); + fn process_request_eof(&mut self, request_id: RequestId); + fn process_response( + &mut self, + request_id: RequestId, + metadata: Result, + ); + fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec); + fn process_response_eof( + &mut self, + request_id: RequestId, + response: Result, + ); fn resource_timing(&self) -> &ResourceFetchTiming; fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming; fn submit_resource_timing(&mut self); } impl FetchTaskTarget for IpcSender { - fn process_request_body(&mut self, _: &Request) { - let _ = self.send(FetchResponseMsg::ProcessRequestBody); + fn process_request_body(&mut self, request: &Request) { + let _ = self.send(FetchResponseMsg::ProcessRequestBody(request.id)); } - fn process_request_eof(&mut self, _: &Request) { - let _ = self.send(FetchResponseMsg::ProcessRequestEOF); + fn process_request_eof(&mut self, request: &Request) { + let _ = self.send(FetchResponseMsg::ProcessRequestEOF(request.id)); } - fn process_response(&mut self, response: &Response) { - let _ = self.send(FetchResponseMsg::ProcessResponse(response.metadata())); + fn process_response(&mut self, request: &Request, response: &Response) { + let _ = self.send(FetchResponseMsg::ProcessResponse( + request.id, + response.metadata(), + )); } - fn process_response_chunk(&mut self, chunk: Vec) { - let _ = self.send(FetchResponseMsg::ProcessResponseChunk(chunk)); + fn process_response_chunk(&mut self, request: &Request, chunk: Vec) { + let _ = self.send(FetchResponseMsg::ProcessResponseChunk(request.id, chunk)); } - fn process_response_eof(&mut self, response: &Response) { - if let Some(e) = response.get_network_error() { - let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(e.clone()))); + fn process_response_eof(&mut self, request: &Request, response: &Response) { + let payload = if let Some(network_error) = response.get_network_error() { + Err(network_error.clone()) } else { - let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(response - .get_resource_timing() - .lock() - .unwrap() - .clone()))); - } + Ok(response.get_resource_timing().lock().unwrap().clone()) + }; + + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(request.id, payload)); } } @@ -275,14 +300,10 @@ pub struct DiscardFetch; impl FetchTaskTarget for DiscardFetch { fn process_request_body(&mut self, _: &Request) {} - fn process_request_eof(&mut self, _: &Request) {} - - fn process_response(&mut self, _: &Response) {} - - fn process_response_chunk(&mut self, _: Vec) {} - - fn process_response_eof(&mut self, _: &Response) {} + fn process_response(&mut self, _: &Request, _: &Response) {} + fn process_response_chunk(&mut self, _: &Request, _: Vec) {} + fn process_response_eof(&mut self, _: &Request, _: &Response) {} } pub trait Action { @@ -293,16 +314,25 @@ impl Action for FetchResponseMsg { /// Execute the default action on a provided listener. fn process(self, listener: &mut T) { match self { - FetchResponseMsg::ProcessRequestBody => listener.process_request_body(), - FetchResponseMsg::ProcessRequestEOF => listener.process_request_eof(), - FetchResponseMsg::ProcessResponse(meta) => listener.process_response(meta), - FetchResponseMsg::ProcessResponseChunk(data) => listener.process_response_chunk(data), - FetchResponseMsg::ProcessResponseEOF(data) => { + FetchResponseMsg::ProcessRequestBody(request_id) => { + listener.process_request_body(request_id) + }, + FetchResponseMsg::ProcessRequestEOF(request_id) => { + listener.process_request_eof(request_id) + }, + FetchResponseMsg::ProcessResponse(request_id, meta) => { + listener.process_response(request_id, meta) + }, + FetchResponseMsg::ProcessResponseChunk(request_id, data) => { + listener.process_response_chunk(request_id, data) + }, + FetchResponseMsg::ProcessResponseEOF(request_id, data) => { match data { Ok(ref response_resource_timing) => { // update listener with values from response *listener.resource_timing_mut() = response_resource_timing.clone(); - listener.process_response_eof(Ok(response_resource_timing.clone())); + listener + .process_response_eof(request_id, Ok(response_resource_timing.clone())); // TODO timing check https://w3c.github.io/resource-timing/#dfn-timing-allow-check listener.submit_resource_timing(); @@ -311,7 +341,7 @@ impl Action for FetchResponseMsg { // (e.g. due to a network error) MAY be included as PerformanceResourceTiming // objects in the Performance Timeline and MUST contain initialized attribute // values for processed substeps of the processing model. - Err(e) => listener.process_response_eof(Err(e)), + Err(e) => listener.process_response_eof(request_id, Err(e)), } }, } @@ -467,22 +497,110 @@ pub enum CoreResourceMsg { Exit(IpcSender<()>), } +enum ToFetchThreadMessage { + StartFetch( + /* request_builder */ RequestBuilder, + /* cancel_chan */ Option>, + /* callback */ BoxedFetchCallback, + ), + FetchResponse(FetchResponseMsg), +} + +pub type BoxedFetchCallback = Box; + +/// A thread to handle fetches in a Servo process. This thread is responsible for +/// listening for new fetch requests as well as updates on those operations and forwarding +/// them to crossbeam channels. +struct FetchThread { + /// A list of active fetches. A fetch is no longer active once the + /// [`FetchResponseMsg::ProcessResponseEOF`] is received. + active_fetches: HashMap, + /// A reference to the [`CoreResourceThread`] used to kick off fetch requests. + core_resource_thread: CoreResourceThread, + /// A crossbeam receiver attached to the router proxy which converts incoming fetch + /// updates from IPC messages to crossbeam messages as well as another sender which + /// handles requests from clients wanting to do fetches. + receiver: Receiver, + /// An [`IpcSender`] that's sent with every fetch request and leads back to our + /// router proxy. + to_fetch_sender: IpcSender, +} + +impl FetchThread { + fn spawn(core_resource_thread: &CoreResourceThread) -> Sender { + let (sender, receiver) = unbounded(); + let (to_fetch_sender, from_fetch_sender) = ipc::channel().unwrap(); + + let sender_clone = sender.clone(); + ROUTER.add_route( + from_fetch_sender.to_opaque(), + Box::new(move |message| { + let message: FetchResponseMsg = message.to().unwrap(); + let _ = sender_clone.send(ToFetchThreadMessage::FetchResponse(message)); + }), + ); + + let core_resource_thread = core_resource_thread.clone(); + thread::Builder::new() + .name("FetchThread".to_owned()) + .spawn(move || { + let mut fetch_thread = FetchThread { + active_fetches: HashMap::new(), + core_resource_thread, + receiver, + to_fetch_sender, + }; + fetch_thread.run(); + }) + .expect("Thread spawning failed"); + sender + } + + fn run(&mut self) { + loop { + match self.receiver.recv().unwrap() { + ToFetchThreadMessage::StartFetch(request_builder, canceller, callback) => { + self.active_fetches.insert(request_builder.id, callback); + self.core_resource_thread + .send(CoreResourceMsg::Fetch( + request_builder, + FetchChannels::ResponseMsg(self.to_fetch_sender.clone(), canceller), + )) + .unwrap(); + }, + ToFetchThreadMessage::FetchResponse(fetch_response_msg) => { + let request_id = fetch_response_msg.request_id(); + let fetch_finished = + matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..)); + + self.active_fetches + .get(&request_id) + .expect("Got fetch response for unknown fetch")( + fetch_response_msg + ); + + if fetch_finished { + self.active_fetches.remove(&request_id); + } + }, + } + } + } +} + /// Instruct the resource thread to make a new request. -pub fn fetch_async(request: RequestBuilder, core_resource_thread: &CoreResourceThread, f: F) -where - F: Fn(FetchResponseMsg) + Send + 'static, -{ - let (action_sender, action_receiver) = ipc::channel().unwrap(); - ROUTER.add_route( - action_receiver.to_opaque(), - Box::new(move |message| f(message.to().unwrap())), - ); - core_resource_thread - .send(CoreResourceMsg::Fetch( - request, - FetchChannels::ResponseMsg(action_sender, None), - )) - .unwrap(); +pub fn fetch_async( + core_resource_thread: &CoreResourceThread, + request: RequestBuilder, + canceller: Option>, + callback: BoxedFetchCallback, +) { + static FETCH_THREAD: OnceLock> = OnceLock::new(); + let _ = FETCH_THREAD + .get_or_init(|| FetchThread::spawn(core_resource_thread)) + .send(ToFetchThreadMessage::StartFetch( + request, canceller, callback, + )); } #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] diff --git a/components/shared/net/request.rs b/components/shared/net/request.rs index b87351c7dc1..4fc1f874b7f 100644 --- a/components/shared/net/request.rs +++ b/components/shared/net/request.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use base::id::PipelineId; @@ -17,6 +18,17 @@ use servo_url::{ImmutableOrigin, ServoUrl}; use crate::response::HttpsState; use crate::{ReferrerPolicy, ResourceTimingType}; +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] +/// An id to differeniate one network request from another. +pub struct RequestId(usize); + +impl RequestId { + pub fn new() -> Self { + static NEXT_REQUEST_ID: AtomicUsize = AtomicUsize::new(0); + Self(NEXT_REQUEST_ID.fetch_add(1, Ordering::Relaxed)) + } +} + /// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator) #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum Initiator { @@ -223,6 +235,7 @@ impl RequestBody { #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct RequestBuilder { + pub id: RequestId, #[serde( deserialize_with = "::hyper_serde::deserialize", serialize_with = "::hyper_serde::serialize" @@ -272,6 +285,7 @@ pub struct RequestBuilder { impl RequestBuilder { pub fn new(url: ServoUrl, referrer: Referrer) -> RequestBuilder { RequestBuilder { + id: RequestId::new(), method: Method::GET, url, headers: HeaderMap::new(), @@ -396,6 +410,11 @@ impl RequestBuilder { self } + pub fn csp_list(mut self, csp_list: Option) -> RequestBuilder { + self.csp_list = csp_list; + self + } + pub fn crash(mut self, crash: Option) -> Self { self.crash = crash; self @@ -403,6 +422,7 @@ impl RequestBuilder { pub fn build(self) -> Request { let mut request = Request::new( + self.id, self.url.clone(), Some(Origin::Origin(self.origin)), self.referrer, @@ -443,6 +463,9 @@ impl RequestBuilder { /// the Fetch spec. #[derive(Clone, MallocSizeOf)] pub struct Request { + /// The id of this request so that the task that triggered it can route + /// messages to the correct listeners. + pub id: RequestId, /// #[ignore_malloc_size_of = "Defined in hyper"] pub method: Method, @@ -514,6 +537,7 @@ pub struct Request { impl Request { pub fn new( + id: RequestId, url: ServoUrl, origin: Option, referrer: Referrer, @@ -521,6 +545,7 @@ impl Request { https_state: HttpsState, ) -> Request { Request { + id, method: Method::GET, local_urls_only: false, sandboxed_storage_area_urls: false,