diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 315702902b9e..0aa732a2418e 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -183,15 +183,21 @@ impl FontCache { ROUTER.add_route(data_receiver.to_opaque(), box move |message| { let response: ResponseAction = message.to().unwrap(); match response { - ResponseAction::HeadersAvailable(metadata) => { - let is_response_valid = - metadata.content_type.as_ref().map_or(false, |content_type| { - let mime = &content_type.0; - is_supported_font_type(&mime.0, &mime.1) - }); - info!("{} font with MIME type {:?}", + ResponseAction::HeadersAvailable(meta_result) => { + let is_response_valid = match meta_result { + Ok(ref metadata) => { + metadata.content_type.as_ref().map_or(false, |content_type| { + let mime = &content_type.0; + is_supported_font_type(&mime.0, &mime.1) + }) + } + Err(_) => false, + }; + + info!("{} font with MIME type {}", if is_response_valid { "Loading" } else { "Ignoring" }, - metadata.content_type); + meta_result.map(|ref meta| format!("{:?}", meta.content_type)) + .unwrap_or(format!(""))); *response_valid.lock().unwrap() = is_response_valid; } ResponseAction::DataAvailable(new_bytes) => { diff --git a/components/net/about_loader.rs b/components/net/about_loader.rs index 5fbaaa686f6b..afc089c625ae 100644 --- a/components/net/about_loader.rs +++ b/components/net/about_loader.rs @@ -9,12 +9,19 @@ use hyper::mime::{Mime, SubLevel, TopLevel}; use mime_classifier::MIMEClassifier; use net_traits::ProgressMsg::Done; use net_traits::response::HttpsState; -use net_traits::{LoadConsumer, LoadData, Metadata}; +use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError}; use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt}; use std::sync::Arc; use url::Url; use util::resource_files::resources_dir_path; +fn url_from_non_relative_scheme(load_data: &mut LoadData, filename: &str) { + let mut path = resources_dir_path(); + path.push(filename); + assert!(path.exists()); + load_data.url = Url::from_file_path(&*path).unwrap(); +} + pub fn factory(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc, @@ -41,15 +48,11 @@ pub fn factory(mut load_data: LoadData, return } "crash" => panic!("Loading the about:crash URL."), - "failure" | "not-found" => { - let mut path = resources_dir_path(); - let file_name = non_relative_scheme_data.to_owned() + ".html"; - path.push(&file_name); - assert!(path.exists()); - load_data.url = Url::from_file_path(&*path).unwrap(); - } + "failure" | "not-found" => + url_from_non_relative_scheme(&mut load_data, &(non_relative_scheme_data.to_owned() + ".html")), + "sslfail" => url_from_non_relative_scheme(&mut load_data, "badcert.html"), _ => { - send_error(load_data.url, "Unknown about: URL.".to_owned(), start_chan); + send_error(load_data.url, NetworkError::Internal("Unknown about: URL.".to_owned()), start_chan); return } }; diff --git a/components/net/chrome_loader.rs b/components/net/chrome_loader.rs new file mode 100644 index 000000000000..f18792de0213 --- /dev/null +++ b/components/net/chrome_loader.rs @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use file_loader; +use mime_classifier::MIMEClassifier; +use net_traits::{LoadConsumer, LoadData, NetworkError}; +use resource_thread::{CancellationListener, send_error}; +use std::path::Path; +use std::sync::Arc; +use url::Url; +use util::resource_files::resources_dir_path; + +pub fn resolve_chrome_url(url: &Url) -> Result { + assert_eq!(url.scheme, "chrome"); + // Skip the initial // + let non_relative_scheme_data = &url.non_relative_scheme_data().unwrap()[2..]; + let relative_path = Path::new(non_relative_scheme_data); + // Don't allow chrome URLs access to files outside of the resources directory. + if non_relative_scheme_data.find("..").is_some() || + relative_path.is_absolute() || + relative_path.has_root() { + return Err(()); + } + + let mut path = resources_dir_path(); + path.push(non_relative_scheme_data); + assert!(path.exists()); + return Ok(Url::from_file_path(&*path).unwrap()); +} + +pub fn factory(mut load_data: LoadData, + start_chan: LoadConsumer, + classifier: Arc, + cancel_listener: CancellationListener) { + let file_url = match resolve_chrome_url(&load_data.url) { + Ok(url) => url, + Err(_) => { + send_error(load_data.url, + NetworkError::Internal("Invalid chrome URL.".to_owned()), + start_chan); + return; + } + }; + load_data.url = file_url; + file_loader::factory(load_data, start_chan, classifier, cancel_listener) +} diff --git a/components/net/data_loader.rs b/components/net/data_loader.rs index 50452302627b..ad881ae40444 100644 --- a/components/net/data_loader.rs +++ b/components/net/data_loader.rs @@ -6,7 +6,7 @@ use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; use mime_classifier::MIMEClassifier; use net_traits::LoadConsumer; use net_traits::ProgressMsg::{Payload, Done}; -use net_traits::{LoadData, Metadata}; +use net_traits::{LoadData, Metadata, NetworkError}; use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt}; use rustc_serialize::base64::FromBase64; use std::sync::Arc; @@ -110,7 +110,9 @@ pub fn load(load_data: LoadData, let _ = chan.send(Done(Ok(()))); } }, - Err(DecodeError::InvalidDataUri) => send_error(url, "invalid data uri".to_owned(), start_chan), - Err(DecodeError::NonBase64DataUri) => send_error(url, "non-base64 data uri".to_owned(), start_chan), + Err(DecodeError::InvalidDataUri) => + send_error(url, NetworkError::Internal("invalid data uri".to_owned()), start_chan), + Err(DecodeError::NonBase64DataUri) => + send_error(url, NetworkError::Internal("non-base64 data uri".to_owned()), start_chan), } } diff --git a/components/net/file_loader.rs b/components/net/file_loader.rs index b3b905c7dcbf..ac1c881768b6 100644 --- a/components/net/file_loader.rs +++ b/components/net/file_loader.rs @@ -6,7 +6,7 @@ use about_loader; use mime_classifier::MIMEClassifier; use mime_guess::guess_mime_type; use net_traits::ProgressMsg::{Done, Payload}; -use net_traits::{LoadConsumer, LoadData, Metadata}; +use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError}; use resource_thread::{CancellationListener, ProgressSender}; use resource_thread::{send_error, start_sending_sniffed_opt}; use std::borrow::ToOwned; @@ -50,7 +50,7 @@ fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: ReadStatus::EOF => return Ok(LoadResult::Finished), } } - let _ = progress_chan.send(Done(Err("load cancelled".to_owned()))); + let _ = progress_chan.send(Done(Err(NetworkError::Internal("load cancelled".to_owned())))); Ok(LoadResult::Cancelled) } @@ -72,7 +72,7 @@ pub fn factory(load_data: LoadData, let file_path = match load_data.url.to_file_path() { Ok(file_path) => file_path, Err(_) => { - send_error(load_data.url, "Could not parse path".to_owned(), senders); + send_error(load_data.url, NetworkError::Internal("Could not parse path".to_owned()), senders); return; }, }; @@ -92,7 +92,7 @@ pub fn factory(load_data: LoadData, if cancel_listener.is_cancelled() { if let Ok(progress_chan) = get_progress_chan(load_data, file_path, senders, classifier, &[]) { - let _ = progress_chan.send(Done(Err("load cancelled".to_owned()))); + let _ = progress_chan.send(Done(Err(NetworkError::Internal("load cancelled".to_owned())))); } return; } @@ -116,7 +116,7 @@ pub fn factory(load_data: LoadData, } } Err(e) => { - send_error(load_data.url, e, senders); + send_error(load_data.url, NetworkError::Internal(e), senders); } } }); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 5473a83fce54..b9caf61446ad 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -8,7 +8,6 @@ use cookie; use cookie_storage::CookieStorage; use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest}; use devtools_traits::{HttpResponse as DevtoolsHttpResponse, NetworkEvent}; -use file_loader; use flate2::read::{DeflateDecoder, GzDecoder}; use hsts::{HstsEntry, HstsList, secure_url}; use hyper::Error as HttpError; @@ -28,7 +27,8 @@ use msg::constellation_msg::{PipelineId}; 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, Metadata}; +use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData}; +use net_traits::{Metadata, NetworkError}; use openssl::ssl::error::{SslError, OpensslError}; use openssl::ssl::{SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_VERIFY_PEER, SslContext, SslMethod}; use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt, AuthCacheEntry}; @@ -158,32 +158,15 @@ fn load_for_consumer(load_data: LoadData, match load(&load_data, &ui_provider, &http_state, devtools_chan, &factory, user_agent, &cancel_listener) { - Err(LoadError::UnsupportedScheme(url)) => { - let s = format!("{} request, but we don't support that scheme", &*url.scheme); - send_error(url, s, start_chan) - } - Err(LoadError::Connection(url, e)) => { - send_error(url, e, start_chan) - } - Err(LoadError::MaxRedirects(url, _)) => { - send_error(url, "too many redirects".to_owned(), start_chan) - } - Err(LoadError::Cors(url, msg)) | - Err(LoadError::Cancelled(url, msg)) | - Err(LoadError::InvalidRedirect(url, msg)) | - Err(LoadError::Decoding(url, msg)) => { - send_error(url, msg, start_chan) - } - Err(LoadError::Ssl(url, msg)) => { - info!("ssl validation error {}, '{}'", url.serialize(), msg); - - let mut image = resources_dir_path(); - image.push("badcert.html"); - let load_data = LoadData::new(load_data.context, Url::from_file_path(&*image).unwrap(), None); - - file_loader::factory(load_data, start_chan, classifier, cancel_listener) + Err(error) => { + match error.error { + LoadErrorType::ConnectionAborted => unreachable!(), + LoadErrorType::Ssl => send_error(error.url.clone(), + NetworkError::SslValidation(error.url), + start_chan), + _ => send_error(error.url, NetworkError::Internal(error.reason), start_chan) + } } - Err(LoadError::ConnectionAborted(_)) => unreachable!(), Ok(mut load_response) => { let metadata = load_response.metadata.clone(); send_data(load_data.context, &mut load_response, start_chan, metadata, classifier, &cancel_listener) @@ -268,20 +251,15 @@ impl HttpRequestFactory for NetworkHttpRequestFactory { let error: &(Error + Send + 'static) = &**error; if let Some(&SslError::OpenSslErrors(ref errors)) = error.downcast_ref::() { if errors.iter().any(is_cert_verify_error) { - return Err( - LoadError::Ssl(url, format!("ssl error: {:?} {:?}", - error.description(), - error.cause()))); + let msg = format!("ssl error: {:?} {:?}", error.description(), error.cause()); + return Err(LoadError::new(url, LoadErrorType::Ssl, msg)); } } } let mut request = match connection { Ok(req) => req, - - Err(e) => { - return Err(LoadError::Connection(url, e.description().to_owned())) - } + Err(e) => return Err(LoadError::new(url, LoadErrorType::Connection, e.description().to_owned())), }; *request.headers_mut() = headers; @@ -306,21 +284,23 @@ impl HttpRequest for WrappedHttpRequest { let url = self.request.url.clone(); let mut request_writer = match self.request.start() { Ok(streaming) => streaming, - Err(e) => return Err(LoadError::Connection(url, e.description().to_owned())) + Err(e) => return Err(LoadError::new(url, LoadErrorType::Connection, e.description().to_owned())), }; if let Some(ref data) = *body { if let Err(e) = request_writer.write_all(&data) { - return Err(LoadError::Connection(url, e.description().to_owned())) + return Err(LoadError::new(url, LoadErrorType::Connection, e.description().to_owned())) } } let response = match request_writer.send() { Ok(w) => w, Err(HttpError::Io(ref io_error)) if io_error.kind() == io::ErrorKind::ConnectionAborted => { - return Err(LoadError::ConnectionAborted(io_error.description().to_owned())); + return Err(LoadError::new(url, LoadErrorType::ConnectionAborted, + io_error.description().to_owned())); }, - Err(e) => return Err(LoadError::Connection(url, e.description().to_owned())) + Err(e) => return Err(LoadError::new(url, LoadErrorType::Connection, + e.description().to_owned())), }; Ok(WrappedHttpResponse { response: response }) @@ -328,16 +308,33 @@ impl HttpRequest for WrappedHttpRequest { } #[derive(Debug)] -pub enum LoadError { - UnsupportedScheme(Url), - Connection(Url, String), - Cors(Url, String), - Ssl(Url, String), - InvalidRedirect(Url, String), - Decoding(Url, String), - MaxRedirects(Url, u32), // u32 indicates number of redirects that occurred - ConnectionAborted(String), - Cancelled(Url, String), +pub struct LoadError { + pub url: Url, + pub error: LoadErrorType, + pub reason: String, +} + +impl LoadError { + pub fn new(url: Url, error: LoadErrorType, reason: String) -> LoadError { + LoadError { + url: url, + error: error, + reason: reason, + } + } +} + +#[derive(Eq, PartialEq, Debug)] +pub enum LoadErrorType { + Cancelled, + Connection, + ConnectionAborted, + Cors, + Decoding, + InvalidRedirect, + MaxRedirects(u32), // u32 indicates number of redirects that occurred + Ssl, + UnsupportedScheme, } fn set_default_accept_encoding(headers: &mut Headers) { @@ -456,12 +453,8 @@ impl StreamedResponse { Some(Encoding::Gzip) => { let result = GzDecoder::new(response); match result { - Ok(response_decoding) => { - Ok(StreamedResponse::new(m, Decoder::Gzip(response_decoding))) - } - Err(err) => { - Err(LoadError::Decoding(m.final_url, err.to_string())) - } + Ok(response_decoding) => Ok(StreamedResponse::new(m, Decoder::Gzip(response_decoding))), + Err(err) => Err(LoadError::new(m.final_url, LoadErrorType::Decoding, err.to_string())), } } Some(Encoding::Deflate) => { @@ -670,7 +663,7 @@ pub fn obtain_response(request_factory: &HttpRequestFactory, headers.clone())); if cancel_listener.is_cancelled() { - return Err(LoadError::Cancelled(connection_url.clone(), "load cancelled".to_owned())); + return Err(LoadError::new(connection_url.clone(), LoadErrorType::Cancelled, "load cancelled".to_owned())); } let maybe_response = req.send(request_body); @@ -685,11 +678,14 @@ pub fn obtain_response(request_factory: &HttpRequestFactory, response = match maybe_response { Ok(r) => r, - Err(LoadError::ConnectionAborted(reason)) => { - debug!("connection aborted ({:?}), possibly stale, trying new connection", reason); - continue; - } - Err(e) => return Err(e), + Err(e) => { + if let LoadErrorType::ConnectionAborted = e.error { + debug!("connection aborted ({:?}), possibly stale, trying new connection", e.reason); + continue; + } else { + return Err(e) + } + }, }; // if no ConnectionAborted, break the loop @@ -736,7 +732,7 @@ pub fn load(load_data: &LoadData, let mut new_auth_header: Option> = None; if cancel_listener.is_cancelled() { - return Err(LoadError::Cancelled(doc_url, "load cancelled".to_owned())); + return Err(LoadError::new(doc_url, LoadErrorType::Cancelled, "load cancelled".to_owned())); } // If the URL is a view-source scheme then the scheme data contains the @@ -758,15 +754,17 @@ pub fn load(load_data: &LoadData, } if iters > max_redirects { - return Err(LoadError::MaxRedirects(doc_url, iters - 1)); + return Err(LoadError::new(doc_url, LoadErrorType::MaxRedirects(iters - 1), + "too many redirects".to_owned())); } if &*doc_url.scheme != "http" && &*doc_url.scheme != "https" { - return Err(LoadError::UnsupportedScheme(doc_url)); + let s = format!("{} request, but we don't support that scheme", &*doc_url.scheme); + return Err(LoadError::new(doc_url, LoadErrorType::UnsupportedScheme, s)); } if cancel_listener.is_cancelled() { - return Err(LoadError::Cancelled(doc_url, "load cancelled".to_owned())); + return Err(LoadError::new(doc_url, LoadErrorType::Cancelled, "load cancelled".to_owned())); } info!("requesting {}", doc_url.serialize()); @@ -832,10 +830,9 @@ pub fn load(load_data: &LoadData, // CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10) if let Some(ref c) = load_data.cors { if c.preflight { - return Err( - LoadError::Cors( - doc_url, - "Preflight fetch inconsistent with main fetch".to_owned())); + return Err(LoadError::new(doc_url, + LoadErrorType::Cors, + "Preflight fetch inconsistent with main fetch".to_owned())); } else { // XXXManishearth There are some CORS-related steps here, // but they don't seem necessary until credentials are implemented @@ -844,9 +841,7 @@ pub fn load(load_data: &LoadData, let new_doc_url = match doc_url.join(&new_url) { Ok(u) => u, - Err(e) => { - return Err(LoadError::InvalidRedirect(doc_url, e.to_string())); - } + Err(e) => return Err(LoadError::new(doc_url, LoadErrorType::InvalidRedirect, e.to_string())), }; // According to https://tools.ietf.org/html/rfc7231#section-6.4.2, @@ -858,7 +853,7 @@ pub fn load(load_data: &LoadData, } if redirected_to.contains(&new_doc_url) { - return Err(LoadError::InvalidRedirect(doc_url, "redirect loop".to_owned())); + return Err(LoadError::new(doc_url, LoadErrorType::InvalidRedirect, "redirect loop".to_owned())); } info!("redirecting to {}", new_doc_url); @@ -921,7 +916,7 @@ fn send_data(context: LoadContext, loop { if cancel_listener.is_cancelled() { - let _ = progress_chan.send(Done(Err("load cancelled".to_owned()))); + let _ = progress_chan.send(Done(Err(NetworkError::Internal("load cancelled".to_owned())))); return; } diff --git a/components/net/image_cache_thread.rs b/components/net/image_cache_thread.rs index 91a0cf0dc32f..660ba87cb4e5 100644 --- a/components/net/image_cache_thread.rs +++ b/components/net/image_cache_thread.rs @@ -10,7 +10,7 @@ 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, ControlMsg, LoadConsumer, LoadData, ResourceThread}; -use net_traits::{ResponseAction, LoadContext}; +use net_traits::{ResponseAction, LoadContext, NetworkError}; use std::borrow::ToOwned; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -44,7 +44,7 @@ struct PendingLoad { metadata: Option, // Once loading is complete, the result of the operation. - result: Option>, + result: Option>, listeners: Vec, // The url being loaded. Do not forget that this may be several Mb diff --git a/components/net/lib.rs b/components/net/lib.rs index a784485f887f..964c0eb842ab 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -39,6 +39,7 @@ extern crate webrender_traits; extern crate websocket; pub mod about_loader; +pub mod chrome_loader; pub mod cookie; pub mod cookie_storage; pub mod data_loader; diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 0bc6f19e053e..5d51b48206d3 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -5,6 +5,7 @@ //! A thread that takes a URL and streams back the binary data. use about_loader; +use chrome_loader; use cookie; use cookie_storage::CookieStorage; use data_loader; @@ -21,7 +22,7 @@ use net_traits::LoadContext; use net_traits::ProgressMsg::Done; use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResourceThread, ResponseAction}; use net_traits::{ControlMsg, CookieSource, LoadConsumer, LoadData, LoadResponse, ResourceId}; -use net_traits::{WebSocketCommunicate, WebSocketConnectData}; +use net_traits::{NetworkError, WebSocketCommunicate, WebSocketConnectData}; use std::borrow::ToOwned; use std::boxed::FnBox; use std::cell::Cell; @@ -55,11 +56,11 @@ impl ProgressSender { } } -pub fn send_error(url: Url, err: String, start_chan: LoadConsumer) { +pub fn send_error(url: Url, err: NetworkError, start_chan: LoadConsumer) { let mut metadata: Metadata = Metadata::default(url); metadata.status = None; - if let Ok(p) = start_sending_opt(start_chan, metadata) { + if let Ok(p) = start_sending_opt(start_chan, metadata, Some(err.clone())) { p.send(Done(Err(err))).unwrap(); } } @@ -107,16 +108,19 @@ pub fn start_sending_sniffed_opt(start_chan: LoadConsumer, mut metadata: Metadat metadata.content_type = Some(ContentType(Mime(mime_tp, mime_sb, vec![]))); } - start_sending_opt(start_chan, metadata) + start_sending_opt(start_chan, metadata, None) } /// For use by loaders in responding to a Load message. -fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata) -> Result { +/// It takes an optional NetworkError, so that we can extract the SSL Validation errors +/// and take it to the HTML parser +fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata, + network_error: Option) -> Result { match start_chan { LoadConsumer::Channel(start_chan) => { let (progress_chan, progress_port) = ipc::channel().unwrap(); let result = start_chan.send(LoadResponse { - metadata: metadata, + metadata: metadata, progress_port: progress_port, }); match result { @@ -125,7 +129,13 @@ fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata) -> Result { - target.invoke_with_listener(ResponseAction::HeadersAvailable(metadata)); + match network_error { + Some(NetworkError::SslValidation(url)) => { + let error = NetworkError::SslValidation(url); + target.invoke_with_listener(ResponseAction::HeadersAvailable(Err(error))); + } + _ => target.invoke_with_listener(ResponseAction::HeadersAvailable(Ok(metadata))), + } Ok(ProgressSender::Listener(target)) } } @@ -323,6 +333,7 @@ impl ResourceManager { let cancel_listener = CancellationListener::new(cancel_resource); let loader = match &*load_data.url.scheme { + "chrome" => from_factory(chrome_loader::factory), "file" => from_factory(file_loader::factory), "http" | "https" | "view-source" => { let http_state = HttpState { @@ -339,7 +350,7 @@ impl ResourceManager { "about" => from_factory(about_loader::factory), _ => { debug!("resource_thread: no loader for scheme {}", load_data.url.scheme); - send_error(load_data.url, "no loader for scheme".to_owned(), consumer); + send_error(load_data.url, NetworkError::Internal("no loader for scheme".to_owned()), consumer); return } }; diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 819d0a80335c..6d4bc855d19e 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -113,13 +113,13 @@ pub trait AsyncFetchListener { /// A listener for asynchronous network events. Cancelling the underlying request is unsupported. pub trait AsyncResponseListener { /// The response headers for a request have been received. - fn headers_available(&mut self, metadata: Metadata); + fn headers_available(&mut self, metadata: Result); /// A portion of the response body has been received. This data is unavailable after /// this method returned, and must be stored accordingly. fn data_available(&mut self, payload: Vec); /// The response is complete. If the provided status is an Err value, there is no guarantee /// that the response body was completely read. - fn response_complete(&mut self, status: Result<(), String>); + fn response_complete(&mut self, status: Result<(), NetworkError>); } /// Data for passing between threads/processes to indicate a particular action to @@ -127,11 +127,11 @@ pub trait AsyncResponseListener { #[derive(Deserialize, Serialize)] pub enum ResponseAction { /// Invoke headers_available - HeadersAvailable(Metadata), + HeadersAvailable(Result), /// Invoke data_available DataAvailable(Vec), /// Invoke response_complete - ResponseComplete(Result<(), String>) + ResponseComplete(Result<(), NetworkError>) } impl ResponseAction { @@ -376,7 +376,7 @@ pub enum ProgressMsg { /// Binary data - there may be multiple of these Payload(Vec), /// Indicates loading is complete, either successfully or not - Done(Result<(), String>) + Done(Result<(), NetworkError>), } /// Convenience function for synchronously loading a whole resource. @@ -384,7 +384,7 @@ pub fn load_whole_resource(context: LoadContext, resource_thread: &ResourceThread, url: Url, pipeline_id: Option) - -> Result<(Metadata, Vec), String> { + -> Result<(Metadata, Vec), NetworkError> { let (start_chan, start_port) = ipc::channel().unwrap(); resource_thread.send(ControlMsg::Load(LoadData::new(context, url, pipeline_id), LoadConsumer::Channel(start_chan), None)).unwrap(); @@ -413,3 +413,13 @@ pub enum ConstellationMsg { /// Queries whether a pipeline or its ancestors are private IsPrivate(PipelineId, Sender), } + +/// Network errors that have to be exported out of the loaders +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, HeapSizeOf)] +pub enum NetworkError { + /// Could be any of the internal errors, like unsupported scheme, load + /// cancellation, connection errors, etc. + Internal(String), + /// SSL validation error that has to be handled in the HTML parser + SslValidation(Url), +} diff --git a/components/script/cors.rs b/components/script/cors.rs index da8c3db68cae..626d3985d8ca 100644 --- a/components/script/cors.rs +++ b/components/script/cors.rs @@ -18,7 +18,7 @@ use hyper::header::{HeaderView, Headers}; use hyper::method::Method; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper::status::StatusClass::Success; -use net_traits::{AsyncResponseListener, Metadata, ResponseAction}; +use net_traits::{AsyncResponseListener, Metadata, NetworkError, ResponseAction}; use network_listener::{NetworkListener, PreInvoke}; use script_runtime::ScriptChan; use std::ascii::AsciiExt; @@ -124,13 +124,15 @@ impl CORSRequest { // This is shoe-horning the CORSReponse stuff into the rest of the async network // framework right now. It would be worth redesigning http_fetch to do this properly. impl AsyncResponseListener for CORSContext { - fn headers_available(&mut self, _metadata: Metadata) { + fn headers_available(&mut self, _metadata: Result) { + } fn data_available(&mut self, _payload: Vec) { + } - fn response_complete(&mut self, _status: Result<(), String>) { + fn response_complete(&mut self, _status: Result<(), NetworkError>) { let response = self.response.take().unwrap(); self.listener.response_available(response); } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 50c3fbf682ff..b7dc3e2f6630 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -57,11 +57,11 @@ use layout_interface::{LayoutChan, LayoutRPC}; use libc; use msg::constellation_msg::ConstellationChan; use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData}; -use net_traits::Metadata; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::response::HttpsState; use net_traits::storage_thread::StorageType; +use net_traits::{Metadata, NetworkError}; use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; @@ -287,6 +287,7 @@ no_jsmanaged_fields!(Size2D); no_jsmanaged_fields!(Arc); no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheChan, ImageCacheThread); no_jsmanaged_fields!(Metadata); +no_jsmanaged_fields!(NetworkError); no_jsmanaged_fields!(Atom, Namespace, QualName); no_jsmanaged_fields!(Trusted); no_jsmanaged_fields!(PropertyDeclarationBlock); diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 1ad52f45d6c3..a971a90ecbfe 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -24,7 +24,7 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use layout_interface::{LayoutChan, Msg}; use msg::constellation_msg::ConstellationChan; -use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata}; +use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; use network_listener::{NetworkListener, PreInvoke}; use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg}; use std::ascii::AsciiExt; @@ -271,8 +271,8 @@ struct StylesheetContext { impl PreInvoke for StylesheetContext {} impl AsyncResponseListener for StylesheetContext { - fn headers_available(&mut self, metadata: Metadata) { - self.metadata = Some(metadata); + fn headers_available(&mut self, metadata: Result) { + self.metadata = metadata.ok(); } fn data_available(&mut self, payload: Vec) { @@ -280,9 +280,12 @@ impl AsyncResponseListener for StylesheetContext { self.data.append(&mut payload); } - fn response_complete(&mut self, _status: Result<(), String>) { + fn response_complete(&mut self, _status: Result<(), NetworkError>) { let data = mem::replace(&mut self.data, vec!()); - let metadata = self.metadata.take().unwrap(); + let metadata = match self.metadata.take() { + Some(meta) => meta, + None => return, + }; // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding let environment_encoding = UTF_8 as EncodingRef; let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s); diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index b5a6a473dd37..af9339571fbf 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -33,7 +33,7 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsapi::RootedValue; use js::jsval::UndefinedValue; -use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata}; +use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; use network_listener::{NetworkListener, PreInvoke}; use script_runtime::ScriptChan; use script_thread::MainThreadScriptChan; @@ -124,7 +124,7 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[ #[derive(HeapSizeOf, JSTraceable)] pub enum ScriptOrigin { Internal(DOMString, Url), - External(Result<(Metadata, Vec), String>), + External(Result<(Metadata, Vec), NetworkError>), } /// The context required for asynchronously loading an external script source. @@ -138,23 +138,25 @@ struct ScriptContext { /// The initial URL requested. url: Url, /// Indicates whether the request failed, and why - status: Result<(), String> + status: Result<(), NetworkError> } impl AsyncResponseListener for ScriptContext { - fn headers_available(&mut self, metadata: Metadata) { - let status_code = match metadata.status { - Some(RawStatus(c, _)) => c, - _ => 0 - }; + fn headers_available(&mut self, metadata: Result) { + self.metadata = metadata.ok(); + + let status_code = self.metadata.as_ref().and_then(|m| { + match m.status { + Some(RawStatus(c, _)) => Some(c), + _ => None, + } + }).unwrap_or(0); self.status = match status_code { - 0 => Err("No http status code received".to_owned()), + 0 => Err(NetworkError::Internal("No http status code received".to_owned())), 200...299 => Ok(()), // HTTP ok status codes - _ => Err(format!("HTTP error code {}", status_code)) + _ => Err(NetworkError::Internal(format!("HTTP error code {}", status_code))) }; - - self.metadata = Some(metadata); } fn data_available(&mut self, payload: Vec) { @@ -164,7 +166,7 @@ impl AsyncResponseListener for ScriptContext { } } - fn response_complete(&mut self, status: Result<(), String>) { + fn response_complete(&mut self, status: Result<(), NetworkError>) { let load = status.and(self.status.clone()).map(|_| { let data = mem::replace(&mut self.data, vec!()); let metadata = self.metadata.take().unwrap(); @@ -398,7 +400,7 @@ impl HTMLScriptElement { let (source, external, url) = match load { // Step 2.a. ScriptOrigin::External(Err(e)) => { - error!("error loading script {}", e); + error!("error loading script {:?}", e); self.dispatch_error_event(); return; } diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index 2312ac96795d..d1d179aa2a7b 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -26,7 +26,7 @@ use hyper::header::ContentType; use hyper::mime::{Mime, SubLevel, TopLevel}; use js::jsapi::JSTracer; use msg::constellation_msg::{PipelineId, SubpageId}; -use net_traits::{AsyncResponseListener, Metadata}; +use net_traits::{AsyncResponseListener, Metadata, NetworkError}; use network_listener::PreInvoke; use parse::Parser; use script_runtime::ScriptChan; @@ -36,6 +36,7 @@ use std::cell::UnsafeCell; use std::default::Default; use std::ptr; use url::Url; +use util::resource_files::read_resource_file; #[must_root] #[derive(JSTraceable, HeapSizeOf)] @@ -239,12 +240,23 @@ impl ParserContext { } impl AsyncResponseListener for ParserContext { - fn headers_available(&mut self, metadata: Metadata) { - let content_type = metadata.content_type.clone(); - - let parser = ScriptThread::page_fetch_complete(self.id.clone(), self.subpage.clone(), - metadata); - let parser = match parser { + fn headers_available(&mut self, meta_result: Result) { + let mut is_ssl_error = false; + let metadata = match meta_result { + Ok(meta) => Some(meta), + Err(NetworkError::SslValidation(url)) => { + is_ssl_error = true; + let mut meta = Metadata::default(url); + let mime: Option = "text/html".parse().ok(); + meta.set_content_type(mime.as_ref()); + Some(meta) + }, + Err(_) => None, + }; + let content_type = metadata.clone().and_then(|meta| meta.content_type); + let parser = match ScriptThread::page_fetch_complete(self.id.clone(), + self.subpage.clone(), + metadata) { Some(parser) => parser, None => return, }; @@ -274,7 +286,15 @@ impl AsyncResponseListener for ParserContext { parser.parse_sync(); parser.set_plaintext_state(); }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => {}, // Handle text/html + Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html + if is_ssl_error { + self.is_synthesized_document = true; + let page_bytes = read_resource_file("badcert.html").unwrap(); + let page = String::from_utf8(page_bytes).unwrap(); + parser.pending_input().borrow_mut().push(page); + parser.parse_sync(); + } + }, Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => {}, // Handle text/xml Some(ContentType(Mime(toplevel, sublevel, _))) => { if toplevel.as_str() == "application" && sublevel.as_str() == "xhtml+xml" { @@ -308,7 +328,7 @@ impl AsyncResponseListener for ParserContext { } } - fn response_complete(&mut self, status: Result<(), String>) { + fn response_complete(&mut self, status: Result<(), NetworkError>) { let parser = match self.parser.as_ref() { Some(parser) => parser.root(), None => return, @@ -316,7 +336,7 @@ impl AsyncResponseListener for ParserContext { parser.r().document().finish_load(LoadType::PageSource(self.url.clone())); if let Err(err) = status { - debug!("Failed to load page URL {}, error: {}", self.url.serialize(), err); + debug!("Failed to load page URL {}, error: {:?}", self.url.serialize(), err); // TODO(Savago): we should send a notification to callers #5463. } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 8d6716201af4..a381cae57b09 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -45,7 +45,7 @@ use js::jsapi::JS_ClearPendingException; use js::jsapi::{JSContext, JS_ParseJSON, RootedValue}; use js::jsval::{JSVal, NullValue, UndefinedValue}; use net_traits::ControlMsg::Load; -use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata}; +use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; use net_traits::{LoadConsumer, LoadContext, LoadData, ResourceCORSData, ResourceThread}; use network_listener::{NetworkListener, PreInvoke}; use parse::html::{ParseContext, parse_html}; @@ -254,7 +254,7 @@ impl XMLHttpRequest { resource_thread: ResourceThread, load_data: LoadData) { impl AsyncResponseListener for XHRContext { - fn headers_available(&mut self, metadata: Metadata) { + fn headers_available(&mut self, metadata: Result) { let xhr = self.xhr.root(); let rv = xhr.process_headers_available(self.cors_request.clone(), self.gen_id, @@ -269,7 +269,7 @@ impl XMLHttpRequest { self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone()); } - fn response_complete(&mut self, status: Result<(), String>) { + fn response_complete(&mut self, status: Result<(), NetworkError>) { let rv = self.xhr.root().process_response_complete(self.gen_id, status); *self.sync_status.borrow_mut() = Some(rv); } @@ -870,7 +870,15 @@ impl XMLHttpRequest { } fn process_headers_available(&self, cors_request: Option, - gen_id: GenerationId, metadata: Metadata) -> Result<(), Error> { + gen_id: GenerationId, metadata: Result) + -> Result<(), Error> { + let metadata = match metadata { + Ok(meta) => meta, + Err(_) => { + self.process_partial_response(XHRProgress::Errored(gen_id, Error::Network)); + return Err(Error::Network); + }, + }; let bypass_cross_origin_check = { // We want to be able to do cross-origin requests in browser.html. @@ -904,9 +912,7 @@ impl XMLHttpRequest { *self.response_url.borrow_mut() = metadata.final_url.serialize_no_fragment(); // XXXManishearth Clear cache entries in case of a network error - self.process_partial_response(XHRProgress::HeadersReceived(gen_id, - metadata.headers, - metadata.status)); + self.process_partial_response(XHRProgress::HeadersReceived(gen_id, metadata.headers, metadata.status)); Ok(()) } @@ -914,7 +920,7 @@ impl XMLHttpRequest { self.process_partial_response(XHRProgress::Loading(gen_id, ByteString::new(payload))); } - fn process_response_complete(&self, gen_id: GenerationId, status: Result<(), String>) + fn process_response_complete(&self, gen_id: GenerationId, status: Result<(), NetworkError>) -> ErrorResult { match status { Ok(()) => { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 78c9aefc4095..d076701a9f5c 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -492,7 +492,7 @@ pub unsafe extern "C" fn shadow_check_callback(_cx: *mut JSContext, } impl ScriptThread { - pub fn page_fetch_complete(id: PipelineId, subpage: Option, metadata: Metadata) + pub fn page_fetch_complete(id: PipelineId, subpage: Option, metadata: Option) -> Option { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.borrow().unwrap() }; @@ -1280,7 +1280,7 @@ impl ScriptThread { /// We have received notification that the response associated with a load has completed. /// Kick off the document and frame tree creation process using the result. fn handle_page_fetch_complete(&self, id: PipelineId, subpage: Option, - metadata: Metadata) -> Option { + metadata: Option) -> Option { let idx = self.incomplete_loads.borrow().iter().position(|load| { load.pipeline_id == id && load.parent_info.map(|info| info.1) == subpage }); @@ -1289,7 +1289,7 @@ impl ScriptThread { match idx { Some(idx) => { let load = self.incomplete_loads.borrow_mut().remove(idx); - Some(self.load(metadata, load)) + metadata.map(|meta| self.load(meta, load)) } None => { assert!(self.closed_pipelines.borrow().contains(&id)); diff --git a/resources/badcert.html b/resources/badcert.html index 023a833e796f..f90a547dc6a7 100644 --- a/resources/badcert.html +++ b/resources/badcert.html @@ -3,6 +3,6 @@ Certificate error - + diff --git a/tests/unit/net/chrome_loader.rs b/tests/unit/net/chrome_loader.rs new file mode 100644 index 000000000000..f4697593fce4 --- /dev/null +++ b/tests/unit/net/chrome_loader.rs @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use net::chrome_loader::resolve_chrome_url; +use url::Url; + +#[test] +fn test_relative() { + let url = Url::parse("chrome://../something").unwrap(); + assert!(resolve_chrome_url(&url).is_err()); +} + +#[test] +fn test_relative_2() { + let url = Url::parse("chrome://subdir/../something").unwrap(); + assert!(resolve_chrome_url(&url).is_err()); +} + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_absolute() { + let url = Url::parse("chrome:///etc/passwd").unwrap(); + assert!(resolve_chrome_url(&url).is_err()); +} + +#[test] +#[cfg(target_os = "windows")] +fn test_absolute_2() { + let url = Url::parse("chrome://C:\\Windows").unwrap(); + assert!(resolve_chrome_url(&url).is_err()); +} + +#[test] +#[cfg(target_os = "windows")] +fn test_absolute_3() { + let url = Url::parse("chrome://\\\\server/C$").unwrap(); + assert!(resolve_chrome_url(&url).is_err()); +} + +#[test] +fn test_valid() { + let url = Url::parse("chrome://badcert.jpg").unwrap(); + let resolved = resolve_chrome_url(&url).unwrap(); + assert_eq!(resolved.scheme, "file"); +} diff --git a/tests/unit/net/data_loader.rs b/tests/unit/net/data_loader.rs index 50ee87714909..2f887f5a2bcf 100644 --- a/tests/unit/net/data_loader.rs +++ b/tests/unit/net/data_loader.rs @@ -7,7 +7,7 @@ extern crate hyper; use ipc_channel::ipc; use net_traits::LoadConsumer::Channel; use net_traits::ProgressMsg::{Payload, Done}; -use net_traits::{LoadData, LoadContext}; +use net_traits::{LoadData, LoadContext, NetworkError}; use self::hyper::header::ContentType; use self::hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; @@ -36,7 +36,7 @@ fn assert_parse(url: &'static str, match data { None => { - assert_eq!(progress, Done(Err("invalid data uri".to_owned()))); + assert_eq!(progress, Done(Err(NetworkError::Internal("invalid data uri".to_owned())))); } Some(dat) => { assert_eq!(progress, Payload(dat)); diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs index 3b9da166a456..06ce33abcc33 100644 --- a/tests/unit/net/http_loader.rs +++ b/tests/unit/net/http_loader.rs @@ -20,6 +20,7 @@ use msg::constellation_msg::PipelineId; 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::resource_thread::{AuthCacheEntry, CancellationListener}; use net_traits::{LoadData, CookieSource, LoadContext, IncludeSubdomains}; @@ -1075,9 +1076,8 @@ fn test_load_errors_when_there_a_redirect_loop() { match load(&load_data, &ui_provider, &http_state, None, &Factory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { - Err(LoadError::InvalidRedirect(_, msg)) => { - assert_eq!(msg, "redirect loop"); - }, + Err(ref load_err) if load_err.error == LoadErrorType::InvalidRedirect => + assert_eq!(&load_err.reason, "redirect loop"), _ => panic!("expected max redirects to fail") } } @@ -1110,10 +1110,11 @@ fn test_load_errors_when_there_is_too_many_redirects() { match load(&load_data, &ui_provider, &http_state, None, &Factory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { - Err(LoadError::MaxRedirects(url, num_redirects)) => { + Err(LoadError { error: LoadErrorType::MaxRedirects(num_redirects), + url, .. }) => { assert_eq!(num_redirects, redirect_limit as u32); - assert_eq!(url.domain().unwrap(), "mozilla.com") - }, + assert_eq!(url.domain().unwrap(), "mozilla.com"); + } _ => panic!("expected max redirects to fail") } @@ -1166,7 +1167,7 @@ impl HttpRequestFactory for DontConnectFactory { type R = MockRequest; fn create(&self, url: Url, _: Method, _: Headers) -> Result { - Err(LoadError::Connection(url, "should not have connected".to_owned())) + Err(LoadError::new(url, LoadErrorType::Connection, "should not have connected".to_owned())) } } @@ -1184,7 +1185,7 @@ fn test_load_errors_when_scheme_is_not_http_or_https() { &DontConnectFactory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { - Err(LoadError::UnsupportedScheme(_)) => {} + Err(ref load_err) if load_err.error == LoadErrorType::UnsupportedScheme => (), _ => panic!("expected ftp scheme to be unsupported") } } @@ -1203,7 +1204,7 @@ fn test_load_errors_when_viewing_source_and_inner_url_scheme_is_not_http_or_http &DontConnectFactory, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) { - Err(LoadError::UnsupportedScheme(_)) => {} + Err(ref load_err) if load_err.error == LoadErrorType::UnsupportedScheme => (), _ => panic!("expected ftp scheme to be unsupported") } } @@ -1245,7 +1246,7 @@ fn test_load_errors_when_cancelled() { &Factory, DEFAULT_USER_AGENT.to_owned(), &cancel_listener) { - Err(LoadError::Cancelled(_, _)) => (), + Err(ref load_err) if load_err.error == LoadErrorType::Cancelled => (), _ => panic!("expected load cancelled error!") } } diff --git a/tests/unit/net/lib.rs b/tests/unit/net/lib.rs index 7de91fb7738d..4e8e38dc8547 100644 --- a/tests/unit/net/lib.rs +++ b/tests/unit/net/lib.rs @@ -18,6 +18,7 @@ extern crate unicase; extern crate url; extern crate util; +#[cfg(test)] mod chrome_loader; #[cfg(test)] mod cookie; #[cfg(test)] mod data_loader; #[cfg(test)] mod file_loader; diff --git a/tests/unit/net/resource_thread.rs b/tests/unit/net/resource_thread.rs index fdaa0cd6bdb8..ecba392d4fb0 100644 --- a/tests/unit/net/resource_thread.rs +++ b/tests/unit/net/resource_thread.rs @@ -5,7 +5,7 @@ use ipc_channel::ipc; use net::resource_thread::new_resource_thread; use net_traits::hosts::{parse_hostsfile, host_replacement}; -use net_traits::{ControlMsg, LoadData, LoadConsumer, ProgressMsg, LoadContext}; +use net_traits::{ControlMsg, LoadData, LoadConsumer, LoadContext, NetworkError, ProgressMsg}; use std::borrow::ToOwned; use std::collections::HashMap; use std::sync::mpsc::channel; @@ -213,9 +213,7 @@ fn test_cancelled_listener() { // (but, the loading has been cancelled) let _ = body_sender.send(body); let response = receiver.recv().unwrap(); - match response.progress_port.recv().unwrap() { - ProgressMsg::Done(result) => assert_eq!(result.unwrap_err(), "load cancelled".to_owned()), - _ => panic!("baaaah!"), - } + assert_eq!(response.progress_port.recv().unwrap(), + ProgressMsg::Done(Err(NetworkError::Internal("load cancelled".to_owned())))); resource_thread.send(ControlMsg::Exit).unwrap(); } diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 1cadcfe17659..0c5279fc574e 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5779,6 +5779,18 @@ "url": "/_mozilla/mozilla/iframe/resize_after_load.html" } ], + "mozilla/sslfail.html": [ + { + "path": "mozilla/sslfail.html", + "references": [ + [ + "/_mozilla/mozilla/sslfail-ref.html", + "==" + ] + ], + "url": "/_mozilla/mozilla/sslfail.html" + } + ], "mozilla/webgl/clearcolor.html": [ { "path": "mozilla/webgl/clearcolor.html", @@ -12441,6 +12453,18 @@ "url": "/_mozilla/mozilla/iframe/resize_after_load.html" } ], + "mozilla/sslfail.html": [ + { + "path": "mozilla/sslfail.html", + "references": [ + [ + "/_mozilla/mozilla/sslfail-ref.html", + "==" + ] + ], + "url": "/_mozilla/mozilla/sslfail.html" + } + ], "mozilla/webgl/clearcolor.html": [ { "path": "mozilla/webgl/clearcolor.html", diff --git a/tests/wpt/mozilla/tests/mozilla/bad_cert_detected.html b/tests/wpt/mozilla/tests/mozilla/bad_cert_detected.html index 22f6b6676e47..7b79c46b48dc 100644 --- a/tests/wpt/mozilla/tests/mozilla/bad_cert_detected.html +++ b/tests/wpt/mozilla/tests/mozilla/bad_cert_detected.html @@ -10,18 +10,14 @@ t.step(function() { var target = location.href.replace(HTTP_ORIGIN, HTTPS_ORIGIN) .replace('bad_cert_detected.html', - 'resources/origin_helpers.js'); - // Servo currently lacks the ability to introspect any content that is blocked - // due to a cert error, so we use a roundabout method to infer that that's happened. - // When the worker has a cert failure, that translates into attempting to evaluate the - // contents of badcert.html as JS, which triggers an exception that currently does not - // propagate to the parent scope. If we _do_ get an error event in the parent scope, - // that means that the cert verification was treated no different than any other - // network error, since we dispatch an error event in that case. + 'resources/worker_success.js'); var w = new Worker(target); - w.addEventListener('error', t.unreached_func("cert not detected as invalid"), false); - // We infer that we detected an invalid cert if nothing happens for a few seconds. - setTimeout(function() { t.done() }, 3000); + // If the script executes successfully, it should send a message. That indicates that + // there was no validation failure, which is bad. + w.addEventListener('message', t.unreached_func("cert not detected as invalid"), true); + // When the worker has a cert failure, that translates into an early error that is reported + // to the Worker object. + w.addEventListener('error', t.step_func_done(), true); }); diff --git a/tests/wpt/mozilla/tests/mozilla/resources/origin_helpers.js b/tests/wpt/mozilla/tests/mozilla/resources/origin_helpers.js index 35e6ebf6a05e..6493d422c083 100644 --- a/tests/wpt/mozilla/tests/mozilla/resources/origin_helpers.js +++ b/tests/wpt/mozilla/tests/mozilla/resources/origin_helpers.js @@ -1,5 +1,5 @@ var HTTP_PORT = '{{ports[http][0]}}'; var HTTPS_PORT = '{{ports[https][0]}}'; -var ORIGINAL_HOST = '\'{{host}}\''; +var ORIGINAL_HOST = '{{host}}'; var HTTP_ORIGIN = 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT; var HTTPS_ORIGIN = 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT; diff --git a/tests/wpt/mozilla/tests/mozilla/resources/ssl.https.html b/tests/wpt/mozilla/tests/mozilla/resources/ssl.https.html new file mode 100644 index 000000000000..ea0a70858a42 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/resources/ssl.https.html @@ -0,0 +1,5 @@ + + +this should be a secure connection + + \ No newline at end of file diff --git a/tests/wpt/mozilla/tests/mozilla/resources/worker_success.js b/tests/wpt/mozilla/tests/mozilla/resources/worker_success.js new file mode 100644 index 000000000000..327986f34b9b --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/resources/worker_success.js @@ -0,0 +1 @@ +postMessage('load succeeded'); diff --git a/tests/wpt/mozilla/tests/mozilla/sslfail-ref.html b/tests/wpt/mozilla/tests/mozilla/sslfail-ref.html new file mode 100644 index 000000000000..4d371a86886b --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/sslfail-ref.html @@ -0,0 +1,9 @@ + + + + SSL Failure Reference + + + + + diff --git a/tests/wpt/mozilla/tests/mozilla/sslfail.html b/tests/wpt/mozilla/tests/mozilla/sslfail.html new file mode 100644 index 000000000000..40eb31ca31a2 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/sslfail.html @@ -0,0 +1,17 @@ + + + + SSL Failure + + + + + + +