diff options
-rw-r--r-- | components/gfx/font_cache_thread.rs | 5 | ||||
-rw-r--r-- | components/net/file_loader.rs | 23 | ||||
-rw-r--r-- | components/net/http_loader.rs | 102 | ||||
-rw-r--r-- | components/net/image_cache_thread.rs | 26 | ||||
-rw-r--r-- | components/net_traits/lib.rs | 76 | ||||
-rw-r--r-- | components/script/document_loader.rs | 15 | ||||
-rw-r--r-- | components/script/dom/bindings/global.rs | 12 | ||||
-rw-r--r-- | components/script/dom/dedicatedworkerglobalscope.rs | 21 | ||||
-rw-r--r-- | components/script/dom/document.rs | 2 | ||||
-rw-r--r-- | components/script/dom/window.rs | 10 | ||||
-rw-r--r-- | components/script/dom/worker.rs | 35 | ||||
-rw-r--r-- | components/script/dom/workerglobalscope.rs | 43 | ||||
-rw-r--r-- | components/script/dom/xmlhttprequest.rs | 30 | ||||
-rw-r--r-- | components/script/script_thread.rs | 39 | ||||
-rw-r--r-- | tests/unit/net/data_loader.rs | 24 | ||||
-rw-r--r-- | tests/unit/net/http_loader.rs | 321 | ||||
-rw-r--r-- | tests/unit/net/resource_thread.rs | 25 |
17 files changed, 663 insertions, 146 deletions
diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 9d9d65c94e9..f132bfc364b 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -6,7 +6,7 @@ use font_template::{FontTemplate, FontTemplateDescriptor}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use mime::{TopLevel, SubLevel}; -use net_traits::{AsyncResponseTarget, LoadContext, PendingAsyncLoad, CoreResourceThread, ResponseAction}; +use net_traits::{AsyncResponseTarget, LoadContext, PendingAsyncLoad, CoreResourceThread, ResponseAction, RequestSource}; use platform::font_context::FontContextHandle; use platform::font_list::SANS_SERIF_FONT_FAMILY; use platform::font_list::for_each_available_family; @@ -186,7 +186,8 @@ impl FontCache { url.clone(), None, None, - None); + None, + RequestSource::None); let (data_sender, data_receiver) = ipc::channel().unwrap(); let data_target = AsyncResponseTarget { sender: data_sender, diff --git a/components/net/file_loader.rs b/components/net/file_loader.rs index f85b6b7dac7..2274279dda1 100644 --- a/components/net/file_loader.rs +++ b/components/net/file_loader.rs @@ -5,8 +5,9 @@ use about_loader; use mime_classifier::MIMEClassifier; use mime_guess::guess_mime_type; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net_traits::ProgressMsg::{Done, Payload}; -use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError}; +use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError, LoadOrigin, RequestSource}; use resource_thread::{CancellationListener, ProgressSender}; use resource_thread::{send_error, start_sending_sniffed_opt}; use std::borrow::ToOwned; @@ -30,6 +31,22 @@ enum LoadResult { Finished, } +struct FileLoadOrigin; +impl LoadOrigin for FileLoadOrigin { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + None + } +} + fn read_block(reader: &mut File) -> Result<ReadStatus, String> { let mut buf = vec![0; READ_SIZE]; match reader.read(&mut buf) { @@ -84,11 +101,12 @@ pub fn factory(load_data: LoadData, // http://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open // but, we'll go for a "file not found!" let url = Url::parse("about:not-found").unwrap(); - let load_data_404 = LoadData::new(load_data.context, url, None, None, None); + let load_data_404 = LoadData::new(load_data.context, url, &FileLoadOrigin); about_loader::factory(load_data_404, senders, classifier, cancel_listener); return; } }; + if cancel_listener.is_cancelled() { if let Ok(progress_chan) = get_progress_chan(load_data, file_path, senders, classifier, &[]) { @@ -96,6 +114,7 @@ pub fn factory(load_data: LoadData, } return; } + match read_block(reader) { Ok(ReadStatus::Partial(buf)) => { let progress_chan = get_progress_chan(load_data, file_path, diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 477872f3ef1..7dd5fc3c193 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -13,15 +13,16 @@ use flate2::read::{DeflateDecoder, GzDecoder}; use hsts::{HstsEntry, HstsList, secure_url}; use hyper::Error as HttpError; use hyper::client::{Pool, Request, Response}; -use hyper::header::{Accept, AcceptEncoding, ContentLength, ContentType, Host, Referer}; +use hyper::header::{Accept, AcceptEncoding, ContentLength, ContentEncoding, ContentType, Host, Referer}; use hyper::header::{Authorization, Basic}; -use hyper::header::{ContentEncoding, Encoding, Header, Headers, Quality, QualityItem}; +use hyper::header::{Encoding, Header, Headers, Quality, QualityItem}; use hyper::header::{Location, SetCookie, StrictTransportSecurity, UserAgent, qitem}; use hyper::http::RawStatus; use hyper::method::Method; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper::net::Fresh; use hyper::status::{StatusClass, StatusCode}; +use ipc_channel::ipc; use log; use mime_classifier::MIMEClassifier; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; @@ -29,7 +30,7 @@ use net_traits::ProgressMsg::{Done, Payload}; use net_traits::hosts::replace_hosts; use net_traits::response::HttpsState; use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData}; -use net_traits::{Metadata, NetworkError}; +use net_traits::{Metadata, NetworkError, RequestSource, CustomResponse}; use openssl::ssl::error::{SslError, OpensslError}; use profile_traits::time::{ProfilerCategory, profile, ProfilerChan, TimerMetadata}; use profile_traits::time::{TimerMetadataReflowType, TimerMetadataFrameType}; @@ -39,7 +40,7 @@ use std::boxed::FnBox; use std::collections::HashSet; use std::error::Error; use std::fmt; -use std::io::{self, Read, Write}; +use std::io::{self, Cursor, Read, Write}; use std::sync::mpsc::Sender; use std::sync::{Arc, RwLock}; use time; @@ -149,6 +150,17 @@ fn load_for_consumer(load_data: LoadData, } } +pub struct WrappedHttpResponse { + pub response: Response +} + +impl Read for WrappedHttpResponse { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.response.read(buf) + } +} + pub trait HttpResponse: Read { fn headers(&self) -> &Headers; fn status(&self) -> StatusCode; @@ -173,20 +185,6 @@ pub trait HttpResponse: Read { } } - -pub struct WrappedHttpResponse { - pub response: Response -} - -impl Read for WrappedHttpResponse { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - self.response.read(buf) - } -} - - - impl HttpResponse for WrappedHttpResponse { fn headers(&self) -> &Headers { &self.response.headers @@ -205,6 +203,34 @@ impl HttpResponse for WrappedHttpResponse { } } +pub struct ReadableCustomResponse { + headers: Headers, + raw_status: RawStatus, + body: Cursor<Vec<u8>> +} + +pub fn to_readable_response(custom_response: CustomResponse) -> ReadableCustomResponse { + ReadableCustomResponse { + headers: custom_response.headers, + raw_status: custom_response.raw_status, + body: Cursor::new(custom_response.body) + } +} + +impl HttpResponse for ReadableCustomResponse { + fn headers(&self) -> &Headers { &self.headers } + fn status(&self) -> StatusCode { + StatusCode::Ok + } + fn status_raw(&self) -> &RawStatus { &self.raw_status } +} + +impl Read for ReadableCustomResponse { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.body.read(buf) + } +} + pub trait HttpRequestFactory { type R: HttpRequest; @@ -466,13 +492,13 @@ fn update_sts_list_from_response(url: &Url, response: &HttpResponse, hsts_list: } } -pub struct StreamedResponse<R: HttpResponse> { - decoder: Decoder<R>, +pub struct StreamedResponse { + decoder: Decoder, pub metadata: Metadata } -impl<R: HttpResponse> Read for StreamedResponse<R> { +impl Read for StreamedResponse { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { match self.decoder { @@ -484,12 +510,12 @@ impl<R: HttpResponse> Read for StreamedResponse<R> { } } -impl<R: HttpResponse> StreamedResponse<R> { - fn new(m: Metadata, d: Decoder<R>) -> StreamedResponse<R> { +impl StreamedResponse { + fn new(m: Metadata, d: Decoder) -> StreamedResponse { StreamedResponse { metadata: m, decoder: d } } - fn from_http_response(response: R, m: Metadata) -> Result<StreamedResponse<R>, LoadError> { + fn from_http_response(response: Box<HttpResponse>, m: Metadata) -> Result<StreamedResponse, LoadError> { let decoder = match response.content_encoding() { Some(Encoding::Gzip) => { let result = GzDecoder::new(response); @@ -515,11 +541,11 @@ impl<R: HttpResponse> StreamedResponse<R> { } } -enum Decoder<R: Read> { - Gzip(GzDecoder<R>), - Deflate(DeflateDecoder<R>), - Brotli(Decompressor<R>), - Plain(R) +enum Decoder { + Gzip(GzDecoder<Box<HttpResponse>>), + Deflate(DeflateDecoder<Box<HttpResponse>>), + Brotli(Decompressor<Box<HttpResponse>>), + Plain(Box<HttpResponse>) } fn send_request_to_devtools(devtools_chan: Option<Sender<DevtoolsControlMsg>>, @@ -771,7 +797,7 @@ pub fn load<A, B>(load_data: &LoadData, request_factory: &HttpRequestFactory<R=A>, user_agent: String, cancel_listener: &CancellationListener) - -> Result<StreamedResponse<A::R>, LoadError> where A: HttpRequest + 'static, B: UIProvider { + -> Result<StreamedResponse, LoadError> where A: HttpRequest + 'static, B: UIProvider { let max_redirects = prefs::get_pref("network.http.redirection-limit").as_i64().unwrap() as u32; let mut iters = 0; // URL of the document being loaded, as seen by all the higher-level code. @@ -785,6 +811,20 @@ pub fn load<A, B>(load_data: &LoadData, return Err(LoadError::new(doc_url, LoadErrorType::Cancelled)); } + let (msg_sender, msg_receiver) = ipc::channel().unwrap(); + match load_data.source { + RequestSource::Window(ref sender) | RequestSource::Worker(ref sender) => { + sender.send(msg_sender.clone()).unwrap(); + let received_msg = msg_receiver.recv().unwrap(); + if let Some(custom_response) = received_msg { + let metadata = Metadata::default(doc_url.clone()); + let readable_response = to_readable_response(custom_response); + return StreamedResponse::from_http_response(box readable_response, metadata); + } + } + RequestSource::None => {} + } + // If the URL is a view-source scheme then the scheme data contains the // real URL that should be used for which the source is to be viewed. // Change our existing URL to that and keep note that we are viewing @@ -942,7 +982,7 @@ pub fn load<A, B>(load_data: &LoadData, metadata.headers.clone(), metadata.status.clone(), pipeline_id); } - return StreamedResponse::from_http_response(response, metadata) + return StreamedResponse::from_http_response(box response, metadata) } } diff --git a/components/net/image_cache_thread.rs b/components/net/image_cache_thread.rs index 6ecf32f5730..3991c5553ac 100644 --- a/components/net/image_cache_thread.rs +++ b/components/net/image_cache_thread.rs @@ -5,12 +5,13 @@ use immeta::load_from_buf; use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; use ipc_channel::router::ROUTER; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net_traits::image::base::{Image, ImageMetadata, load_from_memory, PixelFormat}; use net_traits::image_cache_thread::ImageResponder; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState}; use net_traits::image_cache_thread::{ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, UsePlaceholder}; -use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadData, CoreResourceThread}; -use net_traits::{ResponseAction, LoadContext, NetworkError}; +use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadData, CoreResourceThread, LoadOrigin}; +use net_traits::{ResponseAction, LoadContext, NetworkError, RequestSource}; use std::borrow::ToOwned; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -304,6 +305,23 @@ fn convert_format(format: PixelFormat) -> webrender_traits::ImageFormat { } } +struct ImageCacheOrigin; +impl LoadOrigin for ImageCacheOrigin { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + None + } +} + + impl ImageCache { fn run(core_resource_thread: CoreResourceThread, webrender_api: Option<webrender_traits::RenderApi>, @@ -520,7 +538,9 @@ impl ImageCache { CacheResult::Miss => { // A new load request! Request the load from // the resource thread. - let load_data = LoadData::new(LoadContext::Image, (*ref_url).clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Image, + (*ref_url).clone(), + &ImageCacheOrigin); let (action_sender, action_receiver) = ipc::channel().unwrap(); let response_target = AsyncResponseTarget { sender: action_sender, diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 94b43b65694..e396da5d287 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -76,6 +76,30 @@ pub enum LoadContext { CacheManifest, } +#[derive(Clone, Debug, Deserialize, Serialize, HeapSizeOf)] +pub struct CustomResponse { + #[ignore_heap_size_of = "Defined in hyper"] + pub headers: Headers, + #[ignore_heap_size_of = "Defined in hyper"] + pub raw_status: RawStatus, + pub body: Vec<u8> +} + +impl CustomResponse { + pub fn new(headers: Headers, raw_status: RawStatus, body: Vec<u8>) -> CustomResponse { + CustomResponse { headers: headers, raw_status: raw_status, body: body } + } +} + +pub type CustomResponseSender = IpcSender<Option<CustomResponse>>; + +#[derive(Clone, Deserialize, Serialize, HeapSizeOf)] +pub enum RequestSource { + Window(#[ignore_heap_size_of = "Defined in ipc-channel"] IpcSender<CustomResponseSender>), + Worker(#[ignore_heap_size_of = "Defined in ipc-channel"] IpcSender<CustomResponseSender>), + None +} + #[derive(Clone, Deserialize, Serialize, HeapSizeOf)] pub struct LoadData { pub url: Url, @@ -96,15 +120,14 @@ pub struct LoadData { /// The policy and referring URL for the originator of this request pub referrer_policy: Option<ReferrerPolicy>, pub referrer_url: Option<Url>, + pub source: RequestSource, } impl LoadData { pub fn new(context: LoadContext, url: Url, - id: Option<PipelineId>, - referrer_policy: Option<ReferrerPolicy>, - referrer_url: Option<Url>) -> LoadData { + load_origin: &LoadOrigin) -> LoadData { LoadData { url: url, method: Method::Get, @@ -112,15 +135,23 @@ impl LoadData { preserved_headers: Headers::new(), data: None, cors: None, - pipeline_id: id, + pipeline_id: load_origin.pipeline_id(), credentials_flag: true, context: context, - referrer_policy: referrer_policy, - referrer_url: referrer_url + referrer_policy: load_origin.referrer_policy(), + referrer_url: load_origin.referrer_url(), + source: load_origin.request_source() } } } +pub trait LoadOrigin { + fn referrer_url(&self) -> Option<Url>; + fn referrer_policy(&self) -> Option<ReferrerPolicy>; + fn request_source(&self) -> RequestSource; + fn pipeline_id(&self) -> Option<PipelineId>; +} + /// Interface for observing the final response for an asynchronous fetch operation. pub trait AsyncFetchListener { fn response_available(&self, response: response::Response); @@ -304,6 +335,7 @@ pub struct PendingAsyncLoad { context: LoadContext, referrer_policy: Option<ReferrerPolicy>, referrer_url: Option<Url>, + source: RequestSource } struct PendingLoadGuard { @@ -324,13 +356,29 @@ impl Drop for PendingLoadGuard { } } +impl LoadOrigin for PendingAsyncLoad { + fn referrer_url(&self) -> Option<Url> { + self.referrer_url.clone() + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + self.referrer_policy.clone() + } + fn request_source(&self) -> RequestSource { + self.source.clone() + } + fn pipeline_id(&self) -> Option<PipelineId> { + self.pipeline + } +} + impl PendingAsyncLoad { pub fn new(context: LoadContext, core_resource_thread: CoreResourceThread, url: Url, pipeline: Option<PipelineId>, referrer_policy: Option<ReferrerPolicy>, - referrer_url: Option<Url>) + referrer_url: Option<Url>, + source: RequestSource) -> PendingAsyncLoad { PendingAsyncLoad { core_resource_thread: core_resource_thread, @@ -339,14 +387,18 @@ impl PendingAsyncLoad { guard: PendingLoadGuard { loaded: false, }, context: context, referrer_policy: referrer_policy, - referrer_url: referrer_url + referrer_url: referrer_url, + source: source } } /// Initiate the network request associated with this pending load, using the provided target. pub fn load_async(mut self, listener: AsyncResponseTarget) { self.guard.neuter(); - let load_data = LoadData::new(self.context, self.url, self.pipeline, self.referrer_policy, self.referrer_url); + + let load_data = LoadData::new(self.context.clone(), + self.url.clone(), + &self); let consumer = LoadConsumer::Listener(listener); self.core_resource_thread.send(CoreResourceMsg::Load(load_data, consumer, None)).unwrap(); } @@ -460,11 +512,11 @@ pub enum ProgressMsg { pub fn load_whole_resource(context: LoadContext, core_resource_thread: &CoreResourceThread, url: Url, - pipeline_id: Option<PipelineId>) + load_origin: &LoadOrigin) -> Result<(Metadata, Vec<u8>), NetworkError> { let (start_chan, start_port) = ipc::channel().unwrap(); - core_resource_thread.send(CoreResourceMsg::Load(LoadData::new(context, url, pipeline_id, None, None), - LoadConsumer::Channel(start_chan), None)).unwrap(); + let load_data = LoadData::new(context, url, load_origin); + core_resource_thread.send(CoreResourceMsg::Load(load_data, LoadConsumer::Channel(start_chan), None)).unwrap(); let response = start_port.recv().unwrap(); let mut buf = vec!(); diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index 7282e23c322..4d5df6c74e5 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -8,8 +8,8 @@ use dom::bindings::js::JS; use dom::document::Document; use msg::constellation_msg::PipelineId; -use net_traits::AsyncResponseTarget; use net_traits::{PendingAsyncLoad, CoreResourceThread, LoadContext}; +use net_traits::{RequestSource, AsyncResponseTarget}; use std::sync::Arc; use std::thread; use url::Url; @@ -130,20 +130,27 @@ impl DocumentLoader { /// Create a new pending network request, which can be initiated at some point in /// the future. - pub fn prepare_async_load(&mut self, load: LoadType, referrer: &Document) -> PendingAsyncLoad { + pub fn prepare_async_load(&mut self, + load: LoadType, + referrer: &Document) -> PendingAsyncLoad { let context = load.to_load_context(); let url = load.url().clone(); self.add_blocking_load(load); + let client_chan = referrer.window().custom_message_chan(); PendingAsyncLoad::new(context, (*self.resource_thread).clone(), url, self.pipeline, referrer.get_referrer_policy(), - Some(referrer.url().clone())) + Some(referrer.url().clone()), + RequestSource::Window(client_chan)) } /// Create and initiate a new network request. - pub fn load_async(&mut self, load: LoadType, listener: AsyncResponseTarget, referrer: &Document) { + pub fn load_async(&mut self, + load: LoadType, + listener: AsyncResponseTarget, + referrer: &Document) { let pending = self.prepare_async_load(load, referrer); pending.load_async(listener) } diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index 61b948dbf35..ff36817b660 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -18,8 +18,8 @@ use ipc_channel::ipc::IpcSender; use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment}; use js::jsapi::{JSContext, JSObject, JS_GetClass, MutableHandleValue}; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; -use msg::constellation_msg::{PanicMsg, PipelineId}; -use net_traits::CoreResourceThread; +use msg::constellation_msg::{PipelineId, PanicMsg}; +use net_traits::{CoreResourceThread, RequestSource}; use profile_traits::{mem, time}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_thread::{MainThreadScriptChan, ScriptThread}; @@ -65,6 +65,14 @@ impl<'a> GlobalRef<'a> { } } + /// gets the custom message channel associated with global object + pub fn request_source(&self) -> RequestSource { + match *self { + GlobalRef::Window(ref window) => RequestSource::Window(window.custom_message_chan()), + GlobalRef::Worker(ref worker) => RequestSource::Worker(worker.custom_message_chan()), + } + } + /// Get the `PipelineId` for this global scope. pub fn pipeline(&self) -> PipelineId { match *self { diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index bd5cd63fcfb..120565b00fa 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -16,7 +16,8 @@ use dom::bindings::refcounted::LiveDOMReferences; use dom::bindings::reflector::Reflectable; use dom::bindings::structuredclone::StructuredCloneData; use dom::messageevent::MessageEvent; -use dom::worker::{SimpleWorkerErrorHandler, SharedRt, TrustedWorkerAddress, WorkerMessageHandler}; +use dom::worker::{SimpleWorkerErrorHandler, SharedRt, TrustedWorkerAddress}; +use dom::worker::{WorkerScriptLoadOrigin, WorkerMessageHandler}; use dom::workerglobalscope::WorkerGlobalScope; use dom::workerglobalscope::WorkerGlobalScopeInit; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -26,7 +27,7 @@ use js::jsapi::{JSAutoCompartment, JSContext, RootedValue}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; -use net_traits::{LoadContext, load_whole_resource}; +use net_traits::{LoadContext, load_whole_resource, CustomResponse}; use rand::random; use script_runtime::ScriptThreadEventCategory::WorkerEvent; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; @@ -133,6 +134,7 @@ enum MixedMessage { FromWorker((TrustedWorkerAddress, WorkerScriptMsg)), FromScheduler((TrustedWorkerAddress, TimerEvent)), FromDevtools(DevtoolScriptControlMsg), + FromNetwork(IpcSender<Option<CustomResponse>>), } // https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope @@ -215,18 +217,18 @@ impl DedicatedWorkerGlobalScope { worker: TrustedWorkerAddress, parent_sender: Box<ScriptChan + Send>, own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>) { + receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, + worker_load_origin: WorkerScriptLoadOrigin) { let serialized_worker_url = worker_url.to_string(); let name = format!("WebWorker for {}", serialized_worker_url); let panic_chan = init.panic_chan.clone(); spawn_named_with_send_on_panic(name, SCRIPT | IN_WORKER, move || { let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); - let (url, source) = match load_whole_resource(LoadContext::Script, &init.core_resource_thread, worker_url, - None) { + &worker_load_origin) { Err(_) => { println!("error loading script {}", serialized_worker_url); parent_sender.send(CommonScriptMsg::RunnableMsg(WorkerEvent, @@ -316,17 +318,20 @@ impl DedicatedWorkerGlobalScope { let worker_port = &self.receiver; let timer_event_port = &self.timer_event_port; let devtools_port = scope.from_devtools_receiver(); + let msg_port = scope.custom_message_port(); let sel = Select::new(); let mut worker_handle = sel.handle(worker_port); let mut timer_event_handle = sel.handle(timer_event_port); let mut devtools_handle = sel.handle(devtools_port); + let mut msg_port_handle = sel.handle(msg_port); unsafe { worker_handle.add(); timer_event_handle.add(); if scope.from_devtools_sender().is_some() { devtools_handle.add(); } + msg_port_handle.add(); } let ret = sel.wait(); if ret == worker_handle.id() { @@ -335,6 +340,8 @@ impl DedicatedWorkerGlobalScope { Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv()))) } else if ret == devtools_handle.id() { Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) + } else if ret == msg_port_handle.id() { + Ok(MixedMessage::FromNetwork(try!(msg_port.recv()))) } else { panic!("unexpected select result!") } @@ -397,6 +404,10 @@ impl DedicatedWorkerGlobalScope { let _ar = AutoWorkerReset::new(self, linked_worker); self.handle_script_event(msg); }, + MixedMessage::FromNetwork(network_sender) => { + // We send None as of now + let _ = network_sender.send(None); + } } } } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index ddf23297282..b7ccbd36d26 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1354,7 +1354,7 @@ impl Document { pub fn load_async(&self, load: LoadType, listener: AsyncResponseTarget) { let mut loader = self.loader.borrow_mut(); - loader.load_async(load, listener, self) + loader.load_async(load, listener, self); } pub fn finish_load(&self, load: LoadType) { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index c9111ad15e5..c3385f9214c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -45,10 +45,10 @@ use libc; use msg::constellation_msg::{LoadData, PanicMsg, PipelineId, SubpageId}; use msg::constellation_msg::{WindowSizeData, WindowSizeType}; use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; -use net_traits::ResourceThreads; use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::storage_thread::StorageType; +use net_traits::{ResourceThreads, CustomResponseSender}; use num_traits::ToPrimitive; use profile_traits::mem; use profile_traits::time::{ProfilerCategory, TimerMetadata, TimerMetadataFrameType}; @@ -148,6 +148,8 @@ pub struct Window { image_cache_thread: ImageCacheThread, #[ignore_heap_size_of = "channels are hard"] image_cache_chan: ImageCacheChan, + #[ignore_heap_size_of = "channels are hard"] + custom_message_chan: IpcSender<CustomResponseSender>, #[ignore_heap_size_of = "TODO(#6911) newtypes containing unmeasurable types are hard"] compositor: IpcSender<ScriptToCompositorMsg>, browsing_context: MutNullableHeap<JS<BrowsingContext>>, @@ -303,6 +305,10 @@ impl Window { self.image_cache_chan.clone() } + pub fn custom_message_chan(&self) -> IpcSender<CustomResponseSender> { + self.custom_message_chan.clone() + } + pub fn get_next_worker_id(&self) -> WorkerId { let worker_id = self.next_worker_id.get(); let WorkerId(id_num) = worker_id; @@ -1443,6 +1449,7 @@ impl Window { history_task_source: HistoryTraversalTaskSource, file_task_source: FileReadingTaskSource, image_cache_chan: ImageCacheChan, + custom_message_chan: IpcSender<CustomResponseSender>, compositor: IpcSender<ScriptToCompositorMsg>, image_cache_thread: ImageCacheThread, resource_threads: ResourceThreads, @@ -1480,6 +1487,7 @@ impl Window { history_traversal_task_source: history_task_source, file_reading_task_source: file_task_source, image_cache_chan: image_cache_chan, + custom_message_chan: custom_message_chan, console: Default::default(), crypto: Default::default(), compositor: compositor, diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 661d397460f..f7900a0d0f0 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -24,10 +24,13 @@ use js::jsapi::{HandleValue, JSContext, JSRuntime, RootedValue}; use js::jsapi::{JSAutoCompartment, JS_RequestInterruptCallback}; use js::jsval::UndefinedValue; use js::rust::Runtime; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; +use net_traits::{RequestSource, LoadOrigin}; use script_thread::Runnable; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Sender, channel}; use std::sync::{Arc, Mutex}; +use url::Url; use util::str::DOMString; pub type TrustedWorkerAddress = Trusted<Worker>; @@ -45,6 +48,29 @@ pub struct Worker { runtime: Arc<Mutex<Option<SharedRt>>> } +#[derive(Clone)] +pub struct WorkerScriptLoadOrigin { + referrer_url: Option<Url>, + referrer_policy: Option<ReferrerPolicy>, + request_source: RequestSource, + pipeline_id: Option<PipelineId> +} + +impl LoadOrigin for WorkerScriptLoadOrigin { + fn referrer_url(&self) -> Option<Url> { + self.referrer_url.clone() + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + self.referrer_policy.clone() + } + fn request_source(&self) -> RequestSource { + self.request_source.clone() + } + fn pipeline_id(&self) -> Option<PipelineId> { + self.pipeline_id.clone() + } +} + impl Worker { fn new_inherited(sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, closing: Arc<AtomicBool>) -> Worker { @@ -82,6 +108,13 @@ impl Worker { let worker_ref = Trusted::new(worker.r()); let worker_id = global.get_next_worker_id(); + let worker_load_origin = WorkerScriptLoadOrigin { + referrer_url: None, + referrer_policy: None, + request_source: global.request_source(), + pipeline_id: Some(global.pipeline()) + }; + let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); let optional_sender = match global.devtools_chan() { Some(ref chan) => { @@ -114,7 +147,7 @@ impl Worker { DedicatedWorkerGlobalScope::run_worker_scope( init, worker_url, global.pipeline(), devtools_receiver, worker.runtime.clone(), worker_ref, - global.script_chan(), sender, receiver); + global.script_chan(), sender, receiver, worker_load_origin); Ok(worker) } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index ef9afd34cf7..a8b5d9a48c3 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -17,12 +17,13 @@ use dom::eventtarget::EventTarget; use dom::window::{base64_atob, base64_btoa}; use dom::workerlocation::WorkerLocation; use dom::workernavigator::WorkerNavigator; -use ipc_channel::ipc::IpcSender; +use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; use js::jsapi::{HandleValue, JSContext, JSRuntime, RootedValue}; use js::jsval::UndefinedValue; use js::rust::Runtime; -use msg::constellation_msg::{PanicMsg, PipelineId}; -use net_traits::{LoadContext, CoreResourceThread, load_whole_resource}; +use msg::constellation_msg::{PipelineId, ReferrerPolicy, PanicMsg}; +use net_traits::{LoadContext, CoreResourceThread, load_whole_resource, RequestSource, LoadOrigin, CustomResponseSender}; use profile_traits::{mem, time}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_traits::ScriptMsg as ConstellationMsg; @@ -101,6 +102,12 @@ pub struct WorkerGlobalScope { #[ignore_heap_size_of = "Defined in ipc-channel"] panic_chan: IpcSender<PanicMsg>, + + #[ignore_heap_size_of = "Defined in ipc-channel"] + custom_msg_chan: IpcSender<CustomResponseSender>, + + #[ignore_heap_size_of = "Defined in std"] + custom_msg_port: Receiver<CustomResponseSender>, } impl WorkerGlobalScope { @@ -110,7 +117,8 @@ impl WorkerGlobalScope { from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, timer_event_chan: IpcSender<TimerEvent>) -> WorkerGlobalScope { - + let (msg_chan, msg_port) = ipc::channel().unwrap(); + let custom_msg_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(msg_port); WorkerGlobalScope { eventtarget: EventTarget::new_inherited(), next_worker_id: Cell::new(WorkerId(0)), @@ -133,6 +141,8 @@ impl WorkerGlobalScope { constellation_chan: init.constellation_chan, scheduler_chan: init.scheduler_chan, panic_chan: init.panic_chan, + custom_msg_chan: msg_chan, + custom_msg_port: custom_msg_port } } @@ -182,6 +192,14 @@ impl WorkerGlobalScope { self.runtime.cx() } + pub fn custom_message_chan(&self) -> IpcSender<CustomResponseSender> { + self.custom_msg_chan.clone() + } + + pub fn custom_message_port(&self) -> &Receiver<CustomResponseSender> { + &self.custom_msg_port + } + pub fn is_closing(&self) -> bool { self.closing.load(Ordering::SeqCst) } @@ -210,6 +228,21 @@ impl WorkerGlobalScope { } } +impl LoadOrigin for WorkerGlobalScope { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + Some(self.pipeline()) + } +} + impl WorkerGlobalScopeMethods for WorkerGlobalScope { // https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-self fn Self_(&self) -> Root<WorkerGlobalScope> { @@ -236,7 +269,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { let mut rval = RootedValue::new(self.runtime.cx(), UndefinedValue()); for url in urls { - let (url, source) = match load_whole_resource(LoadContext::Script, &self.core_resource_thread, url, None) { + let (url, source) = match load_whole_resource(LoadContext::Script, &self.core_resource_thread, url, self) { Err(_) => return Err(Error::Network), Ok((metadata, bytes)) => { (metadata.final_url, String::from_utf8(bytes).unwrap()) diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index b72d5995b20..567db277b8f 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -44,9 +44,10 @@ use ipc_channel::router::ROUTER; use js::jsapi::JS_ClearPendingException; use js::jsapi::{JSContext, JS_ParseJSON, RootedValue}; use js::jsval::{JSVal, NullValue, UndefinedValue}; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net_traits::CoreResourceMsg::Load; -use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; -use net_traits::{LoadConsumer, LoadContext, LoadData, ResourceCORSData, CoreResourceThread}; +use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError, RequestSource}; +use net_traits::{LoadConsumer, LoadContext, LoadData, ResourceCORSData, CoreResourceThread, LoadOrigin}; use network_listener::{NetworkListener, PreInvoke}; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; @@ -295,6 +296,26 @@ impl XMLHttpRequest { } } +impl LoadOrigin for XMLHttpRequest { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + if self.sync.get() { + RequestSource::None + } else { + self.global().r().request_source() + } + } + fn pipeline_id(&self) -> Option<PipelineId> { + let global = self.global(); + Some(global.r().pipeline()) + } +} + impl XMLHttpRequestMethods for XMLHttpRequest { // https://xhr.spec.whatwg.org/#handler-xhr-onreadystatechange event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange); @@ -572,14 +593,11 @@ impl XMLHttpRequestMethods for XMLHttpRequest { // Step 5 let global = self.global(); - let pipeline_id = global.r().pipeline(); //TODO - set referrer_policy/referrer_url in load_data let mut load_data = LoadData::new(LoadContext::Browsing, self.request_url.borrow().clone().unwrap(), - Some(pipeline_id), - None, - None); + self); if load_data.url.origin().ne(&global.r().get_url().origin()) { load_data.credentials_flag = self.WithCredentials(); } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 960d39cc244..c0f11b2103a 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -65,8 +65,8 @@ use msg::webdriver_msg::WebDriverScriptCommand; use net_traits::LoadData as NetLoadData; use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; -use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadContext, Metadata}; -use net_traits::{ResourceThreads, IpcSend}; +use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadContext, Metadata, ResourceThreads}; +use net_traits::{RequestSource, CustomResponse, CustomResponseSender, IpcSend}; use network_listener::NetworkListener; use parse::ParserRoot; use parse::html::{ParseContext, parse_html}; @@ -205,6 +205,7 @@ enum MixedMessage { FromDevtools(DevtoolScriptControlMsg), FromImageCache(ImageCacheResult), FromScheduler(TimerEvent), + FromNetwork(IpcSender<Option<CustomResponse>>), } /// Messages used to control the script event loop @@ -321,6 +322,12 @@ pub struct ScriptThread { /// events in the event queue. chan: MainThreadScriptChan, + /// A handle to network event messages + custom_message_chan: IpcSender<CustomResponseSender>, + + /// The port which receives a sender from the network + custom_message_port: Receiver<CustomResponseSender>, + dom_manipulation_task_source: DOMManipulationTaskSource, user_interaction_task_source: UserInteractionTaskSource, @@ -537,6 +544,9 @@ impl ScriptThread { let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap(); let devtools_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_devtools_receiver); + let (ipc_custom_resp_chan, ipc_custom_resp_port) = ipc::channel().unwrap(); + let custom_msg_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_custom_resp_port); + // Ask the router to proxy IPC messages from the image cache thread to us. let (ipc_image_cache_channel, ipc_image_cache_port) = ipc::channel().unwrap(); let image_cache_port = @@ -559,6 +569,9 @@ impl ScriptThread { bluetooth_thread: state.bluetooth_thread, port: port, + custom_message_chan: ipc_custom_resp_chan, + custom_message_port: custom_msg_port, + chan: MainThreadScriptChan(chan.clone()), dom_manipulation_task_source: DOMManipulationTaskSource(chan.clone()), user_interaction_task_source: UserInteractionTaskSource(chan.clone()), @@ -620,7 +633,8 @@ impl ScriptThread { /// Handle incoming control messages. fn handle_msgs(&self) -> bool { - use self::MixedMessage::{FromScript, FromConstellation, FromScheduler, FromDevtools, FromImageCache}; + use self::MixedMessage::{FromConstellation, FromDevtools, FromImageCache}; + use self::MixedMessage::{FromScheduler, FromScript, FromNetwork}; // Handle pending resize events. // Gather them first to avoid a double mut borrow on self. @@ -654,6 +668,7 @@ impl ScriptThread { let mut timer_event_port = sel.handle(&self.timer_event_port); let mut devtools_port = sel.handle(&self.devtools_port); let mut image_cache_port = sel.handle(&self.image_cache_port); + let mut custom_message_port = sel.handle(&self.custom_message_port); unsafe { script_port.add(); control_port.add(); @@ -662,6 +677,7 @@ impl ScriptThread { devtools_port.add(); } image_cache_port.add(); + custom_message_port.add(); } let ret = sel.wait(); if ret == script_port.id() { @@ -674,6 +690,8 @@ impl ScriptThread { FromDevtools(self.devtools_port.recv().unwrap()) } else if ret == image_cache_port.id() { FromImageCache(self.image_cache_port.recv().unwrap()) + } else if ret == custom_message_port.id() { + FromNetwork(self.custom_message_port.recv().unwrap()) } else { panic!("unexpected select result") } @@ -736,7 +754,10 @@ impl ScriptThread { Err(_) => match self.timer_event_port.try_recv() { Err(_) => match self.devtools_port.try_recv() { Err(_) => match self.image_cache_port.try_recv() { - Err(_) => break, + Err(_) => match self.custom_message_port.try_recv() { + Err(_) => break, + Ok(ev) => event = FromNetwork(ev) + }, Ok(ev) => event = FromImageCache(ev), }, Ok(ev) => event = FromDevtools(ev), @@ -762,6 +783,7 @@ impl ScriptThread { }, FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg), FromScript(inner_msg) => self.handle_msg_from_script(inner_msg), + FromNetwork(inner_msg) => self.handle_msg_from_network(inner_msg), FromScheduler(inner_msg) => self.handle_timer_event(inner_msg), FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg), FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg), @@ -821,6 +843,7 @@ impl ScriptThread { } }, MixedMessage::FromScheduler(_) => ScriptThreadEventCategory::TimerEvent, + MixedMessage::FromNetwork(_) => ScriptThreadEventCategory::NetworkEvent } } @@ -990,6 +1013,12 @@ impl ScriptThread { msg.responder.unwrap().respond(msg.image_response); } + fn handle_msg_from_network(&self, msg: IpcSender<Option<CustomResponse>>) { + // We may detect controlling service workers here + // We send None as default + let _ = msg.send(None); + } + fn handle_webdriver_msg(&self, pipeline_id: PipelineId, msg: WebDriverScriptCommand) { let context = self.root_browsing_context(); match msg { @@ -1439,6 +1468,7 @@ impl ScriptThread { HistoryTraversalTaskSource(history_sender.clone()), FileReadingTaskSource(file_sender.clone()), self.image_cache_channel.clone(), + self.custom_message_chan.clone(), self.compositor.borrow_mut().clone(), self.image_cache_thread.clone(), self.resource_threads.clone(), @@ -1907,6 +1937,7 @@ impl ScriptThread { credentials_flag: true, referrer_policy: load_data.referrer_policy, referrer_url: load_data.referrer_url, + source: RequestSource::Window(self.custom_message_chan.clone()) }, LoadConsumer::Listener(response_target), None)).unwrap(); self.incomplete_loads.borrow_mut().push(incomplete); diff --git a/tests/unit/net/data_loader.rs b/tests/unit/net/data_loader.rs index 480e48221de..3c423809cc2 100644 --- a/tests/unit/net/data_loader.rs +++ b/tests/unit/net/data_loader.rs @@ -5,11 +5,30 @@ extern crate hyper; use ipc_channel::ipc; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net_traits::LoadConsumer::Channel; use net_traits::ProgressMsg::{Payload, Done}; -use net_traits::{LoadData, LoadContext, NetworkError}; +use net_traits::{LoadData, LoadContext, NetworkError, LoadOrigin, RequestSource}; use self::hyper::header::ContentType; use self::hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; +use url::Url; + +struct DataLoadTest; + +impl LoadOrigin for DataLoadTest { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + None + } +} #[cfg(test)] fn assert_parse(url: &'static str, @@ -20,11 +39,10 @@ fn assert_parse(url: &'static str, use net::mime_classifier::MIMEClassifier; use net::resource_thread::CancellationListener; use std::sync::Arc; - use url::Url; let (start_chan, start_port) = ipc::channel().unwrap(); let classifier = Arc::new(MIMEClassifier::new()); - load(LoadData::new(LoadContext::Browsing, Url::parse(url).unwrap(), None, None, None), + load(LoadData::new(LoadContext::Browsing, Url::parse(url).unwrap(), &DataLoadTest), Channel(start_chan), classifier, CancellationListener::new(None)); diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs index 422a97fdf07..a9aec911d7a 100644 --- a/tests/unit/net/http_loader.rs +++ b/tests/unit/net/http_loader.rs @@ -20,19 +20,58 @@ use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net::cookie::Cookie; use net::cookie_storage::CookieStorage; use net::hsts::HstsEntry; -use net::http_loader::LoadErrorType; -use net::http_loader::{load, LoadError, HttpRequestFactory, HttpRequest, HttpResponse, UIProvider, HttpState}; +use net::http_loader::{LoadErrorType, HttpResponse}; +use net::http_loader::{load, LoadError, HttpRequestFactory, HttpRequest, UIProvider, HttpState}; use net::resource_thread::{AuthCacheEntry, CancellationListener}; +use net_traits::{CustomResponse, RequestSource, Metadata, LoadOrigin}; use net_traits::{LoadData, CookieSource, LoadContext, IncludeSubdomains}; use std::borrow::Cow; use std::io::{self, Write, Read, Cursor}; use std::sync::mpsc::Receiver; use std::sync::{Arc, mpsc, RwLock}; +use std::thread; use url::Url; use util::prefs; const DEFAULT_USER_AGENT: &'static str = "Test-agent"; +struct HttpTest; + +impl LoadOrigin for HttpTest { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + Some(PipelineId::fake_root_pipeline_id()) + } +} + +struct LoadOriginInfo<'a> { + referrer_url: &'a str, + referrer_policy: Option<ReferrerPolicy>, +} + +impl<'a> LoadOrigin for LoadOriginInfo<'a> { + fn referrer_url(&self) -> Option<Url> { + Some(Url::parse(self.referrer_url).unwrap()) + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + self.referrer_policy.clone() + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + None + } +} + fn respond_with(body: Vec<u8>) -> MockResponse { let headers = Headers::new(); respond_with_headers(body, headers) @@ -135,12 +174,27 @@ fn redirect_with_headers(host: String, mut headers: Headers) -> MockResponse { ) } +enum Source { + Window, + Worker +} + +fn respond_404() -> MockResponse { + MockResponse::new( + Headers::new(), + StatusCode::NotFound, + RawStatus(404, Cow::Borrowed("Not Found")), + b"".to_vec() + ) +} + enum ResponseType { Redirect(String), RedirectWithHeaders(String, Headers), Text(Vec<u8>), WithHeaders(Vec<u8>, Headers), NeedsAuth(Headers), + Dummy404 } struct MockRequest { @@ -169,6 +223,9 @@ fn response_for_request_type(t: ResponseType) -> Result<MockResponse, LoadError> }, ResponseType::NeedsAuth(h) => { Ok(basic_auth(h)) + }, + ResponseType::Dummy404 => { + Ok(respond_404()) } } } @@ -330,7 +387,7 @@ fn test_check_default_headers_loaded_in_every_request() { let http_state = HttpState::new(); let ui_provider = TestProvider::new(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = None; load_data.method = Method::Get; @@ -374,7 +431,7 @@ fn test_load_when_request_is_not_get_or_head_and_there_is_no_body_content_length let http_state = HttpState::new(); let ui_provider = TestProvider::new(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = None; load_data.method = Method::Post; @@ -412,7 +469,7 @@ fn test_request_and_response_data_with_network_messages() { let (devtools_chan, devtools_port) = mpsc::channel::<DevtoolsControlMsg>(); // This will probably have to be changed as it uses fake_root_pipeline_id which is marked for removal. let pipeline_id = PipelineId::fake_root_pipeline_id(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), Some(pipeline_id), None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let mut request_headers = Headers::new(); request_headers.set(Host { hostname: "bar.foo".to_owned(), port: None }); load_data.headers = request_headers.clone(); @@ -464,6 +521,22 @@ fn test_request_and_response_data_with_network_messages() { assert_eq!(devhttpresponse, httpresponse); } +struct HttpTestNoPipeline; +impl LoadOrigin for HttpTestNoPipeline { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + None + } +} + #[test] fn test_request_and_response_message_from_devtool_without_pipeline_id() { struct Factory; @@ -485,7 +558,7 @@ fn test_request_and_response_message_from_devtool_without_pipeline_id() { let url = Url::parse("https://mozilla.com").unwrap(); let (devtools_chan, devtools_port) = mpsc::channel::<DevtoolsControlMsg>(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTestNoPipeline); let _ = load(&load_data, &ui_provider, &http_state, Some(devtools_chan), &Factory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); @@ -514,7 +587,7 @@ fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() { } let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.method = Method::Post; @@ -544,7 +617,7 @@ fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_co } let url = Url::parse("http://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -578,7 +651,7 @@ fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_conte } let url = Url::parse("http://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -621,7 +694,7 @@ fn test_load_doesnt_send_request_body_on_any_redirect() { } let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec("Body on POST!".as_bytes())); @@ -653,7 +726,7 @@ fn test_load_doesnt_add_host_to_sts_list_when_url_is_http_even_if_sts_headers_ar let url = Url::parse("http://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -685,7 +758,7 @@ fn test_load_adds_host_to_sts_list_when_url_is_https_and_sts_headers_are_present let url = Url::parse("https://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -722,7 +795,7 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ assert_cookie_for_domain(http_state.cookie_jar.clone(), "http://mozilla.com", ""); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let _ = load(&load_data, &ui_provider, &http_state, @@ -738,7 +811,7 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_resource_manager() { let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); let http_state = HttpState::new(); @@ -794,7 +867,7 @@ fn test_load_sends_secure_cookie_if_http_changed_to_https_due_to_entry_in_hsts_s cookie_jar.push(cookie, CookieSource::HTTP); } - let mut load_data = LoadData::new(LoadContext::Browsing, url, None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url, &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); let mut headers = Headers::new(); @@ -826,7 +899,7 @@ fn test_load_sends_cookie_if_nonhttp() { cookie_jar.push(cookie, CookieSource::HTTP); } - let mut load_data = LoadData::new(LoadContext::Browsing, url, None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url, &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); let mut headers = Headers::new(); @@ -860,7 +933,7 @@ fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl( let http_state = HttpState::new(); let ui_provider = TestProvider::new(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let _ = load(&load_data, &ui_provider, &http_state, None, @@ -890,7 +963,7 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() { let http_state = HttpState::new(); let ui_provider = TestProvider::new(); - let load_data = LoadData::new(LoadContext::Browsing, Url::parse("http://mozilla.com").unwrap(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, Url::parse("http://mozilla.com").unwrap(), &HttpTest); let _ = load(&load_data, &ui_provider, &http_state, None, @@ -921,7 +994,7 @@ fn test_when_cookie_set_marked_httpsonly_secure_isnt_sent_on_http_request() { cookie_jar.push(cookie, CookieSource::HTTP); } - let mut load_data = LoadData::new(LoadContext::Browsing, url, None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url, &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); assert_cookie_for_domain(http_state.cookie_jar.clone(), "https://mozilla.com", "mozillaIs=theBest"); @@ -939,7 +1012,7 @@ fn test_load_sets_content_length_to_length_of_request_body() { let content = "This is a request body"; let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec(content.as_bytes())); @@ -965,7 +1038,7 @@ fn test_load_uses_explicit_accept_from_headers_in_load_data() { accept_headers.set(Accept(vec![text_html.clone()])); let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); load_data.headers.set(Accept(vec![text_html.clone()])); @@ -994,7 +1067,7 @@ fn test_load_sets_default_accept_to_html_xhtml_xml_and_then_anything_else() { ])); let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); @@ -1017,7 +1090,7 @@ fn test_load_uses_explicit_accept_encoding_from_load_data_headers() { accept_encoding_headers.set(AcceptEncoding(vec![qitem(Encoding::Chunked)])); let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); load_data.headers.set(AcceptEncoding(vec![qitem(Encoding::Chunked)])); @@ -1042,7 +1115,7 @@ fn test_load_sets_default_accept_encoding_to_gzip_and_deflate() { qitem(Encoding::EncodingExt("br".to_owned()))])); let url = Url::parse("http://mozilla.com").unwrap(); - let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); let http_state = HttpState::new(); @@ -1077,7 +1150,7 @@ fn test_load_errors_when_there_a_redirect_loop() { } let url = Url::parse("http://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1106,7 +1179,7 @@ fn test_load_errors_when_there_is_too_many_redirects() { } let url = Url::parse("http://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1153,7 +1226,7 @@ fn test_load_follows_a_redirect() { } let url = Url::parse("http://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1180,7 +1253,7 @@ impl HttpRequestFactory for DontConnectFactory { #[test] fn test_load_errors_when_scheme_is_not_http_or_https() { let url = Url::parse("ftp://not-supported").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1199,7 +1272,7 @@ fn test_load_errors_when_scheme_is_not_http_or_https() { #[test] fn test_load_errors_when_viewing_source_and_inner_url_scheme_is_not_http_or_https() { let url = Url::parse("view-source:ftp://not-supported").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1242,7 +1315,7 @@ fn test_load_errors_when_cancelled() { cancel_sender.send(()).unwrap(); let url = Url::parse("https://mozilla.com").unwrap(); - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1291,7 +1364,7 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { } } - let load_data = LoadData::new(LoadContext::Browsing, url_x.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url_x.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1357,7 +1430,7 @@ fn test_redirect_from_x_to_x_provides_x_with_cookie_from_first_response() { } } - let load_data = LoadData::new(LoadContext::Browsing, url.clone(), None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); let http_state = HttpState::new(); let ui_provider = TestProvider::new(); @@ -1390,7 +1463,7 @@ fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { http_state.auth_cache.write().unwrap().entries.insert(url.clone(), auth_entry); - let mut load_data = LoadData::new(LoadContext::Browsing, url, None, None, None); + let mut load_data = LoadData::new(LoadContext::Browsing, url, &HttpTest); load_data.credentials_flag = true; let mut auth_header = Headers::new(); @@ -1429,7 +1502,7 @@ fn test_auth_ui_sets_header_on_401() { ) ); - let load_data = LoadData::new(LoadContext::Browsing, url, None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url, &HttpTest); match load( &load_data, &ui_provider, &http_state, @@ -1465,7 +1538,7 @@ fn test_auth_ui_needs_www_auth() { } } - let load_data = LoadData::new(LoadContext::Browsing, url, None, None, None); + let load_data = LoadData::new(LoadContext::Browsing, url, &HttpTest); let response = load(&load_data, &AuthProvider, &http_state, None, &Factory, DEFAULT_USER_AGENT.to_owned(), @@ -1478,19 +1551,15 @@ fn test_auth_ui_needs_www_auth() { } } -fn assert_referer_header_matches(request_url: &str, - referrer_url: &str, - referrer_policy: Option<ReferrerPolicy>, +fn assert_referer_header_matches(origin_info: &LoadOrigin, + request_url: &str, expected_referrer: &str) { - let ref_url = Url::parse(referrer_url).unwrap(); let url = Url::parse(request_url).unwrap(); let ui_provider = TestProvider::new(); let load_data = LoadData::new(LoadContext::Browsing, url.clone(), - None, - referrer_policy, - Some(ref_url)); + origin_info); let mut referer_headers = Headers::new(); referer_headers.set(Referer(expected_referrer.to_owned())); @@ -1505,16 +1574,13 @@ fn assert_referer_header_matches(request_url: &str, &CancellationListener::new(None)); } -fn assert_referer_header_not_included(request_url: &str, referrer_url: &str, referrer_policy: Option<ReferrerPolicy>) { - let ref_url = Url::parse(referrer_url).unwrap(); +fn assert_referer_header_not_included(origin_info: &LoadOrigin, request_url: &str) { let url = Url::parse(request_url).unwrap(); let ui_provider = TestProvider::new(); let load_data = LoadData::new(LoadContext::Browsing, url.clone(), - None, - referrer_policy, - Some(ref_url)); + origin_info); let http_state = HttpState::new(); @@ -1533,17 +1599,27 @@ fn test_referer_set_to_origin_with_originonly_policy() { let referrer_policy = Some(ReferrerPolicy::OriginOnly); let expected_referrer = "http://someurl.com/"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] fn test_referer_set_to_stripped_url_with_unsafeurl_policy() { + let request_url = "http://mozilla.com"; let referrer_url = "http://username:password@someurl.com/some/path#fragment"; let referrer_policy = Some(ReferrerPolicy::UnsafeUrl); let expected_referrer = "http://someurl.com/some/path"; + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1553,7 +1629,12 @@ fn test_referer_with_originwhencrossorigin_policy_cross_orig() { let referrer_policy = Some(ReferrerPolicy::OriginWhenCrossOrigin); let expected_referrer = "http://someurl.com/"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1563,7 +1644,12 @@ fn test_referer_with_originwhencrossorigin_policy_same_orig() { let referrer_policy = Some(ReferrerPolicy::OriginWhenCrossOrigin); let expected_referrer = "http://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1573,7 +1659,12 @@ fn test_http_to_https_considered_cross_origin_for_referer_header_logic() { let referrer_policy = Some(ReferrerPolicy::OriginWhenCrossOrigin); let expected_referrer = "http://mozilla.com/"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1583,7 +1674,12 @@ fn test_referer_set_to_ref_url_with_noreferrerwhendowngrade_policy_https_to_http let referrer_policy = Some(ReferrerPolicy::NoRefWhenDowngrade); let expected_referrer = "https://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy, + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1592,7 +1688,12 @@ fn test_no_referer_set_with_noreferrerwhendowngrade_policy_https_to_http() { let referrer_url = "https://username:password@mozilla.com/some/path#fragment"; let referrer_policy = Some(ReferrerPolicy::NoRefWhenDowngrade); - assert_referer_header_not_included(request_url, referrer_url, referrer_policy) + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_not_included(&origin_info, request_url) } #[test] @@ -1602,7 +1703,12 @@ fn test_referer_set_to_ref_url_with_noreferrerwhendowngrade_policy_http_to_https let referrer_policy = Some(ReferrerPolicy::NoRefWhenDowngrade); let expected_referrer = "http://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1612,7 +1718,12 @@ fn test_referer_set_to_ref_url_with_noreferrerwhendowngrade_policy_http_to_http( let referrer_policy = Some(ReferrerPolicy::NoRefWhenDowngrade); let expected_referrer = "http://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1622,7 +1733,12 @@ fn test_no_referrer_policy_follows_noreferrerwhendowngrade_https_to_https() { let referrer_policy = None; let expected_referrer = "https://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1631,7 +1747,12 @@ fn test_no_referrer_policy_follows_noreferrerwhendowngrade_https_to_http() { let referrer_url = "https://username:password@mozilla.com/some/path#fragment"; let referrer_policy = None; - assert_referer_header_not_included(request_url, referrer_url, referrer_policy); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_not_included(&origin_info, request_url); } #[test] @@ -1641,7 +1762,12 @@ fn test_no_referrer_policy_follows_noreferrerwhendowngrade_http_to_https() { let referrer_policy = None; let expected_referrer = "http://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] @@ -1651,14 +1777,87 @@ fn test_no_referrer_policy_follows_noreferrerwhendowngrade_http_to_http() { let referrer_policy = None; let expected_referrer = "http://mozilla.com/some/path"; - assert_referer_header_matches(request_url, referrer_url, referrer_policy, expected_referrer); + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy + }; + + assert_referer_header_matches(&origin_info, request_url, expected_referrer); } #[test] fn test_no_referer_set_with_noreferrer_policy() { + let request_url = "http://mozilla.com"; let referrer_url = "http://someurl.com"; let referrer_policy = Some(ReferrerPolicy::NoReferrer); - assert_referer_header_not_included(request_url, referrer_url, referrer_policy) + let origin_info = LoadOriginInfo { + referrer_url: referrer_url, + referrer_policy: referrer_policy, + }; + + assert_referer_header_not_included(&origin_info, request_url) +} + +fn load_request_with_source(source: Source, expected_body: Vec<u8>) -> (Metadata, String) { + use ipc_channel::ipc; + let (sender, receiver) = ipc::channel().unwrap(); + + struct Factory; + impl HttpRequestFactory for Factory { + type R = MockRequest; + fn create(&self, _: Url, _: Method, _: Headers) -> Result<MockRequest, LoadError> { + Ok(MockRequest::new(ResponseType::Dummy404)) + } + } + + let mock_response = CustomResponse::new( + Headers::new(), + RawStatus(200, Cow::Borrowed("OK")), + expected_body + ); + let url = Url::parse("http://mozilla.com").unwrap(); + let http_state = HttpState::new(); + let ui_provider = TestProvider::new(); + let mut load_data = LoadData::new(LoadContext::Browsing, url.clone(), &HttpTest); + + match source { + Source::Window => load_data.source = RequestSource::Window(sender.clone()), + Source::Worker => load_data.source = RequestSource::Worker(sender.clone()), + } + + let join_handle = thread::spawn(move || { + let response = load(&load_data.clone(), &ui_provider, &http_state, + None, &Factory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)); + match response { + Ok(mut response) => { + let metadata = response.metadata.clone(); + let body = read_response(&mut response); + (metadata, body) + } + Err(e) => panic!("Error Getting Response: {:?}", e) + } + }); + + let network_sender = receiver.recv().unwrap(); + network_sender.send(Some(mock_response)).unwrap(); + let (metadata, body) = join_handle.join().unwrap(); + (metadata, body) +} + +#[test] +fn test_custom_response_from_window() { + let expected_body = b"Yay! From Window".to_vec(); + let (metadata, body) = load_request_with_source(Source::Window, expected_body.clone()); + assert_eq!(metadata.status, Some(RawStatus(200, Cow::Borrowed("OK")))); + assert_eq!(body, String::from_utf8(expected_body).unwrap()); +} + +#[test] +fn test_custom_response_from_worker() { + let expected_body = b"Yay! From Worker".to_vec(); + let (metadata, body) = load_request_with_source(Source::Worker, expected_body.clone()); + assert_eq!(metadata.status, Some(RawStatus(200, Cow::Borrowed("OK")))); + assert_eq!(body, String::from_utf8(expected_body).unwrap()); } diff --git a/tests/unit/net/resource_thread.rs b/tests/unit/net/resource_thread.rs index b4451448033..251f0b3306b 100644 --- a/tests/unit/net/resource_thread.rs +++ b/tests/unit/net/resource_thread.rs @@ -3,9 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use ipc_channel::ipc; +use msg::constellation_msg::{PipelineId, ReferrerPolicy}; use net::resource_thread::new_core_resource_thread; use net_traits::hosts::{parse_hostsfile, host_replacement}; -use net_traits::{CoreResourceMsg, LoadData, LoadConsumer, LoadContext, NetworkError, ProgressMsg}; +use net_traits::{CoreResourceMsg, LoadData, LoadConsumer, LoadContext}; +use net_traits::{NetworkError, ProgressMsg, LoadOrigin, RequestSource}; use profile_traits::time::ProfilerChan; use std::borrow::ToOwned; use std::collections::HashMap; @@ -17,6 +19,23 @@ fn ip(s: &str) -> IpAddr { s.parse().unwrap() } +struct ResourceTest; + +impl LoadOrigin for ResourceTest { + fn referrer_url(&self) -> Option<Url> { + None + } + fn referrer_policy(&self) -> Option<ReferrerPolicy> { + None + } + fn request_source(&self) -> RequestSource { + RequestSource::None + } + fn pipeline_id(&self) -> Option<PipelineId> { + None + } +} + #[test] fn test_exit() { let (tx, _rx) = ipc::channel().unwrap(); @@ -30,7 +49,7 @@ fn test_bad_scheme() { let resource_thread = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx)); let (start_chan, start) = ipc::channel().unwrap(); let url = Url::parse("bogus://whatever").unwrap(); - resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, None, None, None), + resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest), LoadConsumer::Channel(start_chan), None)).unwrap(); let response = start.recv().unwrap(); @@ -210,7 +229,7 @@ fn test_cancelled_listener() { let (sync_sender, sync_receiver) = ipc::channel().unwrap(); let url = Url::parse(&format!("http://127.0.0.1:{}", port)).unwrap(); - resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, None, None, None), + resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest), LoadConsumer::Channel(sender), Some(id_sender))).unwrap(); // get the `ResourceId` and send a cancel message, which should stop the loading loop |