diff --git a/components/net/cookie_storage.rs b/components/net/cookie_storage.rs index e37e8ac1619..788564ad0d3 100644 --- a/components/net/cookie_storage.rs +++ b/components/net/cookie_storage.rs @@ -205,6 +205,27 @@ impl CookieStorage { } } + /// + pub fn query_cookies(&mut self, url: &ServoUrl, name: Option) -> Vec> { + // 1. Retrieve cookie-list given request-uri and "non-HTTP" source + let cookie_list = self.cookies_data_for_url(url, CookieSource::NonHTTP); + + // 3. For each cookie in cookie-list, run these steps: + // 3.2. If name is given, then run these steps: + if let Some(name) = name { + // Let cookieName be the result of running UTF-8 decode without BOM on cookie’s name. + // If cookieName does not equal name, then continue. + cookie_list.filter(|cookie| cookie.name() == name).collect() + } else { + cookie_list.collect() + } + + // Note: we do not convert the list into CookieListItem's here, we do that in script to not not have to define + // the binding types in net. + + // Return list + } + pub fn cookies_data_for_url<'a>( &'a mut self, url: &'a ServoUrl, diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index e8afa6e2c13..5ff2a1fb5ae 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -14,6 +14,7 @@ use std::sync::{Arc, Mutex, RwLock, Weak}; use std::thread; use std::time::Duration; +use base::id::CookieStoreId; use cookie::Cookie; use crossbeam_channel::Sender; use devtools_traits::DevtoolsControlMsg; @@ -29,9 +30,10 @@ use net_traits::request::{Destination, RequestBuilder, RequestId}; use net_traits::response::{Response, ResponseInit}; use net_traits::storage_thread::StorageThreadMsg; use net_traits::{ - AsyncRuntime, CookieSource, CoreResourceMsg, CoreResourceThread, CustomResponseMediator, - DiscardFetch, FetchChannels, FetchTaskTarget, ResourceFetchTiming, ResourceThreads, - ResourceTimingType, WebSocketDomAction, WebSocketNetworkEvent, + AsyncRuntime, CookieAsyncResponse, CookieData, CookieSource, CoreResourceMsg, + CoreResourceThread, CustomResponseMediator, DiscardFetch, FetchChannels, FetchTaskTarget, + ResourceFetchTiming, ResourceThreads, ResourceTimingType, WebSocketDomAction, + WebSocketNetworkEvent, }; use profile_traits::mem::{ ProcessReports, ProfilerChan as MemProfilerChan, Report, ReportKind, ReportsChan, @@ -152,6 +154,7 @@ pub fn new_core_resource_thread( ca_certificates, ignore_certificate_errors, cancellation_listeners: Default::default(), + cookie_listeners: Default::default(), }; mem_profiler_chan.run_with_memory_reporting( @@ -179,6 +182,7 @@ struct ResourceChannelManager { ca_certificates: CACertificates, ignore_certificate_errors: bool, cancellation_listeners: HashMap>, + cookie_listeners: HashMap>, } fn create_http_states( @@ -335,6 +339,20 @@ impl ResourceChannelManager { cancellation_listener } + fn send_cookie_response(&self, store_id: CookieStoreId, data: CookieData) { + let Some(sender) = self.cookie_listeners.get(&store_id) else { + warn!( + "Async cookie request made for store id that is non-existent {:?}", + store_id + ); + return; + }; + let res = sender.send(CookieAsyncResponse { data }); + if res.is_err() { + warn!("Unable to send cookie response to script thread"); + } + } + /// Returns false if the thread should exit. fn process_msg( &mut self, @@ -398,6 +416,14 @@ impl ResourceChannelManager { .delete_cookie_with_name(&request, name); return true; }, + CoreResourceMsg::DeleteCookieAsync(cookie_store_id, url, name) => { + http_state + .cookie_jar + .write() + .unwrap() + .delete_cookie_with_name(&url, name); + self.send_cookie_response(cookie_store_id, CookieData::Delete(Ok(()))); + }, CoreResourceMsg::FetchRedirect(request_builder, res_init, sender) => { let cancellation_listener = self.get_or_create_cancellation_listener(request_builder.id); @@ -423,6 +449,15 @@ impl ResourceChannelManager { ); } }, + CoreResourceMsg::SetCookieForUrlAsync(cookie_store_id, url, cookie, source) => { + self.resource_manager.set_cookie_for_url( + &url, + cookie.into_inner().to_owned(), + source, + http_state, + ); + self.send_cookie_response(cookie_store_id, CookieData::Set(Ok(()))); + }, CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => { let mut cookie_jar = http_state.cookie_jar.write().unwrap(); cookie_jar.remove_expired_cookies_for_url(&url); @@ -430,6 +465,33 @@ impl ResourceChannelManager { .send(cookie_jar.cookies_for_url(&url, source)) .unwrap(); }, + CoreResourceMsg::GetCookieDataForUrlAsync(cookie_store_id, url, name) => { + let mut cookie_jar = http_state.cookie_jar.write().unwrap(); + cookie_jar.remove_expired_cookies_for_url(&url); + let cookie = cookie_jar + .query_cookies(&url, name) + .into_iter() + .map(Serde) + .next(); + self.send_cookie_response(cookie_store_id, CookieData::Get(cookie)); + }, + CoreResourceMsg::GetAllCookieDataForUrlAsync(cookie_store_id, url, name) => { + let mut cookie_jar = http_state.cookie_jar.write().unwrap(); + cookie_jar.remove_expired_cookies_for_url(&url); + let cookies = cookie_jar + .query_cookies(&url, name) + .into_iter() + .map(Serde) + .collect(); + self.send_cookie_response(cookie_store_id, CookieData::GetAll(cookies)); + }, + CoreResourceMsg::NewCookieListener(cookie_store_id, sender, _url) => { + // TODO: Use the URL for setting up the actual monitoring + self.cookie_listeners.insert(cookie_store_id, sender); + }, + CoreResourceMsg::RemoveCookieListener(cookie_store_id) => { + self.cookie_listeners.remove(&cookie_store_id); + }, CoreResourceMsg::NetworkMediator(mediator_chan, origin) => { self.resource_manager .sw_managers diff --git a/components/script/dom/cookiestore.rs b/components/script/dom/cookiestore.rs new file mode 100644 index 00000000000..9253b663fe2 --- /dev/null +++ b/components/script/dom/cookiestore.rs @@ -0,0 +1,615 @@ +/* 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/. */ + +use std::borrow::Cow; +use std::collections::VecDeque; +use std::rc::Rc; + +use base::id::CookieStoreId; +use cookie::Expiration::DateTime; +use cookie::{Cookie, SameSite}; +use dom_struct::dom_struct; +use hyper_serde::Serde; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; +use itertools::Itertools; +use js::jsval::NullValue; +use net_traits::CookieSource::NonHTTP; +use net_traits::{CookieAsyncResponse, CookieData, CoreResourceMsg, IpcSend}; +use script_bindings::script_runtime::CanGc; +use servo_url::ServoUrl; + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::CookieStoreBinding::{ + CookieInit, CookieListItem, CookieSameSite, CookieStoreDeleteOptions, CookieStoreGetOptions, + CookieStoreMethods, +}; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::USVString; +use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; +use crate::dom::promise::Promise; +use crate::dom::window::Window; +use crate::task_source::SendableTaskSource; + +/// +/// CookieStore provides an async API for pages and service workers to access and modify cookies. +/// This requires setting up communication with resource thread's cookie storage that allows for +/// the page to have multiple cookie storage promises in flight at the same time. +#[dom_struct] +pub(crate) struct CookieStore { + eventtarget: EventTarget, + #[ignore_malloc_size_of = "Rc"] + in_flight: DomRefCell>>, + // Store an id so that we can send it with requests and the resource thread knows who to respond to + #[no_trace] + store_id: CookieStoreId, +} + +struct CookieListener { + // TODO:(whatwg/cookiestore#239) The spec is missing details for what task source to use + task_source: SendableTaskSource, + context: Trusted, +} + +impl CookieListener { + pub(crate) fn handle(&self, message: CookieAsyncResponse) { + let context = self.context.clone(); + self.task_source.queue(task!(cookie_message: move || { + let Some(promise) = context.root().in_flight.borrow_mut().pop_front() else { + warn!("No promise exists for cookie store response"); + return; + }; + match message.data { + CookieData::Get(cookie) => { + // If list is failure, then reject p with a TypeError and abort these steps. + // (There is currently no way for list to result in failure) + if let Some(cookie) = cookie { + // Otherwise, resolve p with the first item of list. + promise.resolve_native(&cookie_to_list_item(cookie.into_inner()), CanGc::note()); + } else { + // If list is empty, then resolve p with null. + promise.resolve_native(&NullValue(), CanGc::note()); + } + }, + CookieData::GetAll(cookies) => { + // If list is failure, then reject p with a TypeError and abort these steps. + promise.resolve_native( + &cookies + .into_iter() + .map(|cookie| cookie_to_list_item(cookie.0)) + .collect_vec(), + CanGc::note()); + }, + CookieData::Delete(_) | CookieData::Change(_) | CookieData::Set(_) => { + promise.resolve_native(&(), CanGc::note()); + } + } + })); + } +} + +impl CookieStore { + fn new_inherited() -> CookieStore { + CookieStore { + eventtarget: EventTarget::new_inherited(), + in_flight: Default::default(), + store_id: CookieStoreId::new(), + } + } + + pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot { + let store = reflect_dom_object(Box::new(CookieStore::new_inherited()), global, can_gc); + store.setup_route(); + store + } + + fn setup_route(&self) { + let (cookie_sender, cookie_receiver) = ipc::channel().expect("ipc channel failure"); + + let context = Trusted::new(self); + let cs_listener = CookieListener { + task_source: self + .global() + .task_manager() + .dom_manipulation_task_source() + .to_sendable(), + context, + }; + + ROUTER.add_typed_route( + cookie_receiver, + Box::new(move |message| match message { + Ok(msg) => cs_listener.handle(msg), + Err(err) => warn!("Error receiving a CookieStore message: {:?}", err), + }), + ); + + let res = self + .global() + .resource_threads() + .send(CoreResourceMsg::NewCookieListener( + self.store_id, + cookie_sender, + self.global().creation_url().clone(), + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } + } +} + +/// +fn cookie_to_list_item(cookie: Cookie) -> CookieListItem { + // TODO: Investigate if we need to explicitly UTF-8 decode without BOM here or if thats + // already being done by cookie-rs or implicitly by using rust strings + CookieListItem { + // Let domain be the result of running UTF-8 decode without BOM on cookie’s domain. + domain: cookie + .domain() + .map(|domain| Some(domain.to_string().into())), + + // Let expires be cookie’s expiry-time (as a timestamp). + expires: match cookie.expires() { + None | Some(cookie::Expiration::Session) => None, + Some(DateTime(time)) => Some(Some(Finite::wrap((time.unix_timestamp() * 1000) as f64))), + }, + + // Let name be the result of running UTF-8 decode without BOM on cookie’s name. + name: Some(cookie.name().to_string().into()), + + // Let partitioned be a boolean indicating that the user agent supports cookie partitioning and that i + // that cookie has a partition key. + partitioned: Some(false), // Do we support partitioning? Spec says true only if UA supports it + + // Let path be the result of running UTF-8 decode without BOM on cookie’s path. + path: cookie.path().map(|path| path.to_string().into()), + + sameSite: match cookie.same_site() { + Some(SameSite::None) => Some(CookieSameSite::None), + Some(SameSite::Lax) => Some(CookieSameSite::Lax), + Some(SameSite::Strict) => Some(CookieSameSite::Strict), + None => None, // The spec doesnt handle this case, which implies the default of Lax? + }, + + // Let secure be cookie’s secure-only-flag. + secure: cookie.secure(), + + // Let value be the result of running UTF-8 decode without BOM on cookie’s value. + value: Some(cookie.value().to_string().into()), + } +} + +impl CookieStoreMethods for CookieStore { + /// + fn Get(&self, name: USVString, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 5. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 4. Let url be settings’s creation URL. + let creation_url = global.creation_url(); + + // 6. Run the following steps in parallel: + let res = self + .global() + .resource_threads() + .send(CoreResourceMsg::GetCookieDataForUrlAsync( + self.store_id, + creation_url.clone(), + Some(name.to_string()), + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 7. Return p. + p + } + + /// + fn Get_(&self, options: &CookieStoreGetOptions, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 7. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 4. Let url be settings’s creation URL. + let creation_url = global.creation_url(); + + // 5. If options is empty, then return a promise rejected with a TypeError. + // "is empty" is not strictly defined anywhere in the spec but the only value we require here is "url" + if options.url.is_none() && options.name.is_none() { + p.reject_error(Error::Type("Options cannot be empty".to_string()), can_gc); + return p; + } + + let mut final_url = creation_url.clone(); + + // 6. If options["url"] is present, then run these steps: + if let Some(get_url) = &options.url { + // 6.1. Let parsed be the result of parsing options["url"] with settings’s API base URL. + let parsed_url = ServoUrl::parse_with_base(Some(&global.api_base_url()), get_url); + + // 6.2. If this’s relevant global object is a Window object and parsed does not equal url, + // then return a promise rejected with a TypeError. + if let Some(_window) = DomRoot::downcast::(self.global()) { + if parsed_url + .as_ref() + .is_ok_and(|parsed| parsed.as_url() != creation_url.as_url()) + { + p.reject_error( + Error::Type("URL does not match context".to_string()), + can_gc, + ); + return p; + } + } + + // 6.3. If parsed’s origin and url’s origin are not the same origin, + // then return a promise rejected with a TypeError. + if parsed_url + .as_ref() + .is_ok_and(|parsed| creation_url.origin() != parsed.origin()) + { + p.reject_error(Error::Type("Not same origin".to_string()), can_gc); + return p; + } + + // 6.4. Set url to parsed. + if let Ok(url) = parsed_url { + final_url = url; + } + } + + // 6. Run the following steps in parallel: + let res = self + .global() + .resource_threads() + .send(CoreResourceMsg::GetCookieDataForUrlAsync( + self.store_id, + final_url.clone(), + options.name.clone().map(|val| val.0), + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + p + } + + /// + fn GetAll(&self, name: USVString, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 5. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + // 4. Let url be settings’s creation URL. + let creation_url = global.creation_url(); + + // 6. Run the following steps in parallel: + let res = + self.global() + .resource_threads() + .send(CoreResourceMsg::GetAllCookieDataForUrlAsync( + self.store_id, + creation_url.clone(), + Some(name.to_string()), + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 7. Return p. + p + } + + /// + fn GetAll_(&self, options: &CookieStoreGetOptions, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 7. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 4. Let url be settings’s creation URL. + let creation_url = global.creation_url(); + + // 5. If options is empty, then return a promise rejected with a TypeError. + // "is empty" is not strictly defined anywhere in the spec but the only value we require here is "url" + if options.url.is_none() && options.name.is_none() { + p.reject_error(Error::Type("Options cannot be empty".to_string()), can_gc); + return p; + } + + let mut final_url = creation_url.clone(); + + // 6. If options["url"] is present, then run these steps: + if let Some(get_url) = &options.url { + // 6.1. Let parsed be the result of parsing options["url"] with settings’s API base URL. + let parsed_url = ServoUrl::parse_with_base(Some(&global.api_base_url()), get_url); + + // 6.2. If this’s relevant global object is a Window object and parsed does not equal url, + // then return a promise rejected with a TypeError. + if let Some(_window) = DomRoot::downcast::(self.global()) { + if parsed_url + .as_ref() + .is_ok_and(|parsed| parsed.as_url() != creation_url.as_url()) + { + p.reject_error( + Error::Type("URL does not match context".to_string()), + can_gc, + ); + return p; + } + } + + // 6.3. If parsed’s origin and url’s origin are not the same origin, + // then return a promise rejected with a TypeError. + if parsed_url + .as_ref() + .is_ok_and(|parsed| creation_url.origin() != parsed.origin()) + { + p.reject_error(Error::Type("Not same origin".to_string()), can_gc); + return p; + } + + // 6.4. Set url to parsed. + if let Ok(url) = parsed_url { + final_url = url; + } + } + + // 6. Run the following steps in parallel: + let res = + self.global() + .resource_threads() + .send(CoreResourceMsg::GetAllCookieDataForUrlAsync( + self.store_id, + final_url.clone(), + options.name.clone().map(|val| val.0), + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 8. Return p + p + } + + /// + fn Set(&self, name: USVString, value: USVString, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 9. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 4. Let url be settings’s creation URL. + // 5. Let domain be null. + // 6. Let path be "/". + // 7. Let sameSite be strict. + // 8. Let partitioned be false. + let cookie = Cookie::build((Cow::Owned(name.to_string()), Cow::Owned(value.to_string()))) + .path("/") + .secure(true) + .same_site(SameSite::Strict) + .partitioned(false); + // TODO: This currently doesn't implement all the "set a cookie" steps which involves + // additional processing of the name and value + + // 10. Run the following steps in parallel: + let res = self + .global() + .resource_threads() + .send(CoreResourceMsg::SetCookieForUrlAsync( + self.store_id, + self.global().creation_url().clone(), + Serde(cookie.build()), + NonHTTP, + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 11. Return p. + p + } + + /// + fn Set_(&self, options: &CookieInit, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 5. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 4. Let url be settings’s creation URL. + let creation_url = global.creation_url(); + + // 6.1. Let r be the result of running set a cookie with url, options["name"], options["value"], + // options["expires"], options["domain"], options["path"], options["sameSite"], and options["partitioned"]. + let cookie = Cookie::build(( + Cow::Owned(options.name.to_string()), + Cow::Owned(options.value.to_string()), + )); + // TODO: This currently doesn't implement all the "set a cookie" steps which involves + // additional processing of the name and value + + // 6. Run the following steps in parallel: + let res = self + .global() + .resource_threads() + .send(CoreResourceMsg::SetCookieForUrlAsync( + self.store_id, + creation_url.clone(), + Serde(cookie.build()), + NonHTTP, + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 7. Return p + p + } + + /// + fn Delete(&self, name: USVString, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 5. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 6. Run the following steps in parallel: + // TODO: the spec passes additional parameters to _delete a cookie_ that we don't handle yet + let res = global + .resource_threads() + .send(CoreResourceMsg::DeleteCookieAsync( + self.store_id, + global.creation_url().clone(), + name.0, + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 7. Return p. + p + } + + /// + fn Delete_(&self, options: &CookieStoreDeleteOptions, can_gc: CanGc) -> Rc { + // 1. Let settings be this’s relevant settings object. + let global = self.global(); + + // 2. Let origin be settings’s origin. + let origin = global.origin(); + + // 5. Let p be a new promise. + let p = Promise::new(&global, can_gc); + + // 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException. + if !origin.is_tuple() { + p.reject_error(Error::Security, can_gc); + return p; + } + + // 6. Run the following steps in parallel: + // TODO: the spec passes additional parameters to _delete a cookie_ that we don't handle yet + let res = global + .resource_threads() + .send(CoreResourceMsg::DeleteCookieAsync( + self.store_id, + global.creation_url().clone(), + options.name.to_string(), + )); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } else { + self.in_flight.borrow_mut().push_back(p.clone()); + } + + // 7. Return p. + p + } +} + +impl Drop for CookieStore { + fn drop(&mut self) { + let res = self + .global() + .resource_threads() + .send(CoreResourceMsg::RemoveCookieListener(self.store_id)); + if res.is_err() { + error!("Failed to send cookiestore message to resource threads"); + } + } +} diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index f4e56d93cb9..7a6c78c1a7c 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -115,7 +115,7 @@ use crate::dom::reportingobserver::ReportingObserver; use crate::dom::serviceworker::ServiceWorker; use crate::dom::serviceworkerregistration::ServiceWorkerRegistration; use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory; -use crate::dom::types::{DebuggerGlobalScope, MessageEvent}; +use crate::dom::types::{CookieStore, DebuggerGlobalScope, MessageEvent}; use crate::dom::underlyingsourcecontainer::UnderlyingSourceType; #[cfg(feature = "webgpu")] use crate::dom::webgpu::gpudevice::GPUDevice; @@ -209,6 +209,9 @@ pub(crate) struct GlobalScope { HashMapTracedValues>, >, + /// + cookie_store: MutNullableDom, + /// worker_map: DomRefCell>>, @@ -747,6 +750,7 @@ impl GlobalScope { eventtarget: EventTarget::new_inherited(), crypto: Default::default(), registration_map: DomRefCell::new(HashMapTracedValues::new()), + cookie_store: Default::default(), worker_map: DomRefCell::new(HashMapTracedValues::new()), pipeline_id, devtools_wants_updates: Default::default(), @@ -2361,6 +2365,10 @@ impl GlobalScope { self.crypto.or_init(|| Crypto::new(self, can_gc)) } + pub(crate) fn cookie_store(&self, can_gc: CanGc) -> DomRoot { + self.cookie_store.or_init(|| CookieStore::new(self, can_gc)) + } + pub(crate) fn live_devtools_updates(&self) -> bool { self.devtools_wants_updates.get() } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 09da7571d63..6693b983ea7 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -259,6 +259,7 @@ pub(crate) mod comment; pub(crate) mod compositionevent; pub(crate) mod console; pub(crate) mod constantsourcenode; +pub(crate) mod cookiestore; pub(crate) mod countqueuingstrategy; mod create; pub(crate) mod crypto; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index adb401ff241..229a2df9d87 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -127,6 +127,7 @@ use crate::dom::bindings::utils::GlobalStaticData; use crate::dom::bindings::weakref::DOMTracker; #[cfg(feature = "bluetooth")] use crate::dom::bluetooth::BluetoothExtraPermissionData; +use crate::dom::cookiestore::CookieStore; use crate::dom::crypto::Crypto; use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner}; use crate::dom::customelementregistry::CustomElementRegistry; @@ -1066,6 +1067,11 @@ impl WindowMethods for Window { .or_init(|| Storage::new(self, StorageType::Local, CanGc::note())) } + // https://cookiestore.spec.whatwg.org/#Window + fn CookieStore(&self, can_gc: CanGc) -> DomRoot { + self.global().cookie_store(can_gc) + } + // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-GlobalCrypto fn Crypto(&self) -> DomRoot { self.as_global_scope().crypto(CanGc::note()) diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index f33d0e8a629..291e4b945a3 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -100,6 +100,10 @@ DOMInterfaces = { 'canGc': ['Types'] }, +'CookieStore': { + 'canGc': ['Set', 'Set_', 'Get', 'Get_', 'GetAll', 'GetAll_', 'Delete', 'Delete_'] +}, + 'CountQueuingStrategy': { 'canGc': ['GetSize'], }, @@ -655,7 +659,7 @@ DOMInterfaces = { }, 'Window': { - 'canGc': ['Stop', 'Fetch', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap', 'CreateImageBitmap_', 'SetInterval', 'SetTimeout', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'], + 'canGc': ['CreateImageBitmap', 'CreateImageBitmap_', 'CookieStore', 'Fetch', 'Open', 'SetInterval', 'SetTimeout', 'Stop', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'], 'inRealms': ['Fetch', 'GetOpener', 'WebdriverCallback', 'WebdriverException'], 'additionalTraits': ['crate::interfaces::WindowHelpers'], }, diff --git a/components/script_bindings/webidls/CookieStore.webidl b/components/script_bindings/webidls/CookieStore.webidl new file mode 100644 index 00000000000..7e81cdbc8cd --- /dev/null +++ b/components/script_bindings/webidls/CookieStore.webidl @@ -0,0 +1,71 @@ +/* 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/. */ + +// https://cookiestore.spec.whatwg.org/ + +[Exposed=(ServiceWorker,Window), + SecureContext, + Pref="dom_cookiestore_enabled"] +interface CookieStore : EventTarget { + Promise get(USVString name); + Promise get(optional CookieStoreGetOptions options = {}); + + Promise getAll(USVString name); + Promise getAll(optional CookieStoreGetOptions options = {}); + + Promise set(USVString name, USVString value); + Promise set(CookieInit options); + + Promise delete(USVString name); + Promise delete(CookieStoreDeleteOptions options); + + // [Exposed=Window] + // attribute EventHandler onchange; +}; + +dictionary CookieStoreGetOptions { + USVString name; + USVString url; +}; + +enum CookieSameSite { + "strict", + "lax", + "none" +}; + +dictionary CookieInit { + required USVString name; + required USVString value; + DOMHighResTimeStamp? expires = null; + USVString? domain = null; + USVString path = "/"; + CookieSameSite sameSite = "strict"; + boolean partitioned = false; +}; + +dictionary CookieStoreDeleteOptions { + required USVString name; + USVString? domain = null; + USVString path = "/"; + boolean partitioned = false; +}; + +dictionary CookieListItem { + USVString name; + USVString value; + USVString? domain; + USVString path; + DOMHighResTimeStamp? expires; + boolean secure; + CookieSameSite sameSite; + boolean partitioned; +}; + +typedef sequence CookieList; + +[SecureContext] +partial interface Window { + [SameObject, Pref="dom_cookiestore_enabled"] readonly attribute CookieStore cookieStore; +}; diff --git a/components/shared/base/id.rs b/components/shared/base/id.rs index 6a27d891553..ebd084f596c 100644 --- a/components/shared/base/id.rs +++ b/components/shared/base/id.rs @@ -376,6 +376,8 @@ namespace_id! {ImageBitmapId, ImageBitmapIndex, "ImageBitmap"} namespace_id! {OffscreenCanvasId, OffscreenCanvasIndex, "OffscreenCanvas"} +namespace_id! {CookieStoreId, CookieStoreIndex, "CookieStore"} + // We provide ids just for unit testing. pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234); pub const TEST_PIPELINE_INDEX: Index = diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 987b2d69b85..6aa51b7037b 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -10,7 +10,7 @@ use std::sync::{LazyLock, OnceLock}; use std::thread::{self, JoinHandle}; use base::cross_process_instant::CrossProcessInstant; -use base::id::HistoryStateId; +use base::id::{CookieStoreId, HistoryStateId}; use content_security_policy::{self as csp}; use cookie::Cookie; use crossbeam_channel::{Receiver, Sender, unbounded}; @@ -526,6 +526,12 @@ pub enum CoreResourceMsg { SetCookieForUrl(ServoUrl, Serde>, CookieSource), /// Store a set of cookies for a given originating URL SetCookiesForUrl(ServoUrl, Vec>>, CookieSource), + SetCookieForUrlAsync( + CookieStoreId, + ServoUrl, + Serde>, + CookieSource, + ), /// Retrieve the stored cookies for a given URL GetCookiesForUrl(ServoUrl, IpcSender>, CookieSource), /// Get a cookie by name for a given originating URL @@ -534,8 +540,13 @@ pub enum CoreResourceMsg { IpcSender>>>, CookieSource, ), + GetCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option), + GetAllCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option), DeleteCookies(ServoUrl), DeleteCookie(ServoUrl, String), + DeleteCookieAsync(CookieStoreId, ServoUrl, String), + NewCookieListener(CookieStoreId, IpcSender, ServoUrl), + RemoveCookieListener(CookieStoreId), /// Get a history state by a given history state id GetHistoryState(HistoryStateId, IpcSender>>), /// Set a history state for a given history state id @@ -976,6 +987,26 @@ pub enum CookieSource { NonHTTP, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CookieChange { + changed: Vec>>, + deleted: Vec>>, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum CookieData { + Change(CookieChange), + Get(Option>>), + GetAll(Vec>>), + Set(Result<(), ()>), + Delete(Result<(), ()>), +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CookieAsyncResponse { + pub data: CookieData, +} + /// Network errors that have to be exported out of the loaders #[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] pub enum NetworkError { diff --git a/python/tidy/tidy.py b/python/tidy/tidy.py index 4bad494eb10..9ef72e4f14b 100644 --- a/python/tidy/tidy.py +++ b/python/tidy/tidy.py @@ -138,6 +138,7 @@ WEBIDL_STANDARDS = [ b"//gpuweb.github.io", b"//notifications.spec.whatwg.org", b"//testutils.spec.whatwg.org/", + b"//cookiestore.spec.whatwg.org/", # Not a URL b"// This interface is entirely internal to Servo, and should not be" + b" accessible to\n// web pages.", ] diff --git a/tests/wpt/meta/cookiestore/cookieStore_delete.sub.https.html.ini b/tests/wpt/meta/cookiestore/cookieStore_delete.sub.https.html.ini deleted file mode 100644 index de9470a514e..00000000000 --- a/tests/wpt/meta/cookiestore/cookieStore_delete.sub.https.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[cookieStore_delete.sub.https.html] - expected: TIMEOUT - [Async Cookies: cookieStore basic API across origins] - expected: TIMEOUT diff --git a/tests/wpt/meta/cookiestore/cookieStore_delete_arguments.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_delete_arguments.https.any.js.ini index 23cc27a2782..c16d10dc18b 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_delete_arguments.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_delete_arguments.https.any.js.ini @@ -1,57 +1,37 @@ [cookieStore_delete_arguments.https.any.html] - [cookieStore.delete with positional name] - expected: FAIL - - [cookieStore.delete with name in options] - expected: FAIL - + expected: TIMEOUT [cookieStore.delete domain starts with "."] expected: FAIL [cookieStore.delete with domain that is not equal current host] expected: FAIL - [cookieStore.delete with domain set to the current hostname] - expected: FAIL - [cookieStore.delete with domain set to a subdomain of the current hostname] expected: FAIL [cookieStore.delete with domain set to a non-domain-matching suffix of the current hostname] expected: FAIL - [cookieStore.delete with path set to the current directory] - expected: FAIL - [cookieStore.delete with path set to subdirectory of the current directory] expected: FAIL - [cookieStore.delete does not append / at the end of path] - expected: FAIL - - [cookieStore.delete can delete a cookie set by document.cookie if document is defined] - expected: FAIL - [cookieStore.delete with path that does not start with /] expected: FAIL - [cookieStore.delete with get result] - expected: FAIL - [cookieStore.delete with positional empty name] - expected: FAIL + expected: TIMEOUT [cookieStore.delete with empty name in options] - expected: FAIL + expected: NOTRUN [cookieStore.delete with maximum cookie name size] - expected: FAIL + expected: NOTRUN [cookieStore.delete with a __Host- prefix should not have a domain] - expected: FAIL + expected: NOTRUN [cookieStore.delete with whitespace] - expected: FAIL + expected: NOTRUN [cookieStore_delete_arguments.https.any.serviceworker.html] diff --git a/tests/wpt/meta/cookiestore/cookieStore_delete_basic.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_delete_basic.https.any.js.ini index 0884b3aba3f..5af477aecfc 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_delete_basic.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_delete_basic.https.any.js.ini @@ -2,5 +2,3 @@ expected: ERROR [cookieStore_delete_basic.https.any.html] - [cookieStore.delete return type is Promise] - expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_event_basic.https.window.js.ini b/tests/wpt/meta/cookiestore/cookieStore_event_basic.https.window.js.ini index d4ee7e88a51..6dc07ee3595 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_event_basic.https.window.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_event_basic.https.window.js.ini @@ -1,4 +1,4 @@ [cookieStore_event_basic.https.window.html] - expected: ERROR + expected: TIMEOUT [cookieStore fires change event for cookie set by cookieStore.set()] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/meta/cookiestore/cookieStore_event_delete.https.window.js.ini b/tests/wpt/meta/cookiestore/cookieStore_event_delete.https.window.js.ini index f4f2d8ad572..ba341f5af40 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_event_delete.https.window.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_event_delete.https.window.js.ini @@ -1,7 +1,7 @@ [cookieStore_event_delete.https.window.html] - expected: ERROR + expected: TIMEOUT [cookieStore fires change event for cookie deleted by cookieStore.delete()] - expected: FAIL + expected: TIMEOUT [cookieStore does not fire change events for non-existing expired cookies] expected: NOTRUN diff --git a/tests/wpt/meta/cookiestore/cookieStore_event_overwrite.https.window.js.ini b/tests/wpt/meta/cookiestore/cookieStore_event_overwrite.https.window.js.ini index e10f22e14a1..cda526b686b 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_event_overwrite.https.window.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_event_overwrite.https.window.js.ini @@ -1,4 +1,4 @@ [cookieStore_event_overwrite.https.window.html] - expected: ERROR + expected: TIMEOUT [cookieStore fires change event for cookie overwritten by cookieStore.set()] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/meta/cookiestore/cookieStore_getAll_arguments.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_getAll_arguments.https.any.js.ini index 32f6679b263..d941cedd62f 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_getAll_arguments.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_getAll_arguments.https.any.js.ini @@ -8,27 +8,6 @@ [cookieStore.getAll with empty options] expected: FAIL - [cookieStore.getAll with positional name] - expected: FAIL - - [cookieStore.getAll with name in options] - expected: FAIL - - [cookieStore.getAll with name in both positional arguments and options] - expected: FAIL - - [cookieStore.getAll with absolute url in options] - expected: FAIL - - [cookieStore.getAll with relative url in options] - expected: FAIL - - [cookieStore.getAll with invalid url path in options] - expected: FAIL - - [cookieStore.getAll with invalid url host in options] - expected: FAIL - [cookieStore.getAll with absolute url with fragment in options] expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_getAll_set_basic.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_getAll_set_basic.https.any.js.ini index b868ce9aea2..73ab4b4e6c2 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_getAll_set_basic.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_getAll_set_basic.https.any.js.ini @@ -1,7 +1,4 @@ [cookieStore_getAll_set_basic.https.any.html] - [cookieStore.getAll returns the cookie written by cookieStore.set] - expected: FAIL - [cookieStore_getAll_set_basic.https.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_arguments.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_get_arguments.https.any.js.ini index 77566d9493c..cea0b853517 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_get_arguments.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_get_arguments.https.any.js.ini @@ -2,33 +2,6 @@ expected: ERROR [cookieStore_get_arguments.https.any.html] - [cookieStore.get with no arguments returns TypeError] - expected: FAIL - - [cookieStore.get with empty options returns TypeError] - expected: FAIL - - [cookieStore.get with positional name] - expected: FAIL - - [cookieStore.get with name in options] - expected: FAIL - - [cookieStore.get with name in both positional arguments and options] - expected: FAIL - - [cookieStore.get with absolute url in options] - expected: FAIL - - [cookieStore.get with relative url in options] - expected: FAIL - - [cookieStore.get with invalid url path in options] - expected: FAIL - - [cookieStore.get with invalid url host in options] - expected: FAIL - [cookieStore.get with absolute url with fragment in options] expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_delete_basic.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_get_delete_basic.https.any.js.ini index b00399e9ec4..98eda125bd1 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_get_delete_basic.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_get_delete_basic.https.any.js.ini @@ -1,7 +1,4 @@ [cookieStore_get_delete_basic.https.any.html] - [cookieStore.get returns null for a cookie deleted by cookieStore.delete] - expected: FAIL - [cookieStore_get_delete_basic.https.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_set_across_origins.sub.https.html.ini b/tests/wpt/meta/cookiestore/cookieStore_get_set_across_origins.sub.https.html.ini index e8364450a45..1195f44f60a 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_get_set_across_origins.sub.https.html.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_get_set_across_origins.sub.https.html.ini @@ -1,13 +1,9 @@ [cookieStore_get_set_across_origins.sub.https.html] - expected: TIMEOUT [cookieStore.get() sees cookieStore.set() in cross-origin frame] - expected: TIMEOUT + expected: FAIL [cookieStore.get() in cross-origin frame sees cookieStore.set()] - expected: NOTRUN - - [cookieStore.set() in cross-origin does not overwrite the __Host- cookie] - expected: NOTRUN + expected: FAIL [__Host- cookies set via cookieStore.set() in same-site domains don't overwrite each other] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_set_basic.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_get_set_basic.https.any.js.ini index eb0ab15bfca..cdfb526a409 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_get_set_basic.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_get_set_basic.https.any.js.ini @@ -1,7 +1,4 @@ [cookieStore_get_set_basic.https.any.html] - [cookieStore.get returns the cookie written by cookieStore.set] - expected: FAIL - [cookieStore_get_set_basic.https.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_set_creation_url.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_get_set_creation_url.https.any.js.ini deleted file mode 100644 index 3fb61c77350..00000000000 --- a/tests/wpt/meta/cookiestore/cookieStore_get_set_creation_url.https.any.js.ini +++ /dev/null @@ -1,3 +0,0 @@ -[cookieStore_get_set_creation_url.https.any.html] - [cookieStore.set and cookieStore.get use the creation url] - expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_set_creation_url.sub.https.html.ini b/tests/wpt/meta/cookiestore/cookieStore_get_set_creation_url.sub.https.html.ini deleted file mode 100644 index 2e3677530ff..00000000000 --- a/tests/wpt/meta/cookiestore/cookieStore_get_set_creation_url.sub.https.html.ini +++ /dev/null @@ -1,7 +0,0 @@ -[cookieStore_get_set_creation_url.sub.https.html] - expected: TIMEOUT - [cookieStore.get() option url ignores fragments] - expected: TIMEOUT - - [cookieStore.get() option url + pushState()] - expected: NOTRUN diff --git a/tests/wpt/meta/cookiestore/cookieStore_get_set_ordering.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_get_set_ordering.https.any.js.ini index 7ce82c389a5..39e54e072fe 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_get_set_ordering.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_get_set_ordering.https.any.js.ini @@ -1,11 +1,4 @@ [cookieStore_get_set_ordering.https.any.html] - expected: ERROR - [Set three simple origin session cookies sequentially and ensure they all end up in the cookie jar in order.] - expected: FAIL - - [Set three simple origin session cookies in undefined order using Promise.all and ensure they all end up in the cookie jar in any order. ] - expected: NOTRUN - [cookieStore_get_set_ordering.https.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/cookiestore/cookieStore_in_detached_frame.https.html.ini b/tests/wpt/meta/cookiestore/cookieStore_in_detached_frame.https.html.ini deleted file mode 100644 index cd983631753..00000000000 --- a/tests/wpt/meta/cookiestore/cookieStore_in_detached_frame.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[cookieStore_in_detached_frame.https.html] - [cookieStore on DOMWindow of detached iframe] - expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_set_arguments.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_set_arguments.https.any.js.ini index a986975db31..e8328eed298 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_set_arguments.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_set_arguments.https.any.js.ini @@ -2,164 +2,159 @@ expected: ERROR [cookieStore_set_arguments.https.any.html] - [cookieStore.set with positional name and value] - expected: FAIL - - [cookieStore.set with name and value in options] - expected: FAIL - + expected: TIMEOUT [cookieStore.set fails with empty name and empty value] - expected: FAIL + expected: TIMEOUT [cookieStore.set with empty name and an '=' in value] - expected: FAIL + expected: NOTRUN [cookieStore.set with normal name and an '=' in value] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0000] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0001] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0002] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0003] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0004] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0005] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0006] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0007] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0008] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0010] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0011] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0012] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0013] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0014] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0015] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0016] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0017] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0018] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+0019] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+001A] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+001B] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+001C] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+001D] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+001E] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+001F] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+003B] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if name or value contain invalid character U+007F] - expected: FAIL + expected: NOTRUN [cookieStore.set with expires set to a future Date] - expected: FAIL + expected: NOTRUN [cookieStore.set with expires set to a past Date] - expected: FAIL + expected: NOTRUN [cookieStore.set with expires set to a future timestamp] - expected: FAIL + expected: NOTRUN [cookieStore.set with expires set to a past timestamp] - expected: FAIL + expected: NOTRUN [cookieStore.set domain starts with "."] - expected: FAIL + expected: NOTRUN [cookieStore.set with domain that is not equal current host] - expected: FAIL + expected: NOTRUN [cookieStore.set with domain set to the current hostname] - expected: FAIL + expected: NOTRUN [cookieStore.set with domain set to a subdomain of the current hostname] - expected: FAIL + expected: NOTRUN [cookieStore.set with domain set to a non-domain-matching suffix of the current hostname] - expected: FAIL + expected: NOTRUN [cookieStore.set default domain is null and differs from current hostname] - expected: FAIL + expected: NOTRUN [cookieStore.set with path set to the current directory] - expected: FAIL + expected: NOTRUN [cookieStore.set with path set to a subdirectory of the current directory] - expected: FAIL + expected: NOTRUN [cookieStore.set default path is /] - expected: FAIL + expected: NOTRUN [cookieStore.set does not add / to path that does not end with /] - expected: FAIL + expected: NOTRUN [cookieStore.set can modify a cookie set by document.cookie if document is defined] - expected: FAIL + expected: NOTRUN [cookieStore.set with path that does not start with /] - expected: FAIL + expected: NOTRUN [cookieStore.set with get result] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if the path is too long] - expected: FAIL + expected: NOTRUN [cookieStore.set checks if the domain is too long] - expected: FAIL + expected: NOTRUN [cookieStore.set with a __Host- prefix should not have a domain] - expected: FAIL + expected: NOTRUN [cookieStore.set with whitespace only name and value] - expected: FAIL + expected: NOTRUN [cookieStore.set with whitespace at begining or end] - expected: FAIL + expected: NOTRUN diff --git a/tests/wpt/meta/cookiestore/cookieStore_set_domain_parsing.sub.https.html.ini b/tests/wpt/meta/cookiestore/cookieStore_set_domain_parsing.sub.https.html.ini deleted file mode 100644 index 94340c1ef12..00000000000 --- a/tests/wpt/meta/cookiestore/cookieStore_set_domain_parsing.sub.https.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[cookieStore_set_domain_parsing.sub.https.html] - [cookieStore.set with domain on a IDNA host] - expected: FAIL - - [cookieStore.set with domain set to the current hostname but differently cased] - expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_set_domain_parsing.tentative.sub.https.html.ini b/tests/wpt/meta/cookiestore/cookieStore_set_domain_parsing.tentative.sub.https.html.ini deleted file mode 100644 index 04d3ca580ba..00000000000 --- a/tests/wpt/meta/cookiestore/cookieStore_set_domain_parsing.tentative.sub.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[cookieStore_set_domain_parsing.tentative.sub.https.html] - [cookieStore.set with domain on an IP address host] - expected: FAIL diff --git a/tests/wpt/meta/cookiestore/cookieStore_set_limit.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_set_limit.https.any.js.ini index c749bbdb94c..821c20b9463 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_set_limit.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_set_limit.https.any.js.ini @@ -2,14 +2,12 @@ expected: ERROR [cookieStore_set_limit.https.any.html] - [Set max-size cookie with largest possible name and value (4096 bytes)] - expected: FAIL - - [Set max-size value-less cookie] - expected: FAIL - - [Set max-size cookie with largest possible value (4095 bytes)] - expected: FAIL - + expected: TIMEOUT [Set max-size name-less cookie] - expected: FAIL + expected: TIMEOUT + + [Ignore name-less cookie with value larger than 4096 bytes] + expected: NOTRUN + + [Ignore name-less cookie (without leading =) with value larger than 4096 bytes] + expected: NOTRUN diff --git a/tests/wpt/meta/cookiestore/cookieStore_special_names.https.any.js.ini b/tests/wpt/meta/cookiestore/cookieStore_special_names.https.any.js.ini index dad226902c8..4c1652ffc34 100644 --- a/tests/wpt/meta/cookiestore/cookieStore_special_names.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/cookieStore_special_names.https.any.js.ini @@ -1,40 +1,5 @@ [cookieStore_special_names.https.any.html] - [cookieStore.set with __Secure- name on secure origin] - expected: FAIL - - [cookieStore.set of expired __Secure- cookie name on secure origin] - expected: FAIL - - [cookieStore.delete with __Secure- name on secure origin] - expected: FAIL - - [cookieStore.set with __secure- name on secure origin] - expected: FAIL - - [cookieStore.set of expired __secure- cookie name on secure origin] - expected: FAIL - - [cookieStore.delete with __secure- name on secure origin] - expected: FAIL - - [cookieStore.set with __Host- name on secure origin] - expected: FAIL - - [cookieStore.set of expired __Host- cookie name on secure origin] - expected: FAIL - - [cookieStore.delete with __Host- name on secure origin] - expected: FAIL - - [cookieStore.set with __host- name on secure origin] - expected: FAIL - - [cookieStore.set of expired __host- cookie name on secure origin] - expected: FAIL - - [cookieStore.delete with __host- name on secure origin] - expected: FAIL - + expected: TIMEOUT [cookieStore.set with __Host- prefix and a domain option] expected: FAIL @@ -75,34 +40,34 @@ expected: FAIL [cookieStore.set a nameless cookie cannot have __Host- prefix] - expected: FAIL + expected: TIMEOUT [cookieStore.set a nameless cookie cannot have __Secure- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have __Http- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have __HostHttp- prefix] expected: FAIL [cookieStore.set a nameless cookie cannot have __Host- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have \t__Host- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have __Secure- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have \t__Secure- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have __Http- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have \t__Http- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have __HostHttp- prefix] expected: FAIL @@ -123,13 +88,13 @@ expected: FAIL [cookieStore.set a nameless cookie cannot have __Host-Http- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have __Host-Http- prefix] - expected: FAIL + expected: NOTRUN [cookieStore.set a nameless cookie cannot have \t__Host-Http- prefix] - expected: FAIL + expected: NOTRUN [cookieStore_special_names.https.any.serviceworker.html] diff --git a/tests/wpt/meta/cookiestore/encoding.https.any.js.ini b/tests/wpt/meta/cookiestore/encoding.https.any.js.ini index 81aeea562c0..181b947ef5d 100644 --- a/tests/wpt/meta/cookiestore/encoding.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/encoding.https.any.js.ini @@ -1,10 +1,4 @@ [encoding.https.any.html] - [BOM not stripped from name] - expected: FAIL - - [BOM not stripped from value] - expected: FAIL - [encoding.https.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/cookiestore/httponly_cookies.https.window.js.ini b/tests/wpt/meta/cookiestore/httponly_cookies.https.window.js.ini index b7fe8f2fc06..e518f59269c 100644 --- a/tests/wpt/meta/cookiestore/httponly_cookies.https.window.js.ini +++ b/tests/wpt/meta/cookiestore/httponly_cookies.https.window.js.ini @@ -1,5 +1,4 @@ [httponly_cookies.https.window.html] - expected: ERROR [HttpOnly cookies are not observed] expected: FAIL diff --git a/tests/wpt/meta/cookiestore/idlharness.https.any.js.ini b/tests/wpt/meta/cookiestore/idlharness.https.any.js.ini index 46d9169c923..ff51aa03fce 100644 --- a/tests/wpt/meta/cookiestore/idlharness.https.any.js.ini +++ b/tests/wpt/meta/cookiestore/idlharness.https.any.js.ini @@ -5,48 +5,6 @@ [idl_test setup] expected: FAIL - [CookieStore interface: existence and properties of interface object] - expected: FAIL - - [CookieStore interface object length] - expected: FAIL - - [CookieStore interface object name] - expected: FAIL - - [CookieStore interface: existence and properties of interface prototype object] - expected: FAIL - - [CookieStore interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [CookieStore interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [CookieStore interface: operation get(USVString)] - expected: FAIL - - [CookieStore interface: operation get(optional CookieStoreGetOptions)] - expected: FAIL - - [CookieStore interface: operation getAll(USVString)] - expected: FAIL - - [CookieStore interface: operation getAll(optional CookieStoreGetOptions)] - expected: FAIL - - [CookieStore interface: operation set(USVString, USVString)] - expected: FAIL - - [CookieStore interface: operation set(CookieInit)] - expected: FAIL - - [CookieStore interface: operation delete(USVString)] - expected: FAIL - - [CookieStore interface: operation delete(CookieStoreDeleteOptions)] - expected: FAIL - [CookieStore interface: attribute onchange] expected: FAIL @@ -104,9 +62,6 @@ [ServiceWorkerRegistration interface: attribute cookies] expected: FAIL - [Window interface: attribute cookieStore] - expected: FAIL - [idlharness.https.any.serviceworker.html] expected: ERROR