diff options
103 files changed, 1881 insertions, 322 deletions
diff --git a/Cargo.lock b/Cargo.lock index e6f61bfcdb5..d62bee3ddc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2591,6 +2591,7 @@ dependencies = [ "servo_config 0.0.1", "servo_url 0.0.1", "std_test_override 0.0.1", + "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.57.2 (git+https://github.com/servo/webrender)", diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 3687f249112..70dcf4d7983 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -22,7 +22,7 @@ use mime_guess::guess_mime_type; use net_traits::request::{CredentialsMode, Destination, Referrer, Request, RequestMode}; use net_traits::request::{Origin, ResponseTainting, Window}; use net_traits::response::{Response, ResponseBody, ResponseType}; -use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy}; +use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceFetchTiming}; use servo_url::ServoUrl; use std::borrow::Cow; use std::fs::File; @@ -55,6 +55,7 @@ pub struct FetchContext { pub devtools_chan: Option<Sender<DevtoolsControlMsg>>, pub filemanager: FileManager, pub cancellation_listener: Arc<Mutex<CancellationListener>>, + pub timing: Arc<Mutex<ResourceFetchTiming>>, } pub struct CancellationListener { @@ -503,7 +504,7 @@ fn scheme_fetch( match url.scheme() { "about" if url.path() == "blank" => { - let mut response = Response::new(url); + let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type())); response .headers .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8)); @@ -517,7 +518,8 @@ fn scheme_fetch( "data" => match decode(&url) { Ok((mime, bytes)) => { - let mut response = Response::new(url); + let mut response = + Response::new(url, ResourceFetchTiming::new(request.timing_type())); *response.body.lock().unwrap() = ResponseBody::Done(bytes); response.headers.typed_insert(ContentType::from(mime)); response @@ -537,7 +539,8 @@ fn scheme_fetch( if let Ok(file) = File::open(file_path.clone()) { let mime = guess_mime_type(file_path); - let mut response = Response::new(url); + let mut response = + Response::new(url, ResourceFetchTiming::new(request.timing_type())); response.headers.typed_insert(ContentType::from(mime)); let (done_sender, done_receiver) = unbounded(); @@ -656,7 +659,8 @@ fn scheme_fetch( match load_blob_sync(url.clone(), context.filemanager.clone()) { Ok((headers, bytes)) => { - let mut response = Response::new(url); + let mut response = + Response::new(url, ResourceFetchTiming::new(request.timing_type())); response.headers = headers; *response.body.lock().unwrap() = ResponseBody::Done(bytes); response diff --git a/components/net/http_cache.rs b/components/net/http_cache.rs index a12c935d51d..a91fb6f5180 100644 --- a/components/net/http_cache.rs +++ b/components/net/http_cache.rs @@ -20,7 +20,7 @@ use malloc_size_of::{ }; use net_traits::request::Request; use net_traits::response::{HttpsState, Response, ResponseBody}; -use net_traits::{FetchMetadata, Metadata}; +use net_traits::{FetchMetadata, Metadata, ResourceFetchTiming}; use servo_arc::Arc; use servo_config::prefs::PREFS; use servo_url::ServoUrl; @@ -302,7 +302,11 @@ fn create_cached_response( cached_headers: &HeaderMap, done_chan: &mut DoneChannel, ) -> CachedResponse { - let mut response = Response::new(cached_resource.data.metadata.data.final_url.clone()); + let resource_timing = ResourceFetchTiming::new(request.timing_type()); + let mut response = Response::new( + cached_resource.data.metadata.data.final_url.clone(), + resource_timing, + ); response.headers = cached_headers.clone(); response.body = cached_resource.body.clone(); if let ResponseBody::Receiving(_) = *cached_resource.body.lock().unwrap() { @@ -687,8 +691,11 @@ impl HttpCache { // Received a response with 304 status code, in response to a request that matches a cached resource. // 1. update the headers of the cached resource. // 2. return a response, constructed from the cached resource. - let mut constructed_response = - Response::new(cached_resource.data.metadata.data.final_url.clone()); + let resource_timing = ResourceFetchTiming::new(request.timing_type()); + let mut constructed_response = Response::new( + cached_resource.data.metadata.data.final_url.clone(), + resource_timing, + ); constructed_response.body = cached_resource.body.clone(); constructed_response.status = cached_resource.data.status.clone(); constructed_response.https_state = cached_resource.data.https_state.clone(); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index e15e6ba4d35..66bdce34f89 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -43,6 +43,7 @@ use net_traits::request::{CacheMode, CredentialsMode, Destination, Origin}; use net_traits::request::{RedirectMode, Referrer, Request, RequestMode}; use net_traits::request::{ResponseTainting, ServiceWorkersMode}; use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType}; +use net_traits::ResourceAttribute; use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy}; use openssl::ssl::SslConnectorBuilder; use servo_url::{ImmutableOrigin, ServoUrl}; @@ -408,14 +409,8 @@ fn obtain_response( }, } - if log_enabled!(log::Level::Info) { - info!("{} {}", method, url); - for header in headers.iter() { - info!(" - {:?}", header); - } - info!("{:?}", data); - } - + // TODO(#21261) connect_start: set if a persistent connection is *not* used and the last non-redirected + // fetch passes the timing allow check let connect_start = precise_time_ms(); // https://url.spec.whatwg.org/#percent-encoded-bytes let request = HyperRequest::builder() @@ -435,6 +430,8 @@ fn obtain_response( Err(e) => return Box::new(future::result(Err(NetworkError::from_http_error(&e)))), }; *request.headers_mut() = headers.clone(); + + //TODO(#21262) connect_end let connect_end = precise_time_ms(); let request_id = request_id.map(|v| v.to_owned()); @@ -449,6 +446,8 @@ fn obtain_response( .and_then(move |res| { let send_end = precise_time_ms(); + // TODO(#21271) response_start: immediately after receiving first byte of response + let msg = if let Some(request_id) = request_id { if let Some(pipeline_id) = pipeline_id { Some(prepare_devtools_request( @@ -483,6 +482,7 @@ fn obtain_response( }) .map_err(move |e| NetworkError::from_hyper_error(&e)), ) + // TODO(#21263) response_end (also needs to be set above if fetch is aborted due to an error) } /// [HTTP fetch](https://fetch.spec.whatwg.org#http-fetch) @@ -571,6 +571,19 @@ pub fn http_fetch( } // Substep 3 + // TODO(#21258) maybe set fetch_start (if this is the last resource) + // Generally, we use a persistent connection, so we will also set other PerformanceResourceTiming + // attributes to this as well (domain_lookup_start, domain_lookup_end, connect_start, connect_end, + // secure_connection_start) + // TODO(#21256) maybe set redirect_start if this resource initiates the redirect + // TODO(#21254) also set startTime equal to either fetch_start or redirect_start + // (https://w3c.github.io/resource-timing/#dfn-starttime) + context + .timing + .lock() + .unwrap() + .set_attribute(ResourceAttribute::RequestStart); + let mut fetch_result = http_network_or_cache_fetch( request, authentication_fetch_flag, @@ -640,8 +653,22 @@ pub fn http_fetch( }, }; } + + // TODO redirect_end: last byte of response of last redirect + // set back to default response.return_internal = true; + context + .timing + .lock() + .unwrap() + .set_attribute(ResourceAttribute::RedirectCount( + request.redirect_count as u16, + )); + + let timing = &*context.timing.lock().unwrap(); + response.resource_timing = timing.clone(); + // Step 6 response } @@ -1199,7 +1226,8 @@ fn http_network_fetch( } } - let mut response = Response::new(url.clone()); + let timing = &*context.timing.lock().unwrap(); + let mut response = Response::new(url.clone(), timing.clone()); response.status = Some(( res.status(), res.status().canonical_reason().unwrap_or("").into(), @@ -1224,6 +1252,7 @@ fn http_network_fetch( FetchMetadata::Unfiltered(m) => m, FetchMetadata::Filtered { unsafe_, .. } => unsafe_, }; + let devtools_sender = context.devtools_chan.clone(); let meta_status = meta.status.clone(); let meta_headers = meta.headers.clone(); diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index e72cd27e3da..7c225d94937 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -503,11 +503,11 @@ impl ImageCache for ImageCacheImpl { (FetchResponseMsg::ProcessResponseEOF(result), key) => { debug!("Received EOF for {:?}", key); match result { - Ok(()) => { + Ok(_) => { let bytes = { let mut store = self.store.lock().unwrap(); let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap(); - pending_load.result = Some(result); + pending_load.result = Some(Ok(())); debug!("Async decoding {} ({:?})", pending_load.url, key); pending_load.bytes.mark_complete() }; diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 8c2bad33b41..3c366201d95 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -22,13 +22,14 @@ use embedder_traits::EmbedderProxy; use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcReceiver, IpcReceiverSet, IpcSender}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use net_traits::request::{Request, RequestInit}; +use net_traits::request::{Destination, Request, RequestInit}; use net_traits::response::{Response, ResponseInit}; use net_traits::storage_thread::StorageThreadMsg; use net_traits::WebSocketNetworkEvent; -use net_traits::{CookieSource, CoreResourceThread}; -use net_traits::{CoreResourceMsg, CustomResponseMediator, FetchChannels}; +use net_traits::{CookieSource, CoreResourceMsg, CoreResourceThread}; +use net_traits::{CustomResponseMediator, FetchChannels}; use net_traits::{FetchResponseMsg, ResourceThreads, WebSocketDomAction}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::mem::{Report, ReportKind, ReportsChan}; use profile_traits::time::ProfilerChan; @@ -440,6 +441,11 @@ impl CoreResourceManager { let dc = self.devtools_chan.clone(); let filemanager = self.filemanager.clone(); + let timing_type = match req_init.destination { + Destination::Document => ResourceTimingType::Navigation, + _ => ResourceTimingType::Resource, + }; + thread::Builder::new() .name(format!("fetch thread for {}", req_init.url)) .spawn(move || { @@ -456,11 +462,12 @@ impl CoreResourceManager { cancellation_listener: Arc::new(Mutex::new(CancellationListener::new( cancel_chan, ))), + timing: Arc::new(Mutex::new(ResourceFetchTiming::new(request.timing_type()))), }; match res_init_ { Some(res_init) => { - let response = Response::from_init(res_init); + let response = Response::from_init(res_init, timing_type); http_redirect_fetch( &mut request, &mut CorsCache::new(), diff --git a/components/net/tests/fetch.rs b/components/net/tests/fetch.rs index 04f3828c2c6..51f25059ad1 100644 --- a/components/net/tests/fetch.rs +++ b/components/net/tests/fetch.rs @@ -35,9 +35,9 @@ use net::hsts::HstsEntry; use net::test::HttpState; use net_traits::request::{Destination, Origin, RedirectMode, Referrer, Request, RequestMode}; use net_traits::response::{CacheState, Response, ResponseBody, ResponseType}; -use net_traits::IncludeSubdomains; -use net_traits::NetworkError; -use net_traits::ReferrerPolicy; +use net_traits::{ + IncludeSubdomains, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceTimingType, +}; use servo_url::{ImmutableOrigin, ServoUrl}; use std::fs::File; use std::io::Read; @@ -127,7 +127,7 @@ fn test_fetch_blob() { use ipc_channel::ipc; use net_traits::blob_url_store::BlobBuf; - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); let bytes = b"content"; let blob_buf = BlobBuf { @@ -147,7 +147,7 @@ fn test_fetch_blob() { let url = ServoUrl::parse(&format!("blob:{}{}", origin.as_str(), id.to_simple())).unwrap(); let mut request = Request::new(url, Some(Origin::Origin(origin.origin())), None); - let fetch_response = fetch_with_context(&mut request, &context); + let fetch_response = fetch_with_context(&mut request, &mut context); assert!(!fetch_response.is_network_error()); @@ -649,12 +649,15 @@ fn test_fetch_with_hsts() { .unwrap(); let ssl_client = create_ssl_connector_builder(&ca_content); - let context = FetchContext { + let mut context = FetchContext { state: Arc::new(HttpState::new(ssl_client)), user_agent: DEFAULT_USER_AGENT.into(), devtools_chan: None, filemanager: FileManager::new(create_embedder_proxy()), cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(None))), + timing: Arc::new(Mutex::new(ResourceFetchTiming::new( + ResourceTimingType::Navigation, + ))), }; { @@ -668,7 +671,7 @@ fn test_fetch_with_hsts() { request.referrer = Referrer::NoReferrer; // Set the flag. request.local_urls_only = false; - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); server.close(); assert_eq!( response.internal_response.unwrap().url().unwrap().scheme(), diff --git a/components/net/tests/http_loader.rs b/components/net/tests/http_loader.rs index b61373d14da..0854844f135 100644 --- a/components/net/tests/http_loader.rs +++ b/components/net/tests/http_loader.rs @@ -587,8 +587,8 @@ fn test_load_doesnt_add_host_to_sts_list_when_url_is_http_even_if_sts_headers_ar pipeline_id: Some(TEST_PIPELINE_ID), ..RequestInit::default() }); - let context = new_fetch_context(None, None); - let response = fetch_with_context(&mut request, &context); + let mut context = new_fetch_context(None, None); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -623,7 +623,7 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ }; let (server, url) = make_server(handler); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None); @@ -637,7 +637,7 @@ fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_ credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -669,7 +669,7 @@ fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_re }; let (server, url) = make_server(handler); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); { let mut cookie_jar = context.state.cookie_jar.write().unwrap(); @@ -692,7 +692,7 @@ fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_re credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -718,7 +718,7 @@ fn test_load_sends_cookie_if_nonhttp() { }; let (server, url) = make_server(handler); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); { let mut cookie_jar = context.state.cookie_jar.write().unwrap(); @@ -741,7 +741,7 @@ fn test_load_sends_cookie_if_nonhttp() { credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -767,7 +767,7 @@ fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl( }; let (server, url) = make_server(handler); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None); @@ -781,7 +781,7 @@ fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl( credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -819,7 +819,7 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() { }; let (server, url) = make_server(handler); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None); @@ -833,7 +833,7 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() { credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -1242,7 +1242,7 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { let url_y = ServoUrl::parse(&format!("http://mozilla.org:{}/org/", port)).unwrap(); *shared_url_y_clone.lock().unwrap() = Some(url_y.clone()); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); { let mut cookie_jar = context.state.cookie_jar.write().unwrap(); let cookie_x = Cookie::new_wrapped( @@ -1272,7 +1272,7 @@ fn test_redirect_from_x_to_y_provides_y_cookies_from_y() { credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); @@ -1355,7 +1355,7 @@ fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { credentials_mode: CredentialsMode::Include, ..RequestInit::default() }); - let context = new_fetch_context(None, None); + let mut context = new_fetch_context(None, None); let auth_entry = AuthCacheEntry { user_name: "username".to_owned(), @@ -1370,7 +1370,7 @@ fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() { .entries .insert(url.origin().clone().ascii_serialization(), auth_entry); - let response = fetch_with_context(&mut request, &context); + let response = fetch_with_context(&mut request, &mut context); let _ = server.close(); diff --git a/components/net/tests/main.rs b/components/net/tests/main.rs index 4440506caac..1580eb8310a 100644 --- a/components/net/tests/main.rs +++ b/components/net/tests/main.rs @@ -35,7 +35,7 @@ use net::filemanager_thread::FileManager; use net::test::HttpState; use net_traits::request::Request; use net_traits::response::Response; -use net_traits::FetchTaskTarget; +use net_traits::{FetchTaskTarget, ResourceFetchTiming, ResourceTimingType}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use servo_url::ServoUrl; use std::net::TcpListener as StdTcpListener; @@ -93,6 +93,9 @@ fn new_fetch_context( devtools_chan: dc, filemanager: FileManager::new(sender), cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(None))), + timing: Arc::new(Mutex::new(ResourceFetchTiming::new( + ResourceTimingType::Navigation, + ))), } } impl FetchTaskTarget for FetchResponseCollector { @@ -107,14 +110,14 @@ impl FetchTaskTarget for FetchResponseCollector { } fn fetch(request: &mut Request, dc: Option<Sender<DevtoolsControlMsg>>) -> Response { - fetch_with_context(request, &new_fetch_context(dc, None)) + fetch_with_context(request, &mut new_fetch_context(dc, None)) } -fn fetch_with_context(request: &mut Request, context: &FetchContext) -> Response { +fn fetch_with_context(request: &mut Request, mut context: &mut FetchContext) -> Response { let (sender, receiver) = unbounded(); let mut target = FetchResponseCollector { sender: sender }; - methods::fetch(request, &mut target, context); + methods::fetch(request, &mut target, &mut context); receiver.recv().unwrap() } @@ -123,7 +126,12 @@ fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Respon let (sender, receiver) = unbounded(); let mut target = FetchResponseCollector { sender: sender }; - methods::fetch_with_cors_cache(request, cache, &mut target, &new_fetch_context(None, None)); + methods::fetch_with_cors_cache( + request, + cache, + &mut target, + &mut new_fetch_context(None, None), + ); receiver.recv().unwrap() } diff --git a/components/net/tests/subresource_integrity.rs b/components/net/tests/subresource_integrity.rs index a256734113f..ff6ca3d67ad 100644 --- a/components/net/tests/subresource_integrity.rs +++ b/components/net/tests/subresource_integrity.rs @@ -5,6 +5,7 @@ use net::subresource_integrity::{get_prioritized_hash_function, get_strongest_metadata, SriEntry}; use net::subresource_integrity::{is_response_integrity_valid, parsed_metadata}; use net_traits::response::{Response, ResponseBody}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_url::ServoUrl; #[test] @@ -70,7 +71,10 @@ fn test_get_strongest_metadata_different_algorithm() { #[test] fn test_response_integrity_valid() { let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap(); - let response: Response = Response::new(url); + let response: Response = Response::new( + url, + ResourceFetchTiming::new(ResourceTimingType::Navigation), + ); let integrity_metadata = "sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO"; @@ -83,7 +87,10 @@ fn test_response_integrity_valid() { #[test] fn test_response_integrity_invalid() { let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap(); - let response: Response = Response::new(url); + let response: Response = Response::new( + url, + ResourceFetchTiming::new(ResourceTimingType::Navigation), + ); let integrity_metadata = "sha256-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO"; diff --git a/components/net_traits/Cargo.toml b/components/net_traits/Cargo.toml index 7c40eb55b04..0bba5824d93 100644 --- a/components/net_traits/Cargo.toml +++ b/components/net_traits/Cargo.toml @@ -36,6 +36,7 @@ serde = "1.0" servo_arc = {path = "../servo_arc"} servo_config = {path = "../config"} servo_url = {path = "../url"} +time = "0.1" url = "1.2" uuid = {version = "0.7", features = ["v4", "serde"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 212b6885f1b..0cf5604de80 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -35,6 +35,7 @@ use mime::Mime; use msg::constellation_msg::HistoryStateId; use servo_url::ServoUrl; use std::error::Error; +use time::precise_time_ns; use url::percent_encoding; pub mod blob_url_store; @@ -157,7 +158,7 @@ pub enum FetchResponseMsg { // todo: send more info about the response (or perhaps the entire Response) ProcessResponse(Result<FetchMetadata, NetworkError>), ProcessResponseChunk(Vec<u8>), - ProcessResponseEOF(Result<(), NetworkError>), + ProcessResponseEOF(Result<ResourceFetchTiming, NetworkError>), } pub trait FetchTaskTarget { @@ -207,7 +208,10 @@ pub trait FetchResponseListener { fn process_request_eof(&mut self); fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>); fn process_response_chunk(&mut self, chunk: Vec<u8>); - fn process_response_eof(&mut self, response: Result<(), NetworkError>); + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>); + fn resource_timing(&self) -> &ResourceFetchTiming; + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming; + fn submit_resource_timing(&mut self); } impl FetchTaskTarget for IpcSender<FetchResponseMsg> { @@ -231,7 +235,9 @@ impl FetchTaskTarget for IpcSender<FetchResponseMsg> { if let Some(e) = response.get_network_error() { let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(e.clone()))); } else { - let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(()))); + let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(response + .get_resource_timing() + .clone()))); } } } @@ -248,7 +254,23 @@ impl<T: FetchResponseListener> Action<T> for FetchResponseMsg { FetchResponseMsg::ProcessRequestEOF => listener.process_request_eof(), FetchResponseMsg::ProcessResponse(meta) => listener.process_response(meta), FetchResponseMsg::ProcessResponseChunk(data) => listener.process_response_chunk(data), - FetchResponseMsg::ProcessResponseEOF(data) => listener.process_response_eof(data), + FetchResponseMsg::ProcessResponseEOF(data) => { + match data { + Ok(ref response_resource_timing) => { + // update listener with values from response + *listener.resource_timing_mut() = response_resource_timing.clone(); + listener.process_response_eof(Ok(response_resource_timing.clone())); + // TODO timing check https://w3c.github.io/resource-timing/#dfn-timing-allow-check + + listener.submit_resource_timing(); + }, + // TODO Resources for which the fetch was initiated, but was later aborted + // (e.g. due to a network error) MAY be included as PerformanceResourceTiming + // objects in the Performance Timeline and MUST contain initialized attribute + // values for processed substeps of the processing model. + Err(e) => listener.process_response_eof(Err(e)), + } + }, } } } @@ -418,6 +440,55 @@ pub struct ResourceCorsData { pub origin: ServoUrl, } +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct ResourceFetchTiming { + pub timing_type: ResourceTimingType, + /// Number of redirects until final resource (currently limited to 20) + pub redirect_count: u16, + pub request_start: u64, + pub response_start: u64, + // pub response_end: u64, + // pub redirect_start: u64, + // pub redirect_end: u64, + // pub connect_start: u64, + // pub connect_end: u64, +} + +pub enum ResourceAttribute { + RedirectCount(u16), + RequestStart, + ResponseStart, +} + +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] +pub enum ResourceTimingType { + Resource, + Navigation, + Error, + None, +} + +impl ResourceFetchTiming { + pub fn new(timing_type: ResourceTimingType) -> ResourceFetchTiming { + ResourceFetchTiming { + timing_type: timing_type, + redirect_count: 0, + request_start: 0, + response_start: 0, + } + } + + // TODO currently this is being set with precise time ns when it should be time since + // time origin (as described in Performance::now) + pub fn set_attribute(&mut self, attribute: ResourceAttribute) { + match attribute { + ResourceAttribute::RedirectCount(count) => self.redirect_count = count, + ResourceAttribute::RequestStart => self.request_start = precise_time_ns(), + ResourceAttribute::ResponseStart => self.response_start = precise_time_ns(), + } + } +} + /// Metadata about a loaded resource, such as is obtained from HTTP headers. #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct Metadata { @@ -449,6 +520,8 @@ pub struct Metadata { /// Referrer Policy of the Request used to obtain Response pub referrer_policy: Option<ReferrerPolicy>, + /// Performance information for navigation events + pub timing: Option<ResourceFetchTiming>, } impl Metadata { @@ -465,6 +538,7 @@ impl Metadata { https_state: HttpsState::None, referrer: None, referrer_policy: None, + timing: None, } } @@ -523,7 +597,7 @@ pub fn load_whole_resource( }) }, FetchResponseMsg::ProcessResponseChunk(data) => buf.extend_from_slice(&data), - FetchResponseMsg::ProcessResponseEOF(Ok(())) => return Ok((metadata.unwrap(), buf)), + FetchResponseMsg::ProcessResponseEOF(Ok(_)) => return Ok((metadata.unwrap(), buf)), FetchResponseMsg::ProcessResponse(Err(e)) | FetchResponseMsg::ProcessResponseEOF(Err(e)) => return Err(e), } diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index 1296ad2a193..260aeb99b2a 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::ReferrerPolicy; +use crate::ResourceTimingType; use http::HeaderMap; use hyper::Method; use msg::constellation_msg::PipelineId; @@ -365,6 +366,14 @@ impl Request { _ => false, } } + + pub fn timing_type(&self) -> ResourceTimingType { + if self.is_navigation_request() { + ResourceTimingType::Navigation + } else { + ResourceTimingType::Resource + } + } } impl Referrer { diff --git a/components/net_traits/response.rs b/components/net_traits/response.rs index 88644f80fb0..2b9df272c4d 100644 --- a/components/net_traits/response.rs +++ b/components/net_traits/response.rs @@ -5,6 +5,7 @@ //! The [Response](https://fetch.spec.whatwg.org/#responses) object //! resulting from a [fetch operation](https://fetch.spec.whatwg.org/#concept-fetch) use crate::{FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy}; +use crate::{ResourceFetchTiming, ResourceTimingType}; use headers_core::HeaderMapExt; use headers_ext::{AccessControlExposeHeaders, ContentType}; use http::{HeaderMap, StatusCode}; @@ -119,10 +120,12 @@ pub struct Response { /// https://fetch.spec.whatwg.org/#concept-response-aborted #[ignore_malloc_size_of = "AtomicBool heap size undefined"] pub aborted: Arc<AtomicBool>, + /// track network metrics + pub resource_timing: ResourceFetchTiming, } impl Response { - pub fn new(url: ServoUrl) -> Response { + pub fn new(url: ServoUrl, resource_timing: ResourceFetchTiming) -> Response { Response { response_type: ResponseType::Default, termination_reason: None, @@ -141,11 +144,12 @@ impl Response { internal_response: None, return_internal: true, aborted: Arc::new(AtomicBool::new(false)), + resource_timing: resource_timing, } } - pub fn from_init(init: ResponseInit) -> Response { - let mut res = Response::new(init.url); + pub fn from_init(init: ResponseInit, resource_timing_type: ResourceTimingType) -> Response { + let mut res = Response::new(init.url, ResourceFetchTiming::new(resource_timing_type)); res.location_url = init.location_url; res.headers = init.headers; res.referrer = init.referrer; @@ -174,6 +178,7 @@ impl Response { internal_response: None, return_internal: true, aborted: Arc::new(AtomicBool::new(false)), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Error), } } @@ -219,6 +224,10 @@ impl Response { } } + pub fn get_resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + /// Convert to a filtered response, of type `filter_type`. /// Do not use with type Error or Default #[cfg_attr(rustfmt, rustfmt_skip)] diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 62a2727cade..1b430cb12ff 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -80,7 +80,7 @@ use net_traits::request::{Request, RequestInit}; use net_traits::response::HttpsState; use net_traits::response::{Response, ResponseBody}; use net_traits::storage_thread::StorageType; -use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; +use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads}; use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; @@ -466,6 +466,7 @@ unsafe_no_jsmanaged_fields!(AnalysisEngine, DistanceModel, PanningModel, ParamTy unsafe_no_jsmanaged_fields!(dyn Player<Error = ServoMediaError>); unsafe_no_jsmanaged_fields!(Mutex<MediaFrameRenderer>); unsafe_no_jsmanaged_fields!(RenderApiSender); +unsafe_no_jsmanaged_fields!(ResourceFetchTiming); unsafe impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 367ce9e2377..e15d735527b 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -356,6 +356,8 @@ pub struct Document { top_level_dom_complete: Cell<u64>, load_event_start: Cell<u64>, load_event_end: Cell<u64>, + unload_event_start: Cell<u64>, + unload_event_end: Cell<u64>, /// <https://html.spec.whatwg.org/multipage/#concept-document-https-state> https_state: Cell<HttpsState>, /// The document's origin. @@ -406,6 +408,8 @@ pub struct Document { fired_unload: Cell<bool>, /// List of responsive images responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>, + /// Number of redirects for the document load + redirect_count: Cell<u16>, } #[derive(JSTraceable, MallocSizeOf)] @@ -2290,6 +2294,14 @@ impl Document { self.load_event_end.get() } + pub fn get_unload_event_start(&self) -> u64 { + self.unload_event_start.get() + } + + pub fn get_unload_event_end(&self) -> u64 { + self.unload_event_end.get() + } + pub fn start_tti(&self) { if self.get_interactive_metrics().needs_tti() { self.tti_window.borrow_mut().start_window(); @@ -2654,6 +2666,8 @@ impl Document { top_level_dom_complete: Cell::new(Default::default()), load_event_start: Cell::new(Default::default()), load_event_end: Cell::new(Default::default()), + unload_event_start: Cell::new(Default::default()), + unload_event_end: Cell::new(Default::default()), https_state: Cell::new(HttpsState::None), origin: origin, referrer: referrer, @@ -2674,6 +2688,7 @@ impl Document { salvageable: Cell::new(true), fired_unload: Cell::new(false), responsive_images: Default::default(), + redirect_count: Cell::new(0), } } @@ -2739,6 +2754,14 @@ impl Document { document } + pub fn get_redirect_count(&self) -> u16 { + self.redirect_count.get() + } + + pub fn set_redirect_count(&self, count: u16) { + self.redirect_count.set(count) + } + fn create_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> DomRoot<NodeList> { let doc = self.GetDocumentElement(); let maybe_node = doc.r().map(Castable::upcast::<Node>); @@ -4268,6 +4291,9 @@ impl DocumentMethods for Document { return Ok(DomRoot::from_ref(self)); } + // TODO: prompt to unload. + // TODO: set unload_event_start and unload_event_end + window_from_node(self).set_navigation_start(); // Step 7 diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 4484d217752..10c912fb8e4 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -16,8 +16,9 @@ use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::messageevent::MessageEvent; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::fetch::FetchCanceller; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::OneshotTimerCallback; use dom_struct::dom_struct; @@ -34,6 +35,7 @@ use net_traits::request::{CacheMode, CorsSettings, CredentialsMode}; use net_traits::request::{RequestInit, RequestMode}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata}; use net_traits::{FetchResponseListener, FetchResponseMsg, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_atoms::Atom; use servo_url::ServoUrl; use std::cell::Cell; @@ -91,6 +93,8 @@ struct EventSourceContext { event_type: String, data: String, last_event_id: String, + + resource_timing: ResourceFetchTiming, } impl EventSourceContext { @@ -398,12 +402,34 @@ impl FetchResponseListener for EventSourceContext { } } - fn process_response_eof(&mut self, _response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, _response: Result<ResourceFetchTiming, NetworkError>) { if let Some(_) = self.incomplete_utf8.take() { self.parse("\u{FFFD}".chars()); } self.reestablish_the_connection(); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for EventSourceContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + (InitiatorType::Other, self.event_source.root().url().clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.event_source.root().global() + } } impl PreInvoke for EventSourceContext { @@ -463,6 +489,10 @@ impl EventSource { self.request.borrow().clone().unwrap() } + pub fn url(&self) -> &ServoUrl { + &self.url + } + // https://html.spec.whatwg.org/multipage/#dom-eventsource pub fn Constructor( global: &GlobalScope, @@ -533,6 +563,7 @@ impl EventSource { event_type: String::new(), data: String::new(), last_event_id: String::new(), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), }; let listener = NetworkListener { context: Arc::new(Mutex::new(context)), diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 287688193c0..b07166fde81 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -25,6 +25,7 @@ use crate::dom::element::{reflect_cross_origin_attribute, set_cross_origin_attri use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers}; use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlareaelement::HTMLAreaElement; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlformelement::{FormControl, HTMLFormElement}; @@ -33,15 +34,17 @@ use crate::dom::htmlpictureelement::HTMLPictureElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::mouseevent::MouseEvent; use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext}; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::progressevent::ProgressEvent; use crate::dom::values::UNSIGNED_LONG_MAX; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; use crate::microtask::{Microtask, MicrotaskRunnable}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use cssparser::{Parser, ParserInput}; + use dom_struct::dom_struct; use euclid::Point2D; use html5ever::{LocalName, Prefix}; @@ -54,6 +57,7 @@ use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvail use net_traits::image_cache::{ImageResponder, ImageResponse, ImageState, PendingImageId}; use net_traits::request::RequestInit; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use num_traits::ToPrimitive; use servo_url::origin::MutableOrigin; use servo_url::ServoUrl; @@ -164,6 +168,11 @@ struct ImageContext { id: PendingImageId, /// Used to mark abort aborted: Cell<bool>, + /// The document associated with this request + doc: Trusted<Document>, + /// timing data for this resource + resource_timing: ResourceFetchTiming, + url: ServoUrl, } impl FetchResponseListener for ImageContext { @@ -213,10 +222,35 @@ impl FetchResponseListener for ImageContext { } } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { self.image_cache .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for ImageContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::LocalName("img".to_string()), + self.url.clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.doc.root().global() + } } impl PreInvoke for ImageContext { @@ -306,6 +340,9 @@ impl HTMLImageElement { status: Ok(()), id: id, aborted: Cell::new(false), + doc: Trusted::new(&document), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + url: img_url.clone(), })); let (action_sender, action_receiver) = ipc::channel().unwrap(); diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 8092b4a90eb..b5cc9d9b1fd 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -25,16 +25,18 @@ use crate::dom::blob::Blob; use crate::dom::document::Document; use crate::dom::element::{AttributeMutation, Element}; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::mediaerror::MediaError; use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext}; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::promise::Promise; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::FetchCanceller; use crate::microtask::{Microtask, MicrotaskRunnable}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; @@ -46,8 +48,8 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use mime::{self, Mime}; use net_traits::request::{CredentialsMode, Destination, RequestInit}; -use net_traits::NetworkError; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata}; +use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerEvent, StreamType}; @@ -706,7 +708,10 @@ impl HTMLMediaElement { ..RequestInit::default() }; - let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self))); + let context = Arc::new(Mutex::new(HTMLMediaElementContext::new( + self, + self.resource_url.borrow().as_ref().unwrap().clone(), + ))); let (action_sender, action_receiver) = ipc::channel().unwrap(); let window = window_from_node(self); let (task_source, canceller) = window @@ -1459,6 +1464,10 @@ struct HTMLMediaElementContext { next_progress_event: Timespec, /// True if this response is invalid and should be ignored. ignore_response: bool, + /// timing data for this resource + resource_timing: ResourceFetchTiming, + /// url for the resource + url: ServoUrl, } // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list @@ -1527,7 +1536,7 @@ impl FetchResponseListener for HTMLMediaElementContext { } // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list - fn process_response_eof(&mut self, status: Result<(), NetworkError>) { + fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) { if self.ignore_response { // An error was received previously, skip processing the payload. return; @@ -1579,6 +1588,35 @@ impl FetchResponseListener for HTMLMediaElementContext { elem.queue_dedicated_media_source_failure_steps(); } } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for HTMLMediaElementContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + (document_from_node(&*self.elem.root()).global()) + } } impl PreInvoke for HTMLMediaElementContext { @@ -1589,13 +1627,15 @@ impl PreInvoke for HTMLMediaElementContext { } impl HTMLMediaElementContext { - fn new(elem: &HTMLMediaElement) -> HTMLMediaElementContext { + fn new(elem: &HTMLMediaElement, url: ServoUrl) -> HTMLMediaElementContext { HTMLMediaElementContext { elem: Trusted::new(elem), metadata: None, generation_id: elem.generation_id.get(), next_progress_event: time::get_time() + Duration::milliseconds(350), ignore_response: false, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + url: url, } } } diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index bc70965b1ab..a76f506f352 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -23,8 +23,9 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::node::{document_from_node, window_from_node}; use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node}; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::virtualmethods::VirtualMethods; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use dom_struct::dom_struct; use encoding_rs::Encoding; use html5ever::{LocalName, Prefix}; @@ -33,6 +34,7 @@ use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_atoms::Atom; use servo_config::opts; use servo_url::ServoUrl; @@ -166,6 +168,8 @@ struct ScriptContext { url: ServoUrl, /// Indicates whether the request failed, and why status: Result<(), NetworkError>, + /// Timing object for this resource + resource_timing: ResourceFetchTiming, } impl FetchResponseListener for ScriptContext { @@ -208,7 +212,7 @@ impl FetchResponseListener for ScriptContext { /// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script> /// step 4-9 - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { // Step 5. let load = response.and(self.status.clone()).map(|_| { let metadata = self.metadata.take().unwrap(); @@ -241,6 +245,35 @@ impl FetchResponseListener for ScriptContext { document.finish_load(LoadType::Script(self.url.clone())); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for ScriptContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + (document_from_node(&*self.elem.root()).global()) + } } impl PreInvoke for ScriptContext {} @@ -290,6 +323,7 @@ fn fetch_a_classic_script( metadata: None, url: url.clone(), status: Ok(()), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); let (action_sender, action_receiver) = ipc::channel().unwrap(); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index c03a5a5206f..b9a9bc60663 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -415,10 +415,11 @@ pub mod performance; pub mod performanceentry; pub mod performancemark; pub mod performancemeasure; +pub mod performancenavigationtiming; pub mod performanceobserver; pub mod performanceobserverentrylist; pub mod performancepainttiming; -pub mod performancetiming; +pub mod performanceresourcetiming; pub mod permissions; pub mod permissionstatus; pub mod plugin; diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index f5d041d2903..857dbe569b6 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -11,15 +11,16 @@ use crate::dom::bindings::codegen::Bindings::PerformanceBinding::{ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; -use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::performanceentry::PerformanceEntry; use crate::dom::performancemark::PerformanceMark; use crate::dom::performancemeasure::PerformanceMeasure; +use crate::dom::performancenavigationtiming::PerformanceNavigationTiming; use crate::dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver; -use crate::dom::performancetiming::PerformanceTiming; use crate::dom::window::Window; use dom_struct::dom_struct; use metrics::ToMs; @@ -131,8 +132,7 @@ struct PerformanceObserver { #[dom_struct] pub struct Performance { - reflector_: Reflector, - timing: Option<Dom<PerformanceTiming>>, + eventtarget: EventTarget, entries: DomRefCell<PerformanceEntryList>, observers: DomRefCell<Vec<PerformanceObserver>>, pending_notification_observers_task: Cell<bool>, @@ -140,22 +140,9 @@ pub struct Performance { } impl Performance { - fn new_inherited( - global: &GlobalScope, - navigation_start: u64, - navigation_start_precise: u64, - ) -> Performance { + fn new_inherited(navigation_start_precise: u64) -> Performance { Performance { - reflector_: Reflector::new(), - timing: if global.is::<Window>() { - Some(Dom::from_ref(&*PerformanceTiming::new( - global.as_window(), - navigation_start, - navigation_start_precise, - ))) - } else { - None - }, + eventtarget: EventTarget::new_inherited(), entries: DomRefCell::new(PerformanceEntryList::new(Vec::new())), observers: DomRefCell::new(Vec::new()), pending_notification_observers_task: Cell::new(false), @@ -163,17 +150,9 @@ impl Performance { } } - pub fn new( - global: &GlobalScope, - navigation_start: u64, - navigation_start_precise: u64, - ) -> DomRoot<Performance> { + pub fn new(global: &GlobalScope, navigation_start_precise: u64) -> DomRoot<Performance> { reflect_dom_object( - Box::new(Performance::new_inherited( - global, - navigation_start, - navigation_start_precise, - )), + Box::new(Performance::new_inherited(navigation_start_precise)), global, PerformanceBinding::Wrap, ) @@ -296,21 +275,23 @@ impl Performance { } fn now(&self) -> f64 { - let nav_start = match self.timing { - Some(ref timing) => timing.navigation_start_precise(), - None => self.navigation_start_precise, - }; - (time::precise_time_ns() - nav_start).to_ms() + (time::precise_time_ns() - self.navigation_start_precise).to_ms() } } impl PerformanceMethods for Performance { + // FIXME(avada): this should be deprecated in the future, but some sites still use it // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute - fn Timing(&self) -> DomRoot<PerformanceTiming> { - match self.timing { - Some(ref timing) => DomRoot::from_ref(&*timing), - None => unreachable!("Are we trying to expose Performance.timing in workers?"), + fn Timing(&self) -> DomRoot<PerformanceNavigationTiming> { + let entries = self.GetEntriesByType(DOMString::from("navigation")); + if entries.len() > 0 { + return DomRoot::from_ref( + entries[0] + .downcast::<PerformanceNavigationTiming>() + .unwrap(), + ); } + unreachable!("Are we trying to expose Performance.timing in workers?"); } // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now @@ -318,6 +299,11 @@ impl PerformanceMethods for Performance { Finite::wrap(self.now()) } + // https://www.w3.org/TR/hr-time-2/#dom-performance-timeorigin + fn TimeOrigin(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.navigation_start_precise as f64) + } + // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries fn GetEntries(&self) -> Vec<DomRoot<PerformanceEntry>> { self.entries diff --git a/components/script/dom/performanceentry.rs b/components/script/dom/performanceentry.rs index 7317532f226..334d8b6de85 100644 --- a/components/script/dom/performanceentry.rs +++ b/components/script/dom/performanceentry.rs @@ -59,6 +59,10 @@ impl PerformanceEntry { pub fn start_time(&self) -> f64 { self.start_time } + + pub fn duration(&self) -> f64 { + self.duration + } } impl PerformanceEntryMethods for PerformanceEntry { diff --git a/components/script/dom/performancenavigationtiming.rs b/components/script/dom/performancenavigationtiming.rs new file mode 100644 index 00000000000..8e9ef66c938 --- /dev/null +++ b/components/script/dom/performancenavigationtiming.rs @@ -0,0 +1,126 @@ +/* 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 crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; +use crate::dom::bindings::codegen::Bindings::PerformanceNavigationTimingBinding::PerformanceNavigationTimingMethods; +use crate::dom::bindings::codegen::Bindings::PerformanceNavigationTimingBinding::{ + self, NavigationType, +}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::document::Document; +use crate::dom::globalscope::GlobalScope; +use crate::dom::performanceresourcetiming::{InitiatorType, PerformanceResourceTiming}; +use dom_struct::dom_struct; + +#[dom_struct] +// https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming +/// Only the current document resource is included in the performance timeline; +/// there is only one PerformanceNavigationTiming object in the performance timeline. +pub struct PerformanceNavigationTiming { + // https://w3c.github.io/navigation-timing/#PerformanceResourceTiming + performanceresourcetiming: PerformanceResourceTiming, + navigation_start: u64, + navigation_start_precise: u64, + document: Dom<Document>, + nav_type: NavigationType, +} + +impl PerformanceNavigationTiming { + fn new_inherited( + nav_start: u64, + nav_start_precise: u64, + document: &Document, + ) -> PerformanceNavigationTiming { + PerformanceNavigationTiming { + performanceresourcetiming: PerformanceResourceTiming::new_inherited( + document.url(), + InitiatorType::Navigation, + None, + nav_start_precise as f64, + ), + navigation_start: nav_start, + navigation_start_precise: nav_start_precise, + document: Dom::from_ref(document), + nav_type: NavigationType::Navigate, + } + } + + pub fn new( + global: &GlobalScope, + nav_start: u64, + nav_start_precise: u64, + document: &Document, + ) -> DomRoot<PerformanceNavigationTiming> { + reflect_dom_object( + Box::new(PerformanceNavigationTiming::new_inherited( + nav_start, + nav_start_precise, + document, + )), + global, + PerformanceNavigationTimingBinding::Wrap, + ) + } +} + +// https://w3c.github.io/navigation-timing/ +impl PerformanceNavigationTimingMethods for PerformanceNavigationTiming { + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-unloadeventstart + fn UnloadEventStart(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_unload_event_start() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-unloadeventend + fn UnloadEventEnd(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_unload_event_end() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-dominteractive + fn DomInteractive(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_interactive() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcontentloadedeventstart + fn DomContentLoadedEventStart(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_content_loaded_event_start() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcontentloadedeventstart + fn DomContentLoadedEventEnd(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_content_loaded_event_end() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcomplete + fn DomComplete(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_complete() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-loadeventstart + fn LoadEventStart(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_load_event_start() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-loadeventend + fn LoadEventEnd(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_load_event_end() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-type + fn Type(&self) -> NavigationType { + self.nav_type.clone() + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-redirectcount + fn RedirectCount(&self) -> u16 { + self.document.get_redirect_count() + } + + // check-tidy: no specs after this line + // Servo-only timing for when top-level content (not iframes) is complete + fn TopLevelDomComplete(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_top_level_dom_complete() as f64) + } +} diff --git a/components/script/dom/performanceobserver.rs b/components/script/dom/performanceobserver.rs index cf7ba499f28..dc66e88b5a3 100644 --- a/components/script/dom/performanceobserver.rs +++ b/components/script/dom/performanceobserver.rs @@ -22,9 +22,11 @@ use std::rc::Rc; /// List of allowed performance entry types. const VALID_ENTRY_TYPES: &'static [&'static str] = &[ - "mark", // User Timing API - "measure", // User Timing API - // "resource", XXX Resource Timing API + "mark", // User Timing API + "measure", // User Timing API + "resource", // Resource Timing API + "navigation", // Navigation Timing API + // "frame", //TODO Frame Timing API // "server", XXX Server Timing API "paint", // Paint Timing API ]; diff --git a/components/script/dom/performanceresourcetiming.rs b/components/script/dom/performanceresourcetiming.rs new file mode 100644 index 00000000000..22842160267 --- /dev/null +++ b/components/script/dom/performanceresourcetiming.rs @@ -0,0 +1,187 @@ +/* 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 crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; +use crate::dom::bindings::codegen::Bindings::PerformanceResourceTimingBinding::{ + self, PerformanceResourceTimingMethods, +}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::dom::performanceentry::PerformanceEntry; +use dom_struct::dom_struct; +use net_traits::ResourceFetchTiming; +use servo_url::ServoUrl; + +// TODO UA may choose to limit how many resources are included as PerformanceResourceTiming objects +// recommended minimum is 150, can be changed by setResourceTimingBufferSize in performance +// https://w3c.github.io/resource-timing/#sec-extensions-performance-interface + +// TODO Cross origin resources MUST BE INCLUDED as PerformanceResourceTiming objects +// https://w3c.github.io/resource-timing/#sec-cross-origin-resources + +// TODO CSS, Beacon +#[derive(Debug, JSTraceable, MallocSizeOf, PartialEq)] +pub enum InitiatorType { + LocalName(String), + Navigation, + XMLHttpRequest, + Fetch, + Other, +} + +#[dom_struct] +pub struct PerformanceResourceTiming { + entry: PerformanceEntry, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + worker_start: f64, + redirect_start: f64, + redirect_end: f64, + fetch_start: f64, + domain_lookup_start: f64, + domain_lookup_end: f64, + connect_start: f64, + connect_end: f64, + secure_connection_start: f64, + request_start: f64, + response_start: f64, + response_end: f64, + // transfer_size: f64, //size in octets + // encoded_body_size: f64, //size in octets + // decoded_body_size: f64, //size in octets +} + +// TODO(#21254): startTime +// TODO(#21255): duration +// TODO(#21269): next_hop +// TODO(#21264): worker_start +// TODO(#21256): redirect_start +// TODO(#21257): redirect_end +// TODO(#21258): fetch_start +// TODO(#21259): domain_lookup_start +// TODO(#21260): domain_lookup_end +// TODO(#21261): connect_start +// TODO(#21262): connect_end +// TODO(#21263): response_end +impl PerformanceResourceTiming { + pub fn new_inherited( + url: ServoUrl, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + fetch_start: f64, + ) -> PerformanceResourceTiming { + PerformanceResourceTiming { + entry: PerformanceEntry::new_inherited( + DOMString::from(url.into_string()), + DOMString::from("resource"), + fetch_start, + 0., + ), + initiator_type: initiator_type, + next_hop: next_hop, + worker_start: 0., + redirect_start: 0., + redirect_end: 0., + fetch_start: fetch_start, + domain_lookup_start: 0., + domain_lookup_end: 0., + connect_start: 0., + connect_end: 0., + secure_connection_start: 0., + request_start: 0., + response_start: 0., + response_end: 0., + } + } + + //TODO fetch start should be in RFT + #[allow(unrooted_must_root)] + fn from_resource_timing( + url: ServoUrl, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + resource_timing: &ResourceFetchTiming, + ) -> PerformanceResourceTiming { + PerformanceResourceTiming { + entry: PerformanceEntry::new_inherited( + DOMString::from(url.into_string()), + DOMString::from("resource"), + 0., + 0., + ), + initiator_type: initiator_type, + next_hop: next_hop, + worker_start: 0., + redirect_start: 0., + redirect_end: 0., + fetch_start: 0., + domain_lookup_start: 0., + domain_lookup_end: 0., + connect_start: 0., + connect_end: 0., + secure_connection_start: 0., + request_start: resource_timing.request_start as f64, + response_start: resource_timing.response_start as f64, + response_end: 0., + } + } + + pub fn new( + global: &GlobalScope, + url: ServoUrl, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + resource_timing: &ResourceFetchTiming, + ) -> DomRoot<PerformanceResourceTiming> { + reflect_dom_object( + Box::new(PerformanceResourceTiming::from_resource_timing( + url, + initiator_type, + next_hop, + resource_timing, + )), + global, + PerformanceResourceTimingBinding::Wrap, + ) + } +} + +// https://w3c.github.io/resource-timing/ +impl PerformanceResourceTimingMethods for PerformanceResourceTiming { + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-initiatortype + fn InitiatorType(&self) -> DOMString { + match self.initiator_type { + InitiatorType::LocalName(ref n) => DOMString::from(n.clone()), + InitiatorType::Navigation => DOMString::from("navigation"), + InitiatorType::XMLHttpRequest => DOMString::from("xmlhttprequest"), + InitiatorType::Fetch => DOMString::from("fetch"), + InitiatorType::Other => DOMString::from("other"), + } + } + + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-nexthopprotocol + // returns the ALPN protocol ID of the network protocol used to fetch the resource + // when a proxy is configured + fn NextHopProtocol(&self) -> DOMString { + match self.next_hop { + Some(ref protocol) => DOMString::from(protocol.clone()), + None => DOMString::from(""), + } + } + + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart + fn RequestStart(&self) -> DOMHighResTimeStamp { + // TODO + Finite::wrap(self.request_start) + } + + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart + fn ResponseStart(&self) -> DOMHighResTimeStamp { + // TODO + Finite::wrap(self.response_start) + } +} diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index a023fb19651..3c7e45f91de 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::ServoParserBinding; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; -use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference}; use crate::dom::bindings::settings_stack::is_execution_stack_empty; use crate::dom::bindings::str::DOMString; @@ -28,6 +28,8 @@ use crate::dom::htmlimageelement::HTMLImageElement; use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use crate::dom::htmltemplateelement::HTMLTemplateElement; use crate::dom::node::Node; +use crate::dom::performanceentry::PerformanceEntry; +use crate::dom::performancenavigationtiming::PerformanceNavigationTiming; use crate::dom::processinginstruction::ProcessingInstruction; use crate::dom::text::Text; use crate::dom::virtualmethods::vtable_for; @@ -43,8 +45,10 @@ use hyper_serde::Serde; use mime::{self, Mime}; use msg::constellation_msg::PipelineId; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; -use profile_traits::time::{profile, ProfilerCategory, TimerMetadataReflowType}; -use profile_traits::time::{TimerMetadata, TimerMetadataFrameType}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; +use profile_traits::time::{ + profile, ProfilerCategory, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, +}; use script_traits::DocumentActivity; use servo_config::prefs::PREFS; use servo_url::ServoUrl; @@ -656,6 +660,8 @@ pub struct ParserContext { id: PipelineId, /// The URL for this document. url: ServoUrl, + /// timing data for this resource + resource_timing: ResourceFetchTiming, } impl ParserContext { @@ -665,6 +671,7 @@ impl ParserContext { is_synthesized_document: false, id: id, url: url, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Navigation), } } } @@ -792,7 +799,10 @@ impl FetchResponseListener for ParserContext { parser.parse_bytes_chunk(payload); } - fn process_response_eof(&mut self, status: Result<(), NetworkError>) { + // This method is called via script_thread::handle_fetch_eof, so we must call + // submit_resource_timing in this function + // Resource listeners are called via net_traits::Action::process, which handles submission for them + fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) { let parser = match self.parser.as_ref() { Some(parser) => parser.root(), None => return, @@ -801,15 +811,53 @@ impl FetchResponseListener for ParserContext { return; } - if let Err(err) = status { + match status { + // are we throwing this away or can we use it? + Ok(_) => (), // TODO(Savago): we should send a notification to callers #5463. - debug!("Failed to load page URL {}, error: {:?}", self.url, err); + Err(err) => debug!("Failed to load page URL {}, error: {:?}", self.url, err), } + parser + .document + .set_redirect_count(self.resource_timing.redirect_count); + parser.last_chunk_received.set(true); if !parser.suspended.get() { parser.parse_sync(); } + + //TODO only submit if this is the current document resource + self.submit_resource_timing(); + } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + // store a PerformanceNavigationTiming entry in the globalscope's Performance buffer + fn submit_resource_timing(&mut self) { + let parser = match self.parser.as_ref() { + Some(parser) => parser.root(), + None => return, + }; + if parser.aborted.get() { + return; + } + + let document = &parser.document; + + //TODO nav_start and nav_start_precise + let performance_entry = + PerformanceNavigationTiming::new(&document.global(), 0, 0, &document); + document + .global() + .performance() + .queue_entry(performance_entry.upcast::<PerformanceEntry>(), true); } } diff --git a/components/script/dom/webidls/Performance.webidl b/components/script/dom/webidls/Performance.webidl index 85f8fb58b27..52f5c7e1797 100644 --- a/components/script/dom/webidls/Performance.webidl +++ b/components/script/dom/webidls/Performance.webidl @@ -10,14 +10,10 @@ typedef double DOMHighResTimeStamp; typedef sequence<PerformanceEntry> PerformanceEntryList; [Exposed=(Window, Worker)] -interface Performance { +interface Performance : EventTarget { DOMHighResTimeStamp now(); -}; - -[Exposed=(Window)] -partial interface Performance { - readonly attribute PerformanceTiming timing; - /* readonly attribute PerformanceNavigation navigation; */ + readonly attribute DOMHighResTimeStamp timeOrigin; + // [Default] object toJSON(); }; // https://w3c.github.io/performance-timeline/#extensions-to-the-performance-interface @@ -39,3 +35,10 @@ partial interface Performance { void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark); void clearMeasures(optional DOMString measureName); }; + +// FIXME(avada): this should be deprecated, but is currently included for web compat +// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute +[Exposed=(Window)] +partial interface Performance { + PerformanceNavigationTiming timing(); +}; diff --git a/components/script/dom/webidls/PerformanceNavigationTiming.webidl b/components/script/dom/webidls/PerformanceNavigationTiming.webidl new file mode 100644 index 00000000000..cde173c67e2 --- /dev/null +++ b/components/script/dom/webidls/PerformanceNavigationTiming.webidl @@ -0,0 +1,32 @@ +/* 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/. */ +/* + * The origin of this IDL file is + * https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming + */ + +enum NavigationType { + "navigate", + "reload", + "back_forward", + "prerender" +}; + +[Exposed=Window] +interface PerformanceNavigationTiming : PerformanceResourceTiming { + readonly attribute DOMHighResTimeStamp unloadEventStart; + readonly attribute DOMHighResTimeStamp unloadEventEnd; + readonly attribute DOMHighResTimeStamp domInteractive; + readonly attribute DOMHighResTimeStamp domContentLoadedEventStart; + readonly attribute DOMHighResTimeStamp domContentLoadedEventEnd; + readonly attribute DOMHighResTimeStamp domComplete; + readonly attribute DOMHighResTimeStamp loadEventStart; + readonly attribute DOMHighResTimeStamp loadEventEnd; + readonly attribute NavigationType type; + readonly attribute unsigned short redirectCount; + // [Default] object toJSON(); + /* Servo-only attribute for measuring when the top-level document (not iframes) is complete. */ + [Pref="dom.testperf.enabled"] + readonly attribute DOMHighResTimeStamp topLevelDomComplete; +}; diff --git a/components/script/dom/webidls/PerformanceResourceTiming.webidl b/components/script/dom/webidls/PerformanceResourceTiming.webidl new file mode 100644 index 00000000000..ca8b4c99b81 --- /dev/null +++ b/components/script/dom/webidls/PerformanceResourceTiming.webidl @@ -0,0 +1,35 @@ +/* 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/. */ +/* + * The origin of this IDL file is + * https://w3c.github.io/resource-timing/ + */ + +[Exposed=(Window,Worker)] +interface PerformanceResourceTiming : PerformanceEntry { + readonly attribute DOMString initiatorType; + readonly attribute DOMString nextHopProtocol; + // readonly attribute DOMHighResTimeStamp workerStart; + // readonly attribute DOMHighResTimeStamp redirectStart; + // readonly attribute DOMHighResTimeStamp redirectEnd; + // readonly attribute DOMHighResTimeStamp fetchStart; + // readonly attribute DOMHighResTimeStamp domainLookupStart; + // readonly attribute DOMHighResTimeStamp domainLookupEnd; + // readonly attribute DOMHighResTimeStamp connectStart; + // readonly attribute DOMHighResTimeStamp connectEnd; + // readonly attribute DOMHighResTimeStamp secureConnectionStart; + readonly attribute DOMHighResTimeStamp requestStart; + readonly attribute DOMHighResTimeStamp responseStart; + // readonly attribute DOMHighResTimeStamp responseEnd; + /// readonly attribute unsigned long long transferSize; + /// readonly attribute unsigned long long encodedBodySize; + /// readonly attribute unsigned long long decodedBodySize; + // [Default] object toJSON(); +}; + +// partial interface Performance { +// void clearResourceTimings(); +// void setResourceTimingBufferSize(unsigned long maxSize); +// attribute EventHandler onresourcetimingbufferfull; +// }; diff --git a/components/script/dom/webidls/PerformanceTiming.webidl b/components/script/dom/webidls/PerformanceTiming.webidl deleted file mode 100644 index d2fcb1faec5..00000000000 --- a/components/script/dom/webidls/PerformanceTiming.webidl +++ /dev/null @@ -1,35 +0,0 @@ -/* 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 https://mozilla.org/MPL/2.0/. */ -/* - * The origin of this IDL file is - * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-navigation-timing-interface - */ - -[Exposed=(Window)] -interface PerformanceTiming { - readonly attribute unsigned long long navigationStart; - /* readonly attribute unsigned long long unloadEventStart; - readonly attribute unsigned long long unloadEventEnd; - readonly attribute unsigned long long redirectStart; - readonly attribute unsigned long long redirectEnd; - readonly attribute unsigned long long fetchStart; - readonly attribute unsigned long long domainLookupStart; - readonly attribute unsigned long long domainLookupEnd; - readonly attribute unsigned long long connectStart; - readonly attribute unsigned long long connectEnd; - readonly attribute unsigned long long secureConnectionStart; - readonly attribute unsigned long long requestStart; - readonly attribute unsigned long long responseStart; - readonly attribute unsigned long long responseEnd; */ - readonly attribute unsigned long long domLoading; - readonly attribute unsigned long long domInteractive; - readonly attribute unsigned long long domContentLoadedEventStart; - readonly attribute unsigned long long domContentLoadedEventEnd; - readonly attribute unsigned long long domComplete; - readonly attribute unsigned long long loadEventStart; - readonly attribute unsigned long long loadEventEnd; - /* Servo-onnly attribute for measuring when the top-level document (not iframes) is complete. */ - [Pref="dom.testperf.enabled"] - readonly attribute unsigned long long topLevelDomComplete; -}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 338c78111e5..b07998de6d4 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -812,11 +812,7 @@ impl WindowMethods for Window { fn Performance(&self) -> DomRoot<Performance> { self.performance.or_init(|| { let global_scope = self.upcast::<GlobalScope>(); - Performance::new( - global_scope, - self.navigation_start.get(), - self.navigation_start_precise.get(), - ) + Performance::new(global_scope, self.navigation_start_precise.get()) }) } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index fb29bc1c740..99cee52eaa1 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -357,11 +357,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { fn Performance(&self) -> DomRoot<Performance> { self.performance.or_init(|| { let global_scope = self.upcast::<GlobalScope>(); - Performance::new( - global_scope, - 0, /* navigation start is not used in workers */ - self.navigation_start_precise, - ) + Performance::new(global_scope, self.navigation_start_precise) }) } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 3e1644536ab..18628f27f70 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -28,6 +28,7 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::headers::is_forbidden_header_name; use crate::dom::htmlformelement::{encode_multipart_form_data, generate_boundary}; use crate::dom::node::Node; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::progressevent::ProgressEvent; use crate::dom::servoparser::ServoParser; use crate::dom::urlsearchparams::URLSearchParams; @@ -36,7 +37,7 @@ use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; use crate::dom::xmlhttprequestupload::XMLHttpRequestUpload; use crate::fetch::FetchCanceller; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::TaskSourceName; use crate::timers::{OneshotTimerCallback, OneshotTimerHandle}; @@ -63,6 +64,7 @@ use net_traits::trim_http_whitespace; use net_traits::CoreResourceMsg::Fetch; use net_traits::{FetchChannels, FetchMetadata, FilteredMetadata}; use net_traits::{FetchResponseListener, NetworkError, ReferrerPolicy}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use script_traits::DocumentActivity; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -95,6 +97,7 @@ struct XHRContext { gen_id: GenerationId, buf: DomRefCell<Vec<u8>>, sync_status: DomRefCell<Option<ErrorResult>>, + resource_timing: ResourceFetchTiming, } #[derive(Clone)] @@ -257,13 +260,41 @@ impl XMLHttpRequest { .process_data_available(self.gen_id, self.buf.borrow().clone()); } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof( + &mut self, + response: Result<ResourceFetchTiming, NetworkError>, + ) { let rv = self .xhr .root() - .process_response_complete(self.gen_id, response); + .process_response_complete(self.gen_id, response.map(|_| ())); *self.sync_status.borrow_mut() = Some(rv); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } + } + + impl ResourceTimingListener for XHRContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::XMLHttpRequest, + self.resource_timing_global().get_url().clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.xhr.root().global() + } } impl PreInvoke for XHRContext { @@ -1424,6 +1455,7 @@ impl XMLHttpRequest { gen_id: self.generation_id.get(), buf: DomRefCell::new(vec![]), sync_status: DomRefCell::new(None), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); let (task_source, script_port) = if self.sync.get() { diff --git a/components/script/fetch.rs b/components/script/fetch.rs index 49e441740bc..922ee7d9d03 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -14,11 +14,12 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::globalscope::GlobalScope; use crate::dom::headers::Guard; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::promise::Promise; use crate::dom::request::Request; use crate::dom::response::Response; use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::task_source::TaskSourceName; use ipc_channel::ipc; use ipc_channel::router::ROUTER; @@ -28,6 +29,7 @@ use net_traits::request::{Request as NetTraitsRequest, ServiceWorkersMode}; use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch; use net_traits::{FetchChannels, FetchResponseListener, NetworkError}; use net_traits::{FetchMetadata, FilteredMetadata, Metadata}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_url::ServoUrl; use std::mem; use std::rc::Rc; @@ -37,6 +39,7 @@ struct FetchContext { fetch_promise: Option<TrustedPromise>, response_object: Trusted<Response>, body: Vec<u8>, + resource_timing: ResourceFetchTiming, } /// RAII fetch canceller object. By default initialized to not having a canceller @@ -143,6 +146,8 @@ pub fn Fetch( }, Ok(r) => r.get_request(), }; + let timing_type = request.timing_type(); + let mut request_init = request_init_from_request(request); // Step 3 @@ -159,6 +164,7 @@ pub fn Fetch( fetch_promise: Some(TrustedPromise::new(promise.clone())), response_object: Trusted::new(&*response), body: vec![], + resource_timing: ResourceFetchTiming::new(timing_type), })); let listener = NetworkListener { context: fetch_context, @@ -250,7 +256,7 @@ impl FetchResponseListener for FetchContext { self.body.append(&mut chunk); } - fn process_response_eof(&mut self, _response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, _response: Result<ResourceFetchTiming, NetworkError>) { let response = self.response_object.root(); let global = response.global(); let cx = global.get_cx(); @@ -259,6 +265,35 @@ impl FetchResponseListener for FetchContext { // TODO // ... trailerObject is not supported in Servo yet. } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + // navigation submission is handled in servoparser/mod.rs + match self.resource_timing.timing_type { + ResourceTimingType::Resource => network_listener::submit_timing(self), + _ => {}, + }; + } +} + +impl ResourceTimingListener for FetchContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::Fetch, + self.resource_timing_global().get_url().clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.response_object.root().global() + } } fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata) { diff --git a/components/script/layout_image.rs b/components/script/layout_image.rs index 59dac86e784..e47218cffda 100644 --- a/components/script/layout_image.rs +++ b/components/script/layout_image.rs @@ -7,20 +7,28 @@ //! no guarantee that the responsible nodes will still exist in the future if the //! layout thread holds on to them during asynchronous operations. +use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::root::DomRoot; +use crate::dom::document::Document; +use crate::dom::globalscope::GlobalScope; use crate::dom::node::{document_from_node, Node}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::dom::performanceresourcetiming::InitiatorType; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use net_traits::image_cache::{ImageCache, PendingImageId}; use net_traits::request::{Destination, RequestInit as FetchRequestInit}; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_url::ServoUrl; use std::sync::{Arc, Mutex}; struct LayoutImageContext { id: PendingImageId, cache: Arc<dyn ImageCache>, + resource_timing: ResourceFetchTiming, + doc: Trusted<Document>, } impl FetchResponseListener for LayoutImageContext { @@ -36,10 +44,35 @@ impl FetchResponseListener for LayoutImageContext { .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseChunk(payload)); } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { self.cache .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for LayoutImageContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::Other, + self.resource_timing_global().get_url().clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.doc.root().global() + } } impl PreInvoke for LayoutImageContext {} @@ -50,9 +83,13 @@ pub fn fetch_image_for_layout( id: PendingImageId, cache: Arc<dyn ImageCache>, ) { + let document = document_from_node(node); + let context = Arc::new(Mutex::new(LayoutImageContext { id: id, cache: cache, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + doc: Trusted::new(&document), })); let document = document_from_node(node); diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs index 99042d42c8d..53e9360eee0 100644 --- a/components/script/network_listener.rs +++ b/components/script/network_listener.rs @@ -2,10 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::dom::performanceentry::PerformanceEntry; +use crate::dom::performanceresourcetiming::{InitiatorType, PerformanceResourceTiming}; use crate::task::{TaskCanceller, TaskOnce}; use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::TaskSource; -use net_traits::{Action, FetchResponseListener, FetchResponseMsg}; +use net_traits::{Action, FetchResponseListener, FetchResponseMsg, ResourceTimingType}; +use servo_url::ServoUrl; use std::sync::{Arc, Mutex}; /// An off-thread sink for async network event tasks. All such events are forwarded to @@ -16,6 +22,39 @@ pub struct NetworkListener<Listener: PreInvoke + Send + 'static> { pub canceller: Option<TaskCanceller>, } +pub trait ResourceTimingListener { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl); + fn resource_timing_global(&self) -> DomRoot<GlobalScope>; +} + +pub fn submit_timing<T: ResourceTimingListener + FetchResponseListener>(listener: &T) { + if listener.resource_timing().timing_type != ResourceTimingType::Resource { + warn!( + "Submitting non-resource ({:?}) timing as resource", + listener.resource_timing().timing_type + ); + return; + } + + let (initiator_type, url) = listener.resource_timing_information(); + if initiator_type == InitiatorType::Other { + warn!("Ignoring InitiatorType::Other resource {:?}", url); + return; + } + + let global = listener.resource_timing_global(); + let performance_entry = PerformanceResourceTiming::new( + &global, + url, + initiator_type, + None, + &listener.resource_timing(), + ); + global + .performance() + .queue_entry(performance_entry.upcast::<PerformanceEntry>(), false); +} + impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> { pub fn notify<A: Action<Listener> + Send + 'static>(&self, action: A) { let task = ListenerTask { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 70c541b14c8..21c87d772e9 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -107,7 +107,10 @@ use net_traits::image_cache::{ImageCache, PendingImageResponse}; use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestInit}; use net_traits::storage_thread::StorageType; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg}; -use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; +use net_traits::{ + Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads, + ResourceTimingType, +}; use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan}; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowGoal}; @@ -3097,9 +3100,12 @@ impl ScriptThread { fetch_metadata: Result<FetchMetadata, NetworkError>, ) { match fetch_metadata { - Ok(_) => {}, - Err(ref e) => warn!("Network error: {:?}", e), + Ok(_) => (), + Err(ref e) => { + warn!("Network error: {:?}", e); + }, }; + let mut incomplete_parser_contexts = self.incomplete_parser_contexts.borrow_mut(); let parser = incomplete_parser_contexts .iter_mut() @@ -3119,12 +3125,13 @@ impl ScriptThread { } } - fn handle_fetch_eof(&self, id: PipelineId, eof: Result<(), NetworkError>) { + fn handle_fetch_eof(&self, id: PipelineId, eof: Result<ResourceFetchTiming, NetworkError>) { let idx = self .incomplete_parser_contexts .borrow() .iter() .position(|&(pipeline_id, _)| pipeline_id == id); + if let Some(idx) = idx { let (_, mut ctxt) = self.incomplete_parser_contexts.borrow_mut().remove(idx); ctxt.process_response_eof(eof); @@ -3161,7 +3168,7 @@ impl ScriptThread { context.process_response(Ok(FetchMetadata::Unfiltered(meta))); context.process_response_chunk(chunk); - context.process_response_eof(Ok(())); + context.process_response_eof(Ok(ResourceFetchTiming::new(ResourceTimingType::None))); } fn handle_css_error_reporting( diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 38957c8bf0a..88ad87ada22 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -6,13 +6,16 @@ use crate::document_loader::LoadType; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::root::DomRoot; use crate::dom::document::Document; use crate::dom::element::Element; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmllinkelement::{HTMLLinkElement, RequestGenerationId}; use crate::dom::node::{document_from_node, window_from_node}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::dom::performanceresourcetiming::InitiatorType; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use cssparser::SourceLocation; use encoding_rs::UTF_8; use ipc_channel::ipc; @@ -22,6 +25,7 @@ use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestIni use net_traits::{ FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, }; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use parking_lot::RwLock; use servo_arc::Arc; use servo_url::ServoUrl; @@ -79,6 +83,7 @@ pub struct StylesheetContext { /// A token which must match the generation id of the `HTMLLinkElement` for it to load the stylesheet. /// This is ignored for `HTMLStyleElement` and imports. request_generation_id: Option<RequestGenerationId>, + resource_timing: ResourceFetchTiming, } impl PreInvoke for StylesheetContext {} @@ -108,7 +113,7 @@ impl FetchResponseListener for StylesheetContext { self.data.append(&mut payload); } - fn process_response_eof(&mut self, status: Result<(), NetworkError>) { + fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) { let elem = self.elem.root(); let document = self.document.root(); let mut successful = false; @@ -207,6 +212,35 @@ impl FetchResponseListener for StylesheetContext { elem.upcast::<EventTarget>().fire_event(event); } } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for StylesheetContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + document_from_node(&*self.elem.root()).global() + } } pub struct StylesheetLoader<'a> { @@ -241,6 +275,7 @@ impl<'a> StylesheetLoader<'a> { document: Trusted::new(&*document), origin_clean: true, request_generation_id: gen, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); let (action_sender, action_receiver) = ipc::channel().unwrap(); diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini index 6282824bebe..9219625bfae 100644 --- a/tests/wpt/include.ini +++ b/tests/wpt/include.ini @@ -103,6 +103,8 @@ skip: true skip: false [referrer-policy] skip: false +[resource-timing] + skip: false [subresource-integrity] skip: false [touch-events] diff --git a/tests/wpt/metadata/custom-elements/microtasks-and-constructors.html.ini b/tests/wpt/metadata/custom-elements/microtasks-and-constructors.html.ini index b67eb3e525a..cc30607acbd 100644 --- a/tests/wpt/metadata/custom-elements/microtasks-and-constructors.html.ini +++ b/tests/wpt/metadata/custom-elements/microtasks-and-constructors.html.ini @@ -7,3 +7,4 @@ [Microtasks evaluate afterward when the stack is not empty using createElement()] expected: FAIL + diff --git a/tests/wpt/metadata/fetch/api/request/request-keepalive-quota.html.ini b/tests/wpt/metadata/fetch/api/request/request-keepalive-quota.html.ini index 57f7b4fc44f..c2a729a1e63 100644 --- a/tests/wpt/metadata/fetch/api/request/request-keepalive-quota.html.ini +++ b/tests/wpt/metadata/fetch/api/request/request-keepalive-quota.html.ini @@ -24,5 +24,3 @@ [A Keep-Alive fetch() with a body over the Quota Limit should reject.] expected: FAIL - -[request-keepalive-quota.html?include=slow-1] diff --git a/tests/wpt/metadata/hr-time/idlharness.any.js.ini b/tests/wpt/metadata/hr-time/idlharness.any.js.ini index 9f19d08947f..446960c4847 100644 --- a/tests/wpt/metadata/hr-time/idlharness.any.js.ini +++ b/tests/wpt/metadata/hr-time/idlharness.any.js.ini @@ -2,31 +2,21 @@ [idlharness] expected: FAIL - [Performance interface: existence and properties of interface object] - expected: FAIL - - [Performance interface: existence and properties of interface prototype object] - expected: FAIL - - [Performance interface: attribute timeOrigin] - expected: FAIL - [Performance interface: operation toJSON()] expected: FAIL - [Performance interface: performance must inherit property "timeOrigin" with the proper type] - expected: FAIL - [Performance interface: performance must inherit property "toJSON()" with the proper type] expected: FAIL [Test default toJSON operation of Performance] expected: FAIL + [idlharness.any.serviceworker.html] [idlharness] expected: FAIL + [idlharness.any.sharedworker.html] [idlharness] expected: FAIL @@ -42,21 +32,9 @@ [idlharness] expected: FAIL - [Performance interface: existence and properties of interface object] - expected: FAIL - - [Performance interface: existence and properties of interface prototype object] - expected: FAIL - - [Performance interface: attribute timeOrigin] - expected: FAIL - [Performance interface: operation toJSON()] expected: FAIL - [Performance interface: performance must inherit property "timeOrigin" with the proper type] - expected: FAIL - [Performance interface: performance must inherit property "toJSON()" with the proper type] expected: FAIL diff --git a/tests/wpt/metadata/hr-time/test_cross_frame_start.html.ini b/tests/wpt/metadata/hr-time/test_cross_frame_start.html.ini new file mode 100644 index 00000000000..b1f8757f2f6 --- /dev/null +++ b/tests/wpt/metadata/hr-time/test_cross_frame_start.html.ini @@ -0,0 +1,7 @@ +[test_cross_frame_start.html] + [Child created at least 1 second after parent] + expected: FAIL + + [Child and parent time bases are correct] + expected: FAIL + diff --git a/tests/wpt/metadata/hr-time/timeOrigin.html.ini b/tests/wpt/metadata/hr-time/timeOrigin.html.ini index 0ac417723d6..dfd84d1f80b 100644 --- a/tests/wpt/metadata/hr-time/timeOrigin.html.ini +++ b/tests/wpt/metadata/hr-time/timeOrigin.html.ini @@ -6,6 +6,3 @@ [Window and worker timeOrigins are close when created one after another.] expected: FAIL - [Window and worker timeOrigins differ when worker is created after a delay.] - expected: FAIL - diff --git a/tests/wpt/metadata/hr-time/window-worker-timeOrigin.window.js.ini b/tests/wpt/metadata/hr-time/window-worker-timeOrigin.window.js.ini deleted file mode 100644 index ed7a2abc933..00000000000 --- a/tests/wpt/metadata/hr-time/window-worker-timeOrigin.window.js.ini +++ /dev/null @@ -1,5 +0,0 @@ -[window-worker-timeOrigin.window.html] - type: testharness - [timeOrigin and now() should be correctly ordered between window and worker] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html.ini b/tests/wpt/metadata/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html.ini index 54f6b43a6f2..a3331003f7b 100644 --- a/tests/wpt/metadata/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html.ini +++ b/tests/wpt/metadata/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html.ini @@ -292,7 +292,3 @@ [<meta>: "1\\furl=foo"] expected: TIMEOUT - -[parsing.html?131-last] - -[parsing.html?81-90] diff --git a/tests/wpt/metadata/navigation-timing/dom_interactive_image_document.html.ini b/tests/wpt/metadata/navigation-timing/dom_interactive_image_document.html.ini new file mode 100644 index 00000000000..ba16ab0609f --- /dev/null +++ b/tests/wpt/metadata/navigation-timing/dom_interactive_image_document.html.ini @@ -0,0 +1,5 @@ +[dom_interactive_image_document.html] + expected: ERROR + [Test domInteractive on image document] + expected: NOTRUN + diff --git a/tests/wpt/metadata/navigation-timing/dom_interactive_media_document.html.ini b/tests/wpt/metadata/navigation-timing/dom_interactive_media_document.html.ini new file mode 100644 index 00000000000..03e4f3cf11c --- /dev/null +++ b/tests/wpt/metadata/navigation-timing/dom_interactive_media_document.html.ini @@ -0,0 +1,5 @@ +[dom_interactive_media_document.html] + expected: ERROR + [Test domInteractive on media document] + expected: NOTRUN + diff --git a/tests/wpt/metadata/navigation-timing/nav2_idlharness.html.ini b/tests/wpt/metadata/navigation-timing/nav2_idlharness.html.ini index 4b0d63606b0..5336f84e3ed 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_idlharness.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_idlharness.html.ini @@ -1,56 +1,11 @@ [nav2_idlharness.html] type: testharness - [PerformanceNavigationTiming interface: existence and properties of interface object] - expected: FAIL - - [PerformanceNavigationTiming interface object length] - expected: FAIL - - [PerformanceNavigationTiming interface object name] - expected: FAIL - - [PerformanceNavigationTiming interface: existence and properties of interface prototype object] - expected: FAIL - - [PerformanceNavigationTiming interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute unloadEventStart] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute unloadEventEnd] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute domInteractive] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute domContentLoadedEventStart] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute domContentLoadedEventEnd] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute domComplete] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute loadEventStart] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute loadEventEnd] - expected: FAIL - - [PerformanceNavigationTiming interface: attribute type] - expected: FAIL - [PerformanceNavigationTiming interface: attribute redirectCount] expected: FAIL [PerformanceNavigationTiming interface: operation toJSON()] expected: FAIL - [PerformanceNavigationTiming interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - [Navigation Timing 2 IDL tests] expected: FAIL diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_attributes_exist.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_attributes_exist.html.ini index d5aa84ebe6f..3f595348569 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_attributes_exist.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_attributes_exist.html.ini @@ -1,5 +1,6 @@ [nav2_test_attributes_exist.html] type: testharness + expected: TIMEOUT [Performance navigation timing entries are observable.] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_attributes_values.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_attributes_values.html.ini index c70527cfed7..106a2089e22 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_attributes_values.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_attributes_values.html.ini @@ -1,5 +1,6 @@ [nav2_test_attributes_values.html] type: testharness + expected: TIMEOUT [Performance navigation timing instance's value is reasonable.] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_instance_accessors.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_instance_accessors.html.ini index a75dbdb2c0c..045da4d33d9 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_instance_accessors.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_instance_accessors.html.ini @@ -1,5 +1,6 @@ [nav2_test_instance_accessors.html] type: testharness + expected: TIMEOUT [Performance navigation timing entries are accessible through three different accessors.] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_navigation_type_navigate.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_navigation_type_navigate.html.ini index e14d5984612..cbe2682faf7 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_navigation_type_navigate.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_navigation_type_navigate.html.ini @@ -1,5 +1,6 @@ [nav2_test_navigation_type_navigate.html] type: testharness + expected: TIMEOUT [Navigation type to be navigate.] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_redirect_none.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_redirect_none.html.ini index e3727d63e81..617077228e9 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_redirect_none.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_redirect_none.html.ini @@ -1,5 +1,6 @@ [nav2_test_redirect_none.html] type: testharness + expected: TIMEOUT [Naivation without redirects.] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_unique_nav_instances.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_unique_nav_instances.html.ini index 695df4f36e3..c29f84c38a2 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_unique_nav_instances.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_unique_nav_instances.html.ini @@ -1,5 +1,6 @@ [nav2_test_unique_nav_instances.html] type: testharness + expected: TIMEOUT [Each window has a unique nav timing 2 instance.] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/po-navigation.html.ini b/tests/wpt/metadata/navigation-timing/po-navigation.html.ini index 295e2ccdc3f..3fbca338c46 100644 --- a/tests/wpt/metadata/navigation-timing/po-navigation.html.ini +++ b/tests/wpt/metadata/navigation-timing/po-navigation.html.ini @@ -1,4 +1,5 @@ [po-navigation.html] + expected: TIMEOUT [navigation entry is observable] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/navigation-timing/test_timing_attributes_exist.html.ini b/tests/wpt/metadata/navigation-timing/test_timing_attributes_exist.html.ini index e72584e33d0..5f256e10605 100644 --- a/tests/wpt/metadata/navigation-timing/test_timing_attributes_exist.html.ini +++ b/tests/wpt/metadata/navigation-timing/test_timing_attributes_exist.html.ini @@ -36,3 +36,27 @@ [window.performance.timing.unloadEventStart is defined.] expected: FAIL + [window.performance.timing.domComplete is defined.] + expected: FAIL + + [window.performance.timing.domContentLoadedEventEnd is defined.] + expected: FAIL + + [window.performance.timing.domContentLoadedEventStart is defined.] + expected: FAIL + + [window.performance.timing.domInteractive is defined.] + expected: FAIL + + [window.performance.timing.domLoading is defined.] + expected: FAIL + + [window.performance.timing.loadEventEnd is defined.] + expected: FAIL + + [window.performance.timing.loadEventStart is defined.] + expected: FAIL + + [window.performance.timing.navigationStart is defined.] + expected: FAIL + diff --git a/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini b/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini index b86734a2075..66792a7732c 100644 --- a/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini +++ b/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini @@ -9,9 +9,6 @@ [case-sensitivity.any.html] type: testharness - [getEntriesByType values are case sensitive] - expected: FAIL - [getEntriesByName values are case sensitive] expected: FAIL diff --git a/tests/wpt/metadata/resource-timing/clear_resource_timing_functionality.html.ini b/tests/wpt/metadata/resource-timing/clear_resource_timing_functionality.html.ini new file mode 100644 index 00000000000..e3621276db4 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/clear_resource_timing_functionality.html.ini @@ -0,0 +1,8 @@ +[clear_resource_timing_functionality.html] + expected: ERROR + [4 resource timing entries should be stored in this page.] + expected: FAIL + + [No resource timing entries should be stored after clearResourceTimings.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/idlharness.any.js.ini b/tests/wpt/metadata/resource-timing/idlharness.any.js.ini new file mode 100644 index 00000000000..e2a7284a204 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/idlharness.any.js.ini @@ -0,0 +1,243 @@ +[idlharness.any.html] + [PerformanceResourceTiming interface: attribute transferSize] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "transferSize" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute decodedBodySize] + expected: FAIL + + [Stringification of resource] + expected: FAIL + + [Test default toJSON operation of PerformanceNavigationTiming] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "toJSON()" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "decodedBodySize" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute encodedBodySize] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "encodedBodySize" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: operation toJSON()] + expected: FAIL + + [PerformanceResourceTiming must be primary interface of resource] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "workerStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute responseEnd] + expected: FAIL + + [Performance interface: operation setResourceTimingBufferSize(unsigned long)] + expected: FAIL + + [PerformanceResourceTiming interface: attribute secureConnectionStart] + expected: FAIL + + [Performance interface: calling setResourceTimingBufferSize(unsigned long) on performance with too few arguments must throw TypeError] + expected: FAIL + + [PerformanceResourceTiming interface: attribute fetchStart] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "secureConnectionStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "domainLookupStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "connectStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute workerStart] + expected: FAIL + + [PerformanceResourceTiming interface: attribute domainLookupStart] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "redirectStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute connectStart] + expected: FAIL + + [Performance interface: operation clearResourceTimings()] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "responseEnd" with the proper type] + expected: FAIL + + [Performance interface: performance must inherit property "onresourcetimingbufferfull" with the proper type] + expected: FAIL + + [Performance interface: performance must inherit property "setResourceTimingBufferSize(unsigned long)" with the proper type] + expected: FAIL + + [Performance interface: attribute onresourcetimingbufferfull] + expected: FAIL + + [PerformanceResourceTiming interface: attribute connectEnd] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "redirectEnd" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute redirectEnd] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "domainLookupEnd" with the proper type] + expected: FAIL + + [Performance interface: performance must inherit property "clearResourceTimings()" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute domainLookupEnd] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "fetchStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute redirectStart] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "connectEnd" with the proper type] + expected: FAIL + + +[idlharness.any.worker.html] + [PerformanceResourceTiming interface: resource must inherit property "workerStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "initiatorType" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute transferSize] + expected: FAIL + + [Test default toJSON operation of toJSON object] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "secureConnectionStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "requestStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "transferSize" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "domainLookupStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "connectStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute decodedBodySize] + expected: FAIL + + [Stringification of resource] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "redirectStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "toJSON()" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "responseEnd" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "redirectEnd" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "nextHopProtocol" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "domainLookupEnd" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "decodedBodySize" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "fetchStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute encodedBodySize] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "connectEnd" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "encodedBodySize" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: operation toJSON()] + expected: FAIL + + [PerformanceResourceTiming interface: resource must inherit property "responseStart" with the proper type] + expected: FAIL + + [PerformanceResourceTiming must be primary interface of resource] + expected: FAIL + + [PerformanceResourceTiming interface: attribute responseEnd] + expected: FAIL + + [Performance interface: operation setResourceTimingBufferSize(unsigned long)] + expected: FAIL + + [PerformanceResourceTiming interface: attribute secureConnectionStart] + expected: FAIL + + [Performance interface: calling setResourceTimingBufferSize(unsigned long) on performance with too few arguments must throw TypeError] + expected: FAIL + + [PerformanceResourceTiming interface: attribute fetchStart] + expected: FAIL + + [PerformanceResourceTiming interface: attribute workerStart] + expected: FAIL + + [PerformanceResourceTiming interface: attribute domainLookupStart] + expected: FAIL + + [PerformanceResourceTiming interface: attribute connectStart] + expected: FAIL + + [Performance interface: operation clearResourceTimings()] + expected: FAIL + + [Performance interface: performance must inherit property "onresourcetimingbufferfull" with the proper type] + expected: FAIL + + [Performance interface: performance must inherit property "setResourceTimingBufferSize(unsigned long)" with the proper type] + expected: FAIL + + [Performance interface: attribute onresourcetimingbufferfull] + expected: FAIL + + [PerformanceResourceTiming interface: attribute connectEnd] + expected: FAIL + + [PerformanceResourceTiming interface: attribute redirectEnd] + expected: FAIL + + [Performance interface: performance must inherit property "clearResourceTimings()" with the proper type] + expected: FAIL + + [PerformanceResourceTiming interface: attribute domainLookupEnd] + expected: FAIL + + [PerformanceResourceTiming interface: attribute redirectStart] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource-timing-tojson.html.ini b/tests/wpt/metadata/resource-timing/resource-timing-tojson.html.ini new file mode 100644 index 00000000000..402fec6118d --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource-timing-tojson.html.ini @@ -0,0 +1,7 @@ +[resource-timing-tojson.html] + [Untitled] + expected: FAIL + + [Test toJSON() in PerformanceResourceTiming] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource-timing.html.ini b/tests/wpt/metadata/resource-timing/resource-timing.html.ini new file mode 100644 index 00000000000..0ed5d2668f8 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource-timing.html.ini @@ -0,0 +1,55 @@ +[resource-timing.html] + [No timeline entry for about:blank] + expected: FAIL + + [Setting 'document.domain' does not effect same-origin checks] + expected: FAIL + + ['iframe: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.] + expected: FAIL + + ['xmlhttprequest: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.] + expected: FAIL + + ['script: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.] + expected: FAIL + + ['link: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.] + expected: FAIL + + ['iframe (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.] + expected: FAIL + + ['xmlhttprequest (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.] + expected: FAIL + + ['script (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.] + expected: FAIL + + ['link (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.] + expected: FAIL + + ['iframe (Populate cache): The initial request populates the cache (if appropriate).] + expected: FAIL + + ['iframe (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).] + expected: FAIL + + ['xmlhttprequest (Populate cache): The initial request populates the cache (if appropriate).] + expected: FAIL + + ['xmlhttprequest (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).] + expected: FAIL + + ['script (Populate cache): The initial request populates the cache (if appropriate).] + expected: FAIL + + ['script (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).] + expected: FAIL + + ['link (Populate cache): The initial request populates the cache (if appropriate).] + expected: FAIL + + ['link (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_TAO_cross_origin_redirect_chain.html.ini b/tests/wpt/metadata/resource-timing/resource_TAO_cross_origin_redirect_chain.html.ini new file mode 100644 index 00000000000..0b889e1068d --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_TAO_cross_origin_redirect_chain.html.ini @@ -0,0 +1,5 @@ +[resource_TAO_cross_origin_redirect_chain.html] + expected: ERROR + [There should be one entry.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_TAO_null.htm.ini b/tests/wpt/metadata/resource-timing/resource_TAO_null.htm.ini new file mode 100644 index 00000000000..2c100c3d26f --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_TAO_null.htm.ini @@ -0,0 +1,4 @@ +[resource_TAO_null.htm] + [redirectStart, redirectEnd, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, and responseStart -- should be all returned as 0 when the value of Timing-Allow-Origin is null and TAO algorithm fails] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_TAO_origin.htm.ini b/tests/wpt/metadata/resource-timing/resource_TAO_origin.htm.ini new file mode 100644 index 00000000000..5bc7995d47f --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_TAO_origin.htm.ini @@ -0,0 +1,34 @@ +[resource_TAO_origin.htm] + [domainLookupStart should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [domainLookupEnd should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [connectStart should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [connectEnd should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [requestStart should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [responseStart should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [fetchStart should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [responseEnd should not be 0 in timing-allow cross-origin request.] + expected: FAIL + + [redirectStart should be 0 in cross-origin request since no redirect.] + expected: FAIL + + [redirectEnd should be 0 in cross-origin request since no redirect.] + expected: FAIL + + [secureConnectionStart should be 0 in cross-origin request since no ssl!] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_TAO_origin_uppercase.htm.ini b/tests/wpt/metadata/resource-timing/resource_TAO_origin_uppercase.htm.ini new file mode 100644 index 00000000000..7c5b455b088 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_TAO_origin_uppercase.htm.ini @@ -0,0 +1,4 @@ +[resource_TAO_origin_uppercase.htm] + [redirectStart, redirectEnd, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, and responseStart -- should be all returned as 0 when the value of Timing-Allow-Origin is NOT a case-sensitive match for the value of the origin of the current document and TAO algorithm passes] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_TAO_space.htm.ini b/tests/wpt/metadata/resource-timing/resource_TAO_space.htm.ini new file mode 100644 index 00000000000..d18dd65b509 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_TAO_space.htm.ini @@ -0,0 +1,4 @@ +[resource_TAO_space.htm] + [redirectStart, redirectEnd, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, and responseStart -- should be all returned as 0 when the Timing-Allow-Origin header value of the HTTP response is a space separated origin/wildcard list] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_TAO_zero.htm.ini b/tests/wpt/metadata/resource-timing/resource_TAO_zero.htm.ini new file mode 100644 index 00000000000..c64ef4dfc1d --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_TAO_zero.htm.ini @@ -0,0 +1,28 @@ +[resource_TAO_zero.htm] + [fetchStart should be greater than 0 in cross-origin request.] + expected: FAIL + + [responseEnd should be greater than 0 in cross-origin request.] + expected: FAIL + + [secureConnectionStart should be 0 in cross-origin request.] + expected: FAIL + + [connectEnd should be 0 in cross-origin request.] + expected: FAIL + + [domainLookupStart should be 0 in cross-origin request.] + expected: FAIL + + [connectStart should be 0 in cross-origin request.] + expected: FAIL + + [redirectStart should be 0 in cross-origin request.] + expected: FAIL + + [domainLookupEnd should be 0 in cross-origin request.] + expected: FAIL + + [redirectEnd should be 0 in cross-origin request.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_cached.htm.ini b/tests/wpt/metadata/resource-timing/resource_cached.htm.ini new file mode 100644 index 00000000000..aff4156ee6b --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_cached.htm.ini @@ -0,0 +1,4 @@ +[resource_cached.htm] + [There should be two entries] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_connection_reuse.html.ini b/tests/wpt/metadata/resource-timing/resource_connection_reuse.html.ini new file mode 100644 index 00000000000..1fe5956c6a0 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_connection_reuse.html.ini @@ -0,0 +1,4 @@ +[resource_connection_reuse.html] + [There should be 2 PerformanceEntries] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_dedicated_worker.html.ini b/tests/wpt/metadata/resource-timing/resource_dedicated_worker.html.ini new file mode 100644 index 00000000000..a0318fd5935 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_dedicated_worker.html.ini @@ -0,0 +1,4 @@ +[resource_dedicated_worker.html] + [There should be six entries: 4 scripts, 1 stylesheet, and the worker itself] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_dynamic_insertion.html.ini b/tests/wpt/metadata/resource-timing/resource_dynamic_insertion.html.ini new file mode 100644 index 00000000000..c72ce17c457 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_dynamic_insertion.html.ini @@ -0,0 +1,13 @@ +[resource_dynamic_insertion.html] + [http://web-platform.test:8000/resource-timing/resources/resource_timing_test0.css is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/resource_timing_test0.png is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/inject_resource_test.html is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/inject_resource_test.html is expected to have initiatorType iframe] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_ignore_data_url.html.ini b/tests/wpt/metadata/resource-timing/resource_ignore_data_url.html.ini new file mode 100644 index 00000000000..cca981afd37 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_ignore_data_url.html.ini @@ -0,0 +1,4 @@ +[resource_ignore_data_url.html] + [entries.length == 0] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_ignore_failures.html.ini b/tests/wpt/metadata/resource-timing/resource_ignore_failures.html.ini new file mode 100644 index 00000000000..71f5fa720e1 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_ignore_failures.html.ini @@ -0,0 +1,4 @@ +[resource_ignore_failures.html] + [entries.length == 0] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_initiator_types.html.ini b/tests/wpt/metadata/resource-timing/resource_initiator_types.html.ini new file mode 100644 index 00000000000..37136277fe2 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_initiator_types.html.ini @@ -0,0 +1,7 @@ +[resource_initiator_types.html] + [http://web-platform.test:8000/resource-timing/resources/all_resource_types.htm is not expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/green_frame.htm is not expected to be in the Resource Timing buffer] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_memory_cached.sub.html.ini b/tests/wpt/metadata/resource-timing/resource_memory_cached.sub.html.ini new file mode 100644 index 00000000000..7e8a2a7e473 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_memory_cached.sub.html.ini @@ -0,0 +1,20 @@ +[resource_memory_cached.sub.html] + expected: ERROR + [http://web-platform.test:8000/resource-timing/resources/blue.png?id=cached is expected to be in the Resource Timing buffer] + expected: FAIL + + [requestStart should be non-zero on the same-origin request] + expected: FAIL + + [Entry name should start with cross-origin domain] + expected: FAIL + + [Entry name should end with file name] + expected: FAIL + + [responseEnd should not be before startTime] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/inject_resource_test.html is not expected to be in the Resource Timing buffer] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_redirects.html.ini b/tests/wpt/metadata/resource-timing/resource_redirects.html.ini new file mode 100644 index 00000000000..c97b716ce5c --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_redirects.html.ini @@ -0,0 +1,19 @@ +[resource_redirects.html] + [http://web-platform.test:8000/common/redirect.py?location=/resource-timing/resources/resource_timing_test0.css is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/common/redirect.py?location=/resource-timing/resources/blue.png is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/common/redirect.py?location=/resource-timing/resources/blank_page_green.htm is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/common/redirect.py?location=/resource-timing/resources/empty_script.js is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/common/redirect.py?location=/resource-timing/resources/blank_page_green.htm?id=xhr is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/inject_resource_test.html is not expected to be in the Resource Timing buffer] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_reparenting.html.ini b/tests/wpt/metadata/resource-timing/resource_reparenting.html.ini new file mode 100644 index 00000000000..999306ca8ff --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_reparenting.html.ini @@ -0,0 +1,7 @@ +[resource_reparenting.html] + [http://web-platform.test:8000/resource-timing/resources/blue.png?id=move_to_child is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/inject_resource_test.html is not expected to be in the Resource Timing buffer] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_script_types.html.ini b/tests/wpt/metadata/resource-timing/resource_script_types.html.ini new file mode 100644 index 00000000000..984ebdc1d99 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_script_types.html.ini @@ -0,0 +1,31 @@ +[resource_script_types.html] + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=1 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=2 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=3 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=4 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=5 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=6 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=7 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=8 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js?id=9 is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/inject_resource_test.html is not expected to be in the Resource Timing buffer] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_subframe_self_navigation.html.ini b/tests/wpt/metadata/resource-timing/resource_subframe_self_navigation.html.ini new file mode 100644 index 00000000000..e96e303017d --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_subframe_self_navigation.html.ini @@ -0,0 +1,13 @@ +[resource_subframe_self_navigation.html] + [Subsequent <iframe> navigations don't appear in the resource-timing buffer.] + expected: FAIL + + [Subsequent <frame> navigations don't appear in the resource-timing buffer.] + expected: FAIL + + [Subsequent <embed> navigations don't appear in the resource-timing buffer.] + expected: FAIL + + [Subsequent <object> navigations don't appear in the resource-timing buffer.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing.worker.js.ini b/tests/wpt/metadata/resource-timing/resource_timing.worker.js.ini new file mode 100644 index 00000000000..a6c5cb163da --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing.worker.js.ini @@ -0,0 +1,4 @@ +[resource_timing.worker.html] + [Performance Resouce Entries in workers] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_TAO_cross_origin_redirect.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_TAO_cross_origin_redirect.html.ini new file mode 100644 index 00000000000..3ce32f0f969 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_TAO_cross_origin_redirect.html.ini @@ -0,0 +1,4 @@ +[resource_timing_TAO_cross_origin_redirect.html] + [This test validates the values in resource timing for a timing allowed cross-origin redirect.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_eventually.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_eventually.html.ini new file mode 100644 index 00000000000..aa1d99e9604 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_eventually.html.ini @@ -0,0 +1,5 @@ +[resource_timing_buffer_full_eventually.html] + expected: TIMEOUT + [Finite resource timing entries buffer size] + expected: TIMEOUT + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_when_populate_entries.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_when_populate_entries.html.ini new file mode 100644 index 00000000000..4a3bb388608 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_when_populate_entries.html.ini @@ -0,0 +1,10 @@ +[resource_timing_buffer_full_when_populate_entries.html] + [This test validates the functionality of onresourcetimingbufferfull in resource timing.] + expected: FAIL + + [There should only be |bufferSize| resource entries.] + expected: FAIL + + [onresourcetimingbufferfull should have been invoked once buffer is full.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_when_shrink_buffer_size.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_when_shrink_buffer_size.html.ini new file mode 100644 index 00000000000..a1c46103452 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_buffer_full_when_shrink_buffer_size.html.ini @@ -0,0 +1,7 @@ +[resource_timing_buffer_full_when_shrink_buffer_size.html] + [This test validates the functionality of onresourcetimingbufferfull in resource timing.] + expected: FAIL + + [There are 4 scripts, and setResourceTimingBufferSize does not reduce the size.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_cross_origin_redirect.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_cross_origin_redirect.html.ini new file mode 100644 index 00000000000..dbb2eb922f6 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_cross_origin_redirect.html.ini @@ -0,0 +1,4 @@ +[resource_timing_cross_origin_redirect.html] + [This test validates the values in resource timing for a cross-origin redirect.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_cross_origin_redirect_chain.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_cross_origin_redirect_chain.html.ini new file mode 100644 index 00000000000..b318d55200b --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_cross_origin_redirect_chain.html.ini @@ -0,0 +1,5 @@ +[resource_timing_cross_origin_redirect_chain.html] + expected: ERROR + [There should be one entry.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_same_origin_redirect.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_same_origin_redirect.html.ini new file mode 100644 index 00000000000..8e839b27927 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_same_origin_redirect.html.ini @@ -0,0 +1,4 @@ +[resource_timing_same_origin_redirect.html] + [This test validates the values of the redirectStart/End in resource timing for a same-origin resource redirect.] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/resource_timing_store_and_clear_during_callback.html.ini b/tests/wpt/metadata/resource-timing/resource_timing_store_and_clear_during_callback.html.ini new file mode 100644 index 00000000000..8b7929d6ddc --- /dev/null +++ b/tests/wpt/metadata/resource-timing/resource_timing_store_and_clear_during_callback.html.ini @@ -0,0 +1,28 @@ +[resource_timing_store_and_clear_during_callback.html] + [This test validates the behavior of read and clear operation in onresourcetimingbufferfull callback of resource timing.] + expected: FAIL + + [No entry should be stored in resource timing buffer since its cleared once an item arrived.] + expected: FAIL + + [6 resource timing entries should be moved to global buffer.] + expected: FAIL + + [http://web-platform.test:8000/resources/testharness.js is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resources/testharnessreport.js is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/webperftestharness.js is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/webperftestharnessextension.js is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/empty_script.js is expected to be in the Resource Timing buffer] + expected: FAIL + + [http://web-platform.test:8000/resource-timing/resources/resource_timing_test0.js is expected to be in the Resource Timing buffer] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/single-entry-per-resource.html.ini b/tests/wpt/metadata/resource-timing/single-entry-per-resource.html.ini new file mode 100644 index 00000000000..485e42b1d78 --- /dev/null +++ b/tests/wpt/metadata/resource-timing/single-entry-per-resource.html.ini @@ -0,0 +1,7 @@ +[single-entry-per-resource.html] + [One resource when reusing data] + expected: FAIL + + [Only one resource entry per resource] + expected: FAIL + diff --git a/tests/wpt/metadata/resource-timing/test_resource_timing.html.ini b/tests/wpt/metadata/resource-timing/test_resource_timing.html.ini new file mode 100644 index 00000000000..e52d422b64d --- /dev/null +++ b/tests/wpt/metadata/resource-timing/test_resource_timing.html.ini @@ -0,0 +1,77 @@ +[test_resource_timing.html] + expected: TIMEOUT + [window.performance.getEntriesByName() and window.performance.getEntriesByNameType() return same data (iframe)] + expected: FAIL + + [PerformanceEntry has correct name, initiatorType, startTime, and duration (iframe)] + expected: FAIL + + [PerformanceEntry has correct order of timing attributes (iframe)] + expected: FAIL + + [PerformanceEntry has correct network transfer attributes (iframe)] + expected: FAIL + + [PerformanceEntry has correct protocol attribute (iframe)] + expected: FAIL + + [window.performance.getEntriesByName() and window.performance.getEntriesByNameType() return same data (img)] + expected: FAIL + + [PerformanceEntry has correct name, initiatorType, startTime, and duration (img)] + expected: FAIL + + [PerformanceEntry has correct order of timing attributes (img)] + expected: FAIL + + [PerformanceEntry has correct network transfer attributes (img)] + expected: FAIL + + [PerformanceEntry has correct protocol attribute (img)] + expected: FAIL + + [window.performance.getEntriesByName() and window.performance.getEntriesByNameType() return same data (link)] + expected: TIMEOUT + + [PerformanceEntry has correct name, initiatorType, startTime, and duration (link)] + expected: NOTRUN + + [PerformanceEntry has correct order of timing attributes (link)] + expected: NOTRUN + + [PerformanceEntry has correct network transfer attributes (link)] + expected: NOTRUN + + [PerformanceEntry has correct protocol attribute (link)] + expected: NOTRUN + + [window.performance.getEntriesByName() and window.performance.getEntriesByNameType() return same data (script)] + expected: FAIL + + [PerformanceEntry has correct name, initiatorType, startTime, and duration (script)] + expected: FAIL + + [PerformanceEntry has correct order of timing attributes (script)] + expected: FAIL + + [PerformanceEntry has correct network transfer attributes (script)] + expected: FAIL + + [PerformanceEntry has correct protocol attribute (script)] + expected: FAIL + + [window.performance.getEntriesByName() and window.performance.getEntriesByNameType() return same data (xmlhttprequest)] + expected: FAIL + + [PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)] + expected: FAIL + + [PerformanceEntry has correct order of timing attributes (xmlhttprequest)] + expected: FAIL + + [PerformanceEntry has correct network transfer attributes (xmlhttprequest)] + expected: FAIL + + [PerformanceEntry has correct protocol attribute (xmlhttprequest)] + expected: FAIL + diff --git a/tests/wpt/metadata/workers/baseurl/alpha/import-in-moduleworker.html.ini b/tests/wpt/metadata/workers/baseurl/alpha/import-in-moduleworker.html.ini deleted file mode 100644 index bf2a1d61bab..00000000000 --- a/tests/wpt/metadata/workers/baseurl/alpha/import-in-moduleworker.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[import-in-moduleworker.html] - [Base URL in module dedicated workers: import] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/baseurl/alpha/sharedworker-in-worker.html.ini b/tests/wpt/metadata/workers/baseurl/alpha/sharedworker-in-worker.html.ini deleted file mode 100644 index 333edb3a26e..00000000000 --- a/tests/wpt/metadata/workers/baseurl/alpha/sharedworker-in-worker.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[sharedworker-in-worker.html] - [Base URL in workers: new SharedWorker()] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini b/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini deleted file mode 100644 index 9d72f08e38e..00000000000 --- a/tests/wpt/metadata/workers/semantics/multiple-workers/005.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[005.html] - type: testharness - [dedicated worker in shared worker in dedicated worker] - expected: FAIL - diff --git a/tests/wpt/metadata/workers/worker-performance.worker.js.ini b/tests/wpt/metadata/workers/worker-performance.worker.js.ini index 49f33b3ec9a..6048b1478b2 100644 --- a/tests/wpt/metadata/workers/worker-performance.worker.js.ini +++ b/tests/wpt/metadata/workers/worker-performance.worker.js.ini @@ -1,3 +1,26 @@ [worker-performance.worker.html] type: testharness - expected: CRASH + [Can use performance.getEntriesByType in workers] + expected: FAIL + + [Performance marks and measures seem to be working correctly in workers] + expected: FAIL + + [Can use clearMarks and clearMeasures in workers] + expected: FAIL + + [Resource timing seems to work in workers] + expected: FAIL + + [performance.clearResourceTimings in workers] + expected: FAIL + + [performance.setResourceTimingBufferSize in workers] + expected: FAIL + + [performance.timing is not available in workers] + expected: FAIL + + [performance.toJSON is available in workers] + expected: FAIL + diff --git a/tests/wpt/metadata/xhr/sync-no-timeout.any.js.ini b/tests/wpt/metadata/xhr/sync-no-timeout.any.js.ini index 842f7f5f93f..ecff1270f56 100644 --- a/tests/wpt/metadata/xhr/sync-no-timeout.any.js.ini +++ b/tests/wpt/metadata/xhr/sync-no-timeout.any.js.ini @@ -5,7 +5,3 @@ [sync-no-timeout] expected: FAIL - -[sync-no-timeout.any.worker.html] - -[sync-no-timeout.any.html] diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index d29df9db9e8..6cdd649c457 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -27064,7 +27064,7 @@ "testharness" ], "mozilla/interfaces.html": [ - "b1de57409ad5e6f9fedeb8a34c9474b4e378ec0a", + "ad17e930ddb5bc2daecb86216efe8885ae399173", "testharness" ], "mozilla/interfaces.js": [ @@ -27072,7 +27072,7 @@ "support" ], "mozilla/interfaces.worker.js": [ - "fbd6e92c097dea4f99924de219c9f6aa07a45282", + "a5f2e00f234ea66b80e8a9bd4dbbc5433926191f", "testharness" ], "mozilla/invalid-this.html": [ @@ -32904,11 +32904,11 @@ "testharness" ], "mozilla/window_performance.html": [ - "6b96c18b3cdef8b8bce294f1b45ce09192b00cd0", + "690870b7080e179481ca0255f7c30337e8b6636a", "testharness" ], "mozilla/window_performance_topLevelDomComplete.html": [ - "ce2431a7279e7cefa9e8032edabe276ac5deb227", + "50bbc2917b5ac900b5061a0b2c30b6c1fef1067e", "testharness" ], "mozilla/window_requestAnimationFrame.html": [ diff --git a/tests/wpt/mozilla/meta/mozilla/window_performance.html.ini b/tests/wpt/mozilla/meta/mozilla/window_performance.html.ini new file mode 100644 index 00000000000..9719867b4dc --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/window_performance.html.ini @@ -0,0 +1,4 @@ +[window_performance.html] + [window_performance] + expected: FAIL + diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index b1de57409ad..ad17e930ddb 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -181,10 +181,11 @@ test_interfaces([ "PerformanceEntry", "PerformanceMark", "PerformanceMeasure", + "PerformanceNavigationTiming", "PerformanceObserver", "PerformanceObserverEntryList", "PerformancePaintTiming", - "PerformanceTiming", + "PerformanceResourceTiming", "Plugin", "PluginArray", "PopStateEvent", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index fbd6e92c097..a5f2e00f234 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -39,6 +39,7 @@ test_interfaces([ "PerformanceObserver", "PerformanceObserverEntryList", "PerformancePaintTiming", + "PerformanceResourceTiming", "ProgressEvent", "PromiseRejectionEvent", "Request", diff --git a/tests/wpt/mozilla/tests/mozilla/window_performance.html b/tests/wpt/mozilla/tests/mozilla/window_performance.html index 6b96c18b3cd..690870b7080 100644 --- a/tests/wpt/mozilla/tests/mozilla/window_performance.html +++ b/tests/wpt/mozilla/tests/mozilla/window_performance.html @@ -11,9 +11,13 @@ test(function() { assert_true(window.performance instanceof Performance, "Should be Performance"); assert_not_equals(window.performance.timing, undefined); - assert_true(window.performance.timing instanceof PerformanceTiming, "Should be PerformanceTiming"); - assert_greater_than(window.performance.timing.navigationStart, 0); + var entries = window.performance.getEntries(); + assert_not_equals(entries.length, 0); + assert_true(entries[0] instanceof PerformanceNavigationTiming); + + // TODO(#21254) navigationTiming/startTime is not fully implemented yet, so this assertion will fail + assert_greater_than(entries[0].startTime, 0); var last = window.performance.now(); assert_greater_than(last, 0); diff --git a/tests/wpt/mozilla/tests/mozilla/window_performance_topLevelDomComplete.html b/tests/wpt/mozilla/tests/mozilla/window_performance_topLevelDomComplete.html index ce2431a7279..50bbc2917b5 100644 --- a/tests/wpt/mozilla/tests/mozilla/window_performance_topLevelDomComplete.html +++ b/tests/wpt/mozilla/tests/mozilla/window_performance_topLevelDomComplete.html @@ -1,18 +1,23 @@ -<html> -<head> - <title>Performance toLevelDomComplete</title> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> -</head> +<!doctype html> +<meta charset="utf-8"> +<title>Performance topLevelDomComplete</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> <body> <script> -async_test(function(t) { - window.onload = t.step_func(function() { - assert_true(performance.timing.domLoading <= performance.timing.topLevelDomComplete); - assert_true(performance.timing.topLevelDomComplete <= performance.timing.domComplete); - t.done(); - }); -}, "performance.topLevelDomComplete"); +async_test(function (t) { + window.onload = t.step_func(function (entryList, obs) { + var entries = window.performance.getEntries(); + assert_greater_than(entries.length, 0); + var navigation = entries[0]; + assert_true(navigation instanceof PerformanceNavigationTiming); + + assert_true(navigation.domContentLoadedEventStart <= navigation.topLevelDomComplete); + assert_true(navigation.topLevelDomComplete <= navigation.domComplete); + + t.done(); + }); +}, "top level dom complete"); </script> </body> -</html> + |