diff options
author | ddh <dianehosfelt@gmail.com> | 2018-03-12 22:24:41 +0000 |
---|---|---|
committer | ddh <dianehosfelt@gmail.com> | 2018-11-20 16:21:32 +0000 |
commit | 26007fddd3f8aabfe026f06de64207d31edf5318 (patch) | |
tree | f1a7ef3496871d67907734dc10c2cc9df31f27cf | |
parent | 3fe83f1d06a50969b2fa731a050b35abdc5520d7 (diff) | |
download | servo-26007fddd3f8aabfe026f06de64207d31edf5318.tar.gz servo-26007fddd3f8aabfe026f06de64207d31edf5318.zip |
refactored performance timing to align with updated spec
refactoring with ResourceFetchMetadata
implemented deprecated window.timing functionality
created ResourceTimingListener trait
fixed w3c links in navigation timing
updated include.ini to run resource timing tests on ci
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> + |