diff options
210 files changed, 6549 insertions, 1291 deletions
diff --git a/Cargo.lock b/Cargo.lock index 24bcfab54ed..098934e8a07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5878,9 +5878,9 @@ dependencies = [ [[package]] name = "surfman-chains" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51598e1772bda3dbb1b81a9dc4b46725896948ea58d0f128e5b492a92f297af" +checksum = "d7370be9fb29386426ce52d9c3ac0501e7f71ea8218baf4bee061465f2fcec04" dependencies = [ "euclid", "fnv", @@ -6910,7 +6910,7 @@ dependencies = [ [[package]] name = "webxr" version = "0.0.1" -source = "git+https://github.com/servo/webxr#da44de55ca8c082a033787946cb59dbbd2903a15" +source = "git+https://github.com/servo/webxr#7763f4826fb04a1b48dbf502e48ca9d6ff3b6378" dependencies = [ "android_injected_glue", "bindgen", @@ -6933,7 +6933,7 @@ dependencies = [ [[package]] name = "webxr-api" version = "0.0.1" -source = "git+https://github.com/servo/webxr#da44de55ca8c082a033787946cb59dbbd2903a15" +source = "git+https://github.com/servo/webxr#7763f4826fb04a1b48dbf502e48ca9d6ff3b6378" dependencies = [ "euclid", "ipc-channel", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 2281ea30242..7e3267148e6 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -40,7 +40,7 @@ style = { path = "../style" } style_traits = { path = "../style_traits" } # NOTE: the sm-angle feature only enables angle on windows, not other platforms! surfman = { version = "0.3", features = ["sm-angle","sm-angle-default"] } -surfman-chains = "0.4" +surfman-chains = "0.5" surfman-chains-api = "0.2" time = { version = "0.1.0", optional = true } webrender = { git = "https://github.com/servo/webrender" } diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index 3c27c523866..168a7e9f070 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -827,22 +827,30 @@ impl WebGLThread { debug!("Swapping {:?}", context_id); swap_chain - .swap_buffers(&mut self.device, &mut data.ctx) + .swap_buffers( + &mut self.device, + &mut data.ctx, + if data.attributes.preserve_drawing_buffer { + surfman_chains::PreserveBuffer::Yes(&*data.gl) + } else { + surfman_chains::PreserveBuffer::No + }, + ) .unwrap(); debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR); - // TODO: if preserveDrawingBuffer is true, then blit the front buffer to the back buffer - // https://github.com/servo/servo/issues/24604 - debug!("Clearing {:?}", context_id); - let alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32]; - swap_chain - .clear_surface(&mut self.device, &mut data.ctx, &*data.gl, clear_color) - .unwrap(); - debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR); + if !data.attributes.preserve_drawing_buffer { + debug!("Clearing {:?}", context_id); + let alpha = data + .state + .requested_flags + .contains(ContextAttributeFlags::ALPHA); + let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32]; + swap_chain + .clear_surface(&mut self.device, &mut data.ctx, &*data.gl, clear_color) + .unwrap(); + debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR); + } // Rebind framebuffers as appropriate. debug!("Rebinding {:?}", context_id); diff --git a/components/devtools/actor.rs b/components/devtools/actor.rs index 158092ee1a8..641c6f717d4 100644 --- a/components/devtools/actor.rs +++ b/components/devtools/actor.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /// General actor system infrastructure. +use crate::StreamId; use devtools_traits::PreciseTime; use serde_json::{Map, Value}; use std::any::Any; @@ -21,18 +22,20 @@ pub enum ActorMessageStatus { /// A common trait for all devtools actors that encompasses an immutable name /// and the ability to process messages that are directed to particular actors. /// TODO: ensure the name is immutable -pub trait Actor: Any + ActorAsAny { +pub(crate) trait Actor: Any + ActorAsAny { fn handle_message( &self, registry: &ActorRegistry, msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + id: StreamId, ) -> Result<ActorMessageStatus, ()>; fn name(&self) -> String; + fn cleanup(&self, _id: StreamId) {} } -pub trait ActorAsAny { +pub(crate) trait ActorAsAny { fn actor_as_any(&self) -> &dyn Any; fn actor_as_any_mut(&mut self) -> &mut dyn Any; } @@ -71,6 +74,12 @@ impl ActorRegistry { } } + pub(crate) fn cleanup(&self, id: StreamId) { + for actor in self.actors.values() { + actor.cleanup(id); + } + } + /// Creating shareable registry pub fn create_shareable(self) -> Arc<Mutex<ActorRegistry>> { if let Some(shareable) = self.shareable { @@ -131,11 +140,11 @@ impl ActorRegistry { } /// Add an actor to the registry of known actors that can receive messages. - pub fn register(&mut self, actor: Box<dyn Actor + Send>) { + pub(crate) fn register(&mut self, actor: Box<dyn Actor + Send>) { self.actors.insert(actor.name(), actor); } - pub fn register_later(&self, actor: Box<dyn Actor + Send>) { + pub(crate) fn register_later(&self, actor: Box<dyn Actor + Send>) { let mut actors = self.new_actors.borrow_mut(); actors.push(actor); } @@ -154,10 +163,11 @@ impl ActorRegistry { /// Attempt to process a message as directed by its `to` property. If the actor is not /// found or does not indicate that it knew how to process the message, ignore the failure. - pub fn handle_message( + pub(crate) fn handle_message( &mut self, msg: &Map<String, Value>, stream: &mut TcpStream, + id: StreamId, ) -> Result<(), ()> { let to = match msg.get("to") { Some(to) => to.as_str().unwrap(), @@ -171,7 +181,7 @@ impl ActorRegistry { None => debug!("message received for unknown actor \"{}\"", to), Some(actor) => { let msg_type = msg.get("type").unwrap().as_str().unwrap(); - if actor.handle_message(self, msg_type, msg, stream)? != + if actor.handle_message(self, msg_type, msg, stream, id)? != ActorMessageStatus::Processed { debug!( diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 708c9f69de9..beee86e50b5 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -17,6 +17,7 @@ use crate::actors::tab::TabDescriptorActor; use crate::actors::thread::ThreadActor; use crate::actors::timeline::TimelineActor; use crate::protocol::JsonPacketStream; +use crate::StreamId; use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications}; use devtools_traits::DevtoolsPageInfo; use devtools_traits::NavigationState; @@ -24,6 +25,7 @@ use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use serde_json::{Map, Value}; use std::cell::{Cell, RefCell}; +use std::collections::HashMap; use std::net::TcpStream; #[derive(Serialize)] @@ -118,7 +120,7 @@ pub struct BrowsingContextActorMsg { manifestActor: String,*/ } -pub struct BrowsingContextActor { +pub(crate) struct BrowsingContextActor { pub name: String, pub title: RefCell<String>, pub url: RefCell<String>, @@ -131,7 +133,7 @@ pub struct BrowsingContextActor { pub styleSheets: String, pub thread: String, pub tab: String, - pub streams: RefCell<Vec<TcpStream>>, + pub streams: RefCell<HashMap<StreamId, TcpStream>>, pub browsing_context_id: BrowsingContextId, pub active_pipeline: Cell<PipelineId>, pub script_chan: IpcSender<DevtoolScriptControlMsg>, @@ -148,6 +150,7 @@ impl Actor for BrowsingContextActor { msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "reconfigure" => { @@ -160,7 +163,7 @@ impl Actor for BrowsingContextActor { } } } - stream.write_json_packet(&ReconfigureReply { from: self.name() }); + let _ = stream.write_json_packet(&ReconfigureReply { from: self.name() }); ActorMessageStatus::Processed }, @@ -181,26 +184,26 @@ impl Actor for BrowsingContextActor { watchpoints: false, }, }; - self.streams.borrow_mut().push(stream.try_clone().unwrap()); - stream.write_json_packet(&msg); + + if stream.write_json_packet(&msg).is_err() { + return Ok(ActorMessageStatus::Processed); + } + self.streams + .borrow_mut() + .insert(id, stream.try_clone().unwrap()); self.script_chan .send(WantsLiveNotifications(self.active_pipeline.get(), true)) .unwrap(); ActorMessageStatus::Processed }, - //FIXME: The current implementation won't work for multiple connections. Need to ensure - // that the correct stream is removed. "detach" => { let msg = BrowsingContextDetachedReply { from: self.name(), type_: "detached".to_owned(), }; - self.streams.borrow_mut().pop(); - stream.write_json_packet(&msg); - self.script_chan - .send(WantsLiveNotifications(self.active_pipeline.get(), false)) - .unwrap(); + let _ = stream.write_json_packet(&msg); + self.cleanup(id); ActorMessageStatus::Processed }, @@ -215,7 +218,7 @@ impl Actor for BrowsingContextActor { title: self.title.borrow().clone(), }], }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -224,13 +227,22 @@ impl Actor for BrowsingContextActor { from: self.name(), workers: vec![], }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, _ => ActorMessageStatus::Ignored, }) } + + fn cleanup(&self, id: StreamId) { + self.streams.borrow_mut().remove(&id); + if self.streams.borrow().is_empty() { + self.script_chan + .send(WantsLiveNotifications(self.active_pipeline.get(), false)) + .unwrap(); + } + } } impl BrowsingContextActor { @@ -284,7 +296,7 @@ impl BrowsingContextActor { styleSheets: styleSheets.name(), tab: tabdesc.name(), thread: thread.name(), - streams: RefCell::new(Vec::new()), + streams: RefCell::new(HashMap::new()), browsing_context_id: id, active_pipeline: Cell::new(pipeline), }; @@ -347,8 +359,8 @@ impl BrowsingContextActor { state: state.to_owned(), isFrameSwitching: false, }; - for stream in &mut *self.streams.borrow_mut() { - stream.write_json_packet(&msg); + for stream in self.streams.borrow_mut().values_mut() { + let _ = stream.write_json_packet(&msg); } } diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 4576a296775..86e4f39dd33 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -12,7 +12,7 @@ use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::object::ObjectActor; use crate::actors::worker::WorkerActor; use crate::protocol::JsonPacketStream; -use crate::UniqueId; +use crate::{StreamId, UniqueId}; use devtools_traits::CachedConsoleMessage; use devtools_traits::ConsoleMessage; use devtools_traits::EvaluateJSReply::{ActorValue, BooleanValue, StringValue}; @@ -23,7 +23,7 @@ use devtools_traits::{ use ipc_channel::ipc::{self, IpcSender}; use msg::constellation_msg::TEST_PIPELINE_ID; use serde_json::{self, Map, Number, Value}; -use std::cell::{RefCell, RefMut}; +use std::cell::RefCell; use std::collections::HashMap; use std::net::TcpStream; use time::precise_time_ns; @@ -130,15 +130,20 @@ impl ConsoleActor { } } - fn streams_mut<'a>(&self, registry: &'a ActorRegistry) -> RefMut<'a, Vec<TcpStream>> { + fn streams_mut<'a>(&self, registry: &'a ActorRegistry, cb: impl Fn(&mut TcpStream)) { match &self.root { Root::BrowsingContext(bc) => registry .find::<BrowsingContextActor>(bc) .streams - .borrow_mut(), - Root::DedicatedWorker(worker) => { - registry.find::<WorkerActor>(worker).streams.borrow_mut() - }, + .borrow_mut() + .values_mut() + .for_each(cb), + Root::DedicatedWorker(worker) => registry + .find::<WorkerActor>(worker) + .streams + .borrow_mut() + .values_mut() + .for_each(cb), } } @@ -255,9 +260,9 @@ impl ConsoleActor { type_: "pageError".to_owned(), pageError: page_error, }; - for stream in &mut *self.streams_mut(registry) { - stream.write_json_packet(&msg); - } + self.streams_mut(registry, |stream| { + let _ = stream.write_json_packet(&msg); + }); } } @@ -303,9 +308,9 @@ impl ConsoleActor { columnNumber: console_message.columnNumber, }, }; - for stream in &mut *self.streams_mut(registry) { - stream.write_json_packet(&msg); - } + self.streams_mut(registry, |stream| { + let _ = stream.write_json_packet(&msg); + }); } } } @@ -321,6 +326,7 @@ impl Actor for ConsoleActor { msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "clearMessagesCache" => { @@ -380,7 +386,7 @@ impl Actor for ConsoleActor { from: self.name(), messages: messages, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -396,7 +402,7 @@ impl Actor for ConsoleActor { .collect(), traits: StartedListenersTraits, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -413,7 +419,7 @@ impl Actor for ConsoleActor { .map(|listener| listener.as_str().unwrap().to_owned()) .collect(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -425,13 +431,13 @@ impl Actor for ConsoleActor { matches: vec![], matchProp: "".to_owned(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "evaluateJS" => { let msg = self.evaluateJS(®istry, &msg); - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -443,7 +449,9 @@ impl Actor for ConsoleActor { }; // Emit an eager reply so that the client starts listening // for an async event with the resultID - stream.write_json_packet(&early_reply); + if stream.write_json_packet(&early_reply).is_err() { + return Ok(ActorMessageStatus::Processed); + } if msg.get("eager").and_then(|v| v.as_bool()).unwrap_or(false) { // We don't support the side-effect free evaluation that eager evalaution @@ -464,7 +472,7 @@ impl Actor for ConsoleActor { helperResult: reply.helperResult, }; // Send the data from evaluateJS along with a resultID - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -473,7 +481,7 @@ impl Actor for ConsoleActor { from: self.name(), updated: vec![], }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/device.rs b/components/devtools/actors/device.rs index 11785d8c0b8..c54b72b6d5d 100644 --- a/components/devtools/actors/device.rs +++ b/components/devtools/actors/device.rs @@ -5,6 +5,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; use crate::protocol::{ActorDescription, Method}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -34,6 +35,7 @@ impl Actor for DeviceActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "getDescription" => { @@ -44,7 +46,7 @@ impl Actor for DeviceActor { platformVersion: "71.0".to_string(), }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/emulation.rs b/components/devtools/actors/emulation.rs index 94b769a106f..58f13b16d34 100644 --- a/components/devtools/actors/emulation.rs +++ b/components/devtools/actors/emulation.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -21,6 +22,7 @@ impl Actor for EmulationActor { msg_type: &str, _msg: &Map<String, Value>, _stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { _ => ActorMessageStatus::Ignored, diff --git a/components/devtools/actors/framerate.rs b/components/devtools/actors/framerate.rs index 243c7ea4c4c..6203be2c57e 100644 --- a/components/devtools/actors/framerate.rs +++ b/components/devtools/actors/framerate.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::timeline::HighResolutionStamp; +use crate::StreamId; use devtools_traits::DevtoolScriptControlMsg; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; @@ -32,6 +33,7 @@ impl Actor for FramerateActor { _msg_type: &str, _msg: &Map<String, Value>, _stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(ActorMessageStatus::Ignored) } diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index d2f0929ac89..d01267b68e7 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -8,6 +8,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::browsing_context::BrowsingContextActor; use crate::protocol::JsonPacketStream; +use crate::StreamId; use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, GetRootNode}; use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute}; use devtools_traits::{ComputedNodeLayout, DevtoolScriptControlMsg, NodeInfo}; @@ -68,17 +69,18 @@ impl Actor for HighlighterActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "showBoxModel" => { let msg = ShowBoxModelReply { from: self.name() }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "hideBoxModel" => { let msg = HideBoxModelReply { from: self.name() }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -103,6 +105,7 @@ impl Actor for NodeActor { msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "modifyAttributes" => { @@ -123,7 +126,7 @@ impl Actor for NodeActor { )) .unwrap(); let reply = ModifyAttributeReply { from: self.name() }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -289,11 +292,12 @@ impl Actor for WalkerActor { msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "querySelector" => { let msg = QuerySelectorReply { from: self.name() }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -310,13 +314,13 @@ impl Actor for WalkerActor { from: self.name(), node: node, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "clearPseudoClassLocks" => { let msg = ClearPseudoclassesReply { from: self.name() }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -343,7 +347,7 @@ impl Actor for WalkerActor { .collect(), from: self.name(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -471,6 +475,7 @@ impl Actor for PageStyleActor { msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "getApplied" => { @@ -481,7 +486,7 @@ impl Actor for PageStyleActor { sheets: vec![], from: self.name(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -491,7 +496,7 @@ impl Actor for PageStyleActor { computed: vec![], from: self.name(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -576,7 +581,7 @@ impl Actor for PageStyleActor { }; let msg = serde_json::to_string(&msg).unwrap(); let msg = serde_json::from_str::<Value>(&msg).unwrap(); - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -596,6 +601,7 @@ impl Actor for InspectorActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { let browsing_context = registry.find::<BrowsingContextActor>(&self.browsing_context); let pipeline = browsing_context.active_pipeline.get(); @@ -625,7 +631,7 @@ impl Actor for InspectorActor { root: node, }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -647,7 +653,7 @@ impl Actor for InspectorActor { actor: self.pageStyle.borrow().clone().unwrap(), }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -670,7 +676,7 @@ impl Actor for InspectorActor { actor: self.highlighter.borrow().clone().unwrap(), }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/memory.rs b/components/devtools/actors/memory.rs index 3240a105260..67f83548f9c 100644 --- a/components/devtools/actors/memory.rs +++ b/components/devtools/actors/memory.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -34,6 +35,7 @@ impl Actor for MemoryActor { _msg_type: &str, _msg: &Map<String, Value>, _stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(ActorMessageStatus::Ignored) } diff --git a/components/devtools/actors/network_event.rs b/components/devtools/actors/network_event.rs index 8af38f52c0c..622ac6002eb 100644 --- a/components/devtools/actors/network_event.rs +++ b/components/devtools/actors/network_event.rs @@ -8,6 +8,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use devtools_traits::HttpRequest as DevtoolsHttpRequest; use devtools_traits::HttpResponse as DevtoolsHttpResponse; use headers::{ContentType, Cookie, HeaderMapExt}; @@ -180,6 +181,7 @@ impl Actor for NetworkEventActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "getRequestHeaders" => { @@ -201,7 +203,7 @@ impl Actor for NetworkEventActor { headerSize: headersSize, rawHeaders: rawHeadersString, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getRequestCookies" => { @@ -217,7 +219,7 @@ impl Actor for NetworkEventActor { from: self.name(), cookies: cookies, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getRequestPostData" => { @@ -226,7 +228,7 @@ impl Actor for NetworkEventActor { postData: self.request.body.clone(), postDataDiscarded: false, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getResponseHeaders" => { @@ -251,7 +253,7 @@ impl Actor for NetworkEventActor { headerSize: headersSize, rawHeaders: rawHeadersString, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); } ActorMessageStatus::Processed }, @@ -268,7 +270,7 @@ impl Actor for NetworkEventActor { from: self.name(), cookies: cookies, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getResponseContent" => { @@ -277,7 +279,7 @@ impl Actor for NetworkEventActor { content: self.response.body.clone(), contentDiscarded: self.response.body.is_none(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getEventTimings" => { @@ -297,7 +299,7 @@ impl Actor for NetworkEventActor { timings: timingsObj, totalTime: total, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getSecurityInfo" => { @@ -308,7 +310,7 @@ impl Actor for NetworkEventActor { state: "insecure".to_owned(), }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, _ => ActorMessageStatus::Ignored, diff --git a/components/devtools/actors/object.rs b/components/devtools/actors/object.rs index fc2a2002e7d..b10f5e9fd65 100644 --- a/components/devtools/actors/object.rs +++ b/components/devtools/actors/object.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -21,6 +22,7 @@ impl Actor for ObjectActor { _: &str, _: &Map<String, Value>, _: &mut TcpStream, + _: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(ActorMessageStatus::Ignored) } diff --git a/components/devtools/actors/performance.rs b/components/devtools/actors/performance.rs index d26e4aee1d6..497922a2745 100644 --- a/components/devtools/actors/performance.rs +++ b/components/devtools/actors/performance.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::{ActorDescription, JsonPacketStream, Method}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -57,6 +58,7 @@ impl Actor for PerformanceActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "connect" => { @@ -72,7 +74,7 @@ impl Actor for PerformanceActor { }, }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "canCurrentlyRecord" => { @@ -83,7 +85,7 @@ impl Actor for PerformanceActor { errors: vec![], }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, _ => ActorMessageStatus::Ignored, diff --git a/components/devtools/actors/preference.rs b/components/devtools/actors/preference.rs index aefd8638631..a690148342f 100644 --- a/components/devtools/actors/preference.rs +++ b/components/devtools/actors/preference.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -28,6 +29,7 @@ impl Actor for PreferenceActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "getBoolPref" => { @@ -35,7 +37,7 @@ impl Actor for PreferenceActor { from: self.name(), value: false, }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -44,7 +46,7 @@ impl Actor for PreferenceActor { from: self.name(), value: "".to_owned(), }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -53,7 +55,7 @@ impl Actor for PreferenceActor { from: self.name(), value: 0, }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/process.rs b/components/devtools/actors/process.rs index de9a9f96ed1..8b2d54d5ee7 100644 --- a/components/devtools/actors/process.rs +++ b/components/devtools/actors/process.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -34,6 +35,7 @@ impl Actor for ProcessActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "listWorkers" => { @@ -41,7 +43,7 @@ impl Actor for ProcessActor { from: self.name(), workers: vec![], }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/profiler.rs b/components/devtools/actors/profiler.rs index a6aacb66caa..f7d5a338525 100644 --- a/components/devtools/actors/profiler.rs +++ b/components/devtools/actors/profiler.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -21,6 +22,7 @@ impl Actor for ProfilerActor { _msg_type: &str, _msg: &Map<String, Value>, _stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(ActorMessageStatus::Ignored) } diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs index 1965d973728..1f0ae9cd33e 100644 --- a/components/devtools/actors/root.rs +++ b/components/devtools/actors/root.rs @@ -12,6 +12,7 @@ use crate::actors::performance::PerformanceActor; use crate::actors::tab::{TabDescriptorActor, TabDescriptorActorMsg}; use crate::actors::worker::{WorkerActor, WorkerMsg}; use crate::protocol::{ActorDescription, JsonPacketStream}; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -124,6 +125,7 @@ impl Actor for RootActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "listAddons" => { @@ -131,7 +133,7 @@ impl Actor for RootActor { from: "root".to_owned(), addons: vec![], }; - stream.write_json_packet(&actor); + let _ = stream.write_json_packet(&actor); ActorMessageStatus::Processed }, @@ -144,7 +146,7 @@ impl Actor for RootActor { isParent: true, }], }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -157,7 +159,7 @@ impl Actor for RootActor { isParent: true, }, }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -169,7 +171,7 @@ impl Actor for RootActor { deviceActor: self.device.clone(), preferenceActor: self.preference.clone(), }; - stream.write_json_packet(&actor); + let _ = stream.write_json_packet(&actor); ActorMessageStatus::Processed }, @@ -188,7 +190,7 @@ impl Actor for RootActor { }) .collect(), }; - stream.write_json_packet(&actor); + let _ = stream.write_json_packet(&actor); ActorMessageStatus::Processed }, @@ -197,7 +199,7 @@ impl Actor for RootActor { from: self.name(), registrations: vec![], }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -210,7 +212,7 @@ impl Actor for RootActor { .map(|name| registry.find::<WorkerActor>(name).encodable()) .collect(), }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -220,7 +222,7 @@ impl Actor for RootActor { from: self.name(), tab: tab.encodable(®istry), }; - stream.write_json_packet(&reply); + let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed }, @@ -232,7 +234,7 @@ impl Actor for RootActor { device: DeviceActor::description(), }, }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/stylesheets.rs b/components/devtools/actors/stylesheets.rs index ccec3a7d30f..141a30ae106 100644 --- a/components/devtools/actors/stylesheets.rs +++ b/components/devtools/actors/stylesheets.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -27,6 +28,7 @@ impl Actor for StyleSheetsActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "getStyleSheets" => { @@ -34,7 +36,7 @@ impl Actor for StyleSheetsActor { from: self.name(), styleSheets: vec![], }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/tab.rs b/components/devtools/actors/tab.rs index 1f83d246233..bd2ab331769 100644 --- a/components/devtools/actors/tab.rs +++ b/components/devtools/actors/tab.rs @@ -6,6 +6,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg}; use crate::actors::root::RootActor; use crate::protocol::JsonPacketStream; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -48,13 +49,14 @@ impl Actor for TabDescriptorActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "getTarget" => { let frame = registry .find::<BrowsingContextActor>(&self.browsing_context_actor) .encodable(); - stream.write_json_packet(&GetTargetReply { + let _ = stream.write_json_packet(&GetTargetReply { from: self.name(), frame, }); diff --git a/components/devtools/actors/thread.rs b/components/devtools/actors/thread.rs index f0e2f1e98a2..084a7bcf506 100644 --- a/components/devtools/actors/thread.rs +++ b/components/devtools/actors/thread.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -84,6 +85,7 @@ impl Actor for ThreadActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "attach" => { @@ -100,8 +102,8 @@ impl Actor for ThreadActor { type_: "attached".to_owned(), }, }; - stream.write_json_packet(&msg); - stream.write_json_packet(&VoidAttachedReply { from: self.name() }); + let _ = stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&VoidAttachedReply { from: self.name() }); ActorMessageStatus::Processed }, @@ -110,8 +112,8 @@ impl Actor for ThreadActor { from: self.name(), type_: "resumed".to_owned(), }; - stream.write_json_packet(&msg); - stream.write_json_packet(&VoidAttachedReply { from: self.name() }); + let _ = stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&VoidAttachedReply { from: self.name() }); ActorMessageStatus::Processed }, @@ -120,12 +122,12 @@ impl Actor for ThreadActor { from: self.name(), type_: "interrupted".to_owned(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "reconfigure" => { - stream.write_json_packet(&ReconfigureReply { from: self.name() }); + let _ = stream.write_json_packet(&ReconfigureReply { from: self.name() }); ActorMessageStatus::Processed }, @@ -134,7 +136,7 @@ impl Actor for ThreadActor { from: self.name(), sources: vec![], }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/devtools/actors/timeline.rs b/components/devtools/actors/timeline.rs index 7ed53f9f8d4..b21990ae0b0 100644 --- a/components/devtools/actors/timeline.rs +++ b/components/devtools/actors/timeline.rs @@ -6,6 +6,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::framerate::FramerateActor; use crate::actors::memory::{MemoryActor, TimelineMemoryReply}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use devtools_traits::DevtoolScriptControlMsg; use devtools_traits::DevtoolScriptControlMsg::{DropTimelineMarkers, SetTimelineMarkers}; use devtools_traits::{PreciseTime, TimelineMarker, TimelineMarkerType}; @@ -14,6 +15,7 @@ use msg::constellation_msg::PipelineId; use serde::{Serialize, Serializer}; use serde_json::{Map, Value}; use std::cell::RefCell; +use std::error::Error; use std::net::TcpStream; use std::sync::{Arc, Mutex}; use std::thread; @@ -166,7 +168,9 @@ impl TimelineActor { while let Ok(Some(marker)) = receiver.try_recv() { markers.push(emitter.marker(marker)); } - emitter.send(markers); + if emitter.send(markers).is_err() { + break; + } thread::sleep(Duration::from_millis(DEFAULT_TIMELINE_DATA_PULL_TIMEOUT)); }) @@ -185,6 +189,7 @@ impl Actor for TimelineActor { msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, + _id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "start" => { @@ -199,6 +204,7 @@ impl Actor for TimelineActor { )) .unwrap(); + //TODO: support multiple connections by using root actor's streams instead. *self.stream.borrow_mut() = stream.try_clone().ok(); // init memory actor @@ -235,7 +241,7 @@ impl Actor for TimelineActor { from: self.name(), value: HighResolutionStamp::new(registry.start_stamp(), PreciseTime::now()), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -245,7 +251,7 @@ impl Actor for TimelineActor { value: HighResolutionStamp::new(registry.start_stamp(), PreciseTime::now()), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); self.script_sender .send(DropTimelineMarkers( self.pipeline, @@ -253,6 +259,7 @@ impl Actor for TimelineActor { )) .unwrap(); + //TODO: move this to the cleanup method. if let Some(ref actor_name) = *self.framerate_actor.borrow() { registry.drop_actor_later(actor_name.clone()); } @@ -272,7 +279,7 @@ impl Actor for TimelineActor { value: self.is_recording.lock().unwrap().clone(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -311,7 +318,7 @@ impl Emitter { } } - fn send(&mut self, markers: Vec<TimelineMarkerReply>) { + fn send(&mut self, markers: Vec<TimelineMarkerReply>) -> Result<(), Box<dyn Error>> { let end_time = PreciseTime::now(); let reply = MarkersEmitterReply { type_: "markers".to_owned(), @@ -319,7 +326,7 @@ impl Emitter { from: self.from.clone(), endTime: HighResolutionStamp::new(self.start_stamp, end_time), }; - self.stream.write_json_packet(&reply); + self.stream.write_json_packet(&reply)?; if let Some(ref actor_name) = self.framerate_actor { let mut lock = self.registry.lock(); @@ -331,7 +338,7 @@ impl Emitter { delta: HighResolutionStamp::new(self.start_stamp, end_time), timestamps: framerate_actor.take_pending_ticks(), }; - self.stream.write_json_packet(&framerateReply); + self.stream.write_json_packet(&framerateReply)?; } if let Some(ref actor_name) = self.memory_actor { @@ -343,7 +350,9 @@ impl Emitter { delta: HighResolutionStamp::new(self.start_stamp, end_time), measurement: memory_actor.measure(), }; - self.stream.write_json_packet(&memoryReply); + self.stream.write_json_packet(&memoryReply)?; } + + Ok(()) } } diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs index fd578d0d3dc..cf7b26448fa 100644 --- a/components/devtools/actors/worker.rs +++ b/components/devtools/actors/worker.rs @@ -4,6 +4,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::StreamId; use devtools_traits::DevtoolScriptControlMsg::WantsLiveNotifications; use devtools_traits::{DevtoolScriptControlMsg, WorkerId}; use ipc_channel::ipc::IpcSender; @@ -11,6 +12,7 @@ use msg::constellation_msg::TEST_PIPELINE_ID; use serde_json::{Map, Value}; use servo_url::ServoUrl; use std::cell::RefCell; +use std::collections::HashMap; use std::net::TcpStream; #[derive(Clone, Copy)] @@ -21,7 +23,7 @@ pub enum WorkerType { Service = 2, } -pub struct WorkerActor { +pub(crate) struct WorkerActor { pub name: String, pub console: String, pub thread: String, @@ -29,7 +31,7 @@ pub struct WorkerActor { pub url: ServoUrl, pub type_: WorkerType, pub script_chan: IpcSender<DevtoolScriptControlMsg>, - pub streams: RefCell<Vec<TcpStream>>, + pub streams: RefCell<HashMap<StreamId, TcpStream>>, } impl WorkerActor { @@ -58,6 +60,7 @@ impl Actor for WorkerActor { msg_type: &str, _msg: &Map<String, Value>, stream: &mut TcpStream, + id: StreamId, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { "attach" => { @@ -66,8 +69,12 @@ impl Actor for WorkerActor { type_: "attached".to_owned(), url: self.url.as_str().to_owned(), }; - self.streams.borrow_mut().push(stream.try_clone().unwrap()); - stream.write_json_packet(&msg); + if stream.write_json_packet(&msg).is_err() { + return Ok(ActorMessageStatus::Processed); + } + self.streams + .borrow_mut() + .insert(id, stream.try_clone().unwrap()); // FIXME: fix messages to not require forging a pipeline for worker messages self.script_chan .send(WantsLiveNotifications(TEST_PIPELINE_ID, true)) @@ -82,7 +89,7 @@ impl Actor for WorkerActor { threadActor: self.thread.clone(), consoleActor: self.console.clone(), }; - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -91,18 +98,23 @@ impl Actor for WorkerActor { from: self.name(), type_: "detached".to_string(), }; - // FIXME: we should ensure we're removing the correct stream. - self.streams.borrow_mut().pop(); - stream.write_json_packet(&msg); - self.script_chan - .send(WantsLiveNotifications(TEST_PIPELINE_ID, false)) - .unwrap(); + let _ = stream.write_json_packet(&msg); + self.cleanup(id); ActorMessageStatus::Processed }, _ => ActorMessageStatus::Ignored, }) } + + fn cleanup(&self, id: StreamId) { + self.streams.borrow_mut().remove(&id); + if self.streams.borrow().is_empty() { + self.script_chan + .send(WantsLiveNotifications(TEST_PIPELINE_ID, false)) + .unwrap(); + } + } } #[derive(Serialize)] diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 62238c656ee..28c6675da42 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -127,6 +127,9 @@ pub fn start_server(port: u16, embedder: EmbedderProxy) -> Sender<DevtoolsContro sender } +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub(crate) struct StreamId(u32); + fn run_server( sender: Sender<DevtoolsControlMsg>, receiver: Receiver<DevtoolsControlMsg>, @@ -188,22 +191,25 @@ fn run_server( let mut actor_workers: HashMap<WorkerId, String> = HashMap::new(); /// Process the input from a single devtools client until EOF. - fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) { + fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream, id: StreamId) { debug!("connection established to {}", stream.peer_addr().unwrap()); { let actors = actors.lock().unwrap(); let msg = actors.find::<RootActor>("root").encodable(); - stream.write_json_packet(&msg); + if let Err(e) = stream.write_json_packet(&msg) { + warn!("Error writing response: {:?}", e); + return; + } } 'outer: loop { match stream.read_json_packet() { Ok(Some(json_packet)) => { - if let Err(()) = actors - .lock() - .unwrap() - .handle_message(json_packet.as_object().unwrap(), &mut stream) - { + if let Err(()) = actors.lock().unwrap().handle_message( + json_packet.as_object().unwrap(), + &mut stream, + id, + ) { debug!("error: devtools actor stopped responding"); let _ = stream.shutdown(Shutdown::Both); break 'outer; @@ -219,6 +225,8 @@ fn run_server( }, } } + + actors.lock().unwrap().cleanup(id); } fn handle_framerate_tick(actors: Arc<Mutex<ActorRegistry>>, actor_name: String, tick: f64) { @@ -451,7 +459,7 @@ fn run_server( eventActor: actor.event_actor(), }; for stream in &mut connections { - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); } }, NetworkEvent::HttpResponse(httpresponse) => { @@ -464,7 +472,7 @@ fn run_server( updateType: "requestHeaders".to_owned(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &actor.request_headers()); + let _ = stream.write_merged_json_packet(&msg, &actor.request_headers()); } let msg = NetworkEventUpdateMsg { @@ -473,7 +481,7 @@ fn run_server( updateType: "requestCookies".to_owned(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &actor.request_cookies()); + let _ = stream.write_merged_json_packet(&msg, &actor.request_cookies()); } //Send a networkEventUpdate (responseStart) to the client @@ -485,7 +493,7 @@ fn run_server( }; for stream in &mut connections { - stream.write_json_packet(&msg); + let _ = stream.write_json_packet(&msg); } let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), @@ -496,7 +504,7 @@ fn run_server( totalTime: actor.total_time(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &extra); + let _ = stream.write_merged_json_packet(&msg, &extra); } let msg = NetworkEventUpdateMsg { @@ -508,7 +516,7 @@ fn run_server( state: "insecure".to_owned(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &extra); + let _ = stream.write_merged_json_packet(&msg, &extra); } let msg = NetworkEventUpdateMsg { @@ -517,7 +525,7 @@ fn run_server( updateType: "responseContent".to_owned(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &actor.response_content()); + let _ = stream.write_merged_json_packet(&msg, &actor.response_content()); } let msg = NetworkEventUpdateMsg { @@ -526,7 +534,7 @@ fn run_server( updateType: "responseCookies".to_owned(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &actor.response_cookies()); + let _ = stream.write_merged_json_packet(&msg, &actor.response_cookies()); } let msg = NetworkEventUpdateMsg { @@ -535,7 +543,7 @@ fn run_server( updateType: "responseHeaders".to_owned(), }; for stream in &mut connections { - stream.write_merged_json_packet(&msg, &actor.response_headers()); + let _ = stream.write_merged_json_packet(&msg, &actor.response_headers()); } }, } @@ -583,15 +591,18 @@ fn run_server( }) .expect("Thread spawning failed"); + let mut next_id = StreamId(0); while let Ok(msg) = receiver.recv() { debug!("{:?}", msg); match msg { DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => { let actors = actors.clone(); + let id = next_id; + next_id = StreamId(id.0 + 1); accepted_connections.push(stream.try_clone().unwrap()); thread::Builder::new() .name("DevtoolsClientHandler".to_owned()) - .spawn(move || handle_client(actors, stream.try_clone().unwrap())) + .spawn(move || handle_client(actors, stream.try_clone().unwrap(), id)) .expect("Thread spawning failed"); }, DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::FramerateTick( diff --git a/components/devtools/protocol.rs b/components/devtools/protocol.rs index 29fbaa2beeb..b01c982a762 100644 --- a/components/devtools/protocol.rs +++ b/components/devtools/protocol.rs @@ -8,6 +8,7 @@ use serde::Serialize; use serde_json::{self, Value}; +use std::error::Error; use std::io::{Read, Write}; use std::net::TcpStream; @@ -26,29 +27,38 @@ pub struct Method { } pub trait JsonPacketStream { - fn write_json_packet<T: Serialize>(&mut self, obj: &T); - fn write_merged_json_packet<T: Serialize, U: Serialize>(&mut self, base: &T, extra: &U); + fn write_json_packet<T: Serialize>(&mut self, obj: &T) -> Result<(), Box<dyn Error>>; + fn write_merged_json_packet<T: Serialize, U: Serialize>( + &mut self, + base: &T, + extra: &U, + ) -> Result<(), Box<dyn Error>>; fn read_json_packet(&mut self) -> Result<Option<Value>, String>; } impl JsonPacketStream for TcpStream { - fn write_json_packet<T: Serialize>(&mut self, obj: &T) { - let s = serde_json::to_string(obj).unwrap(); + fn write_json_packet<T: Serialize>(&mut self, obj: &T) -> Result<(), Box<dyn Error>> { + let s = serde_json::to_string(obj)?; debug!("<- {}", s); - write!(self, "{}:{}", s.len(), s).unwrap(); + write!(self, "{}:{}", s.len(), s)?; + Ok(()) } - fn write_merged_json_packet<T: Serialize, U: Serialize>(&mut self, base: &T, extra: &U) { - let mut obj = serde_json::to_value(base).unwrap(); + fn write_merged_json_packet<T: Serialize, U: Serialize>( + &mut self, + base: &T, + extra: &U, + ) -> Result<(), Box<dyn Error>> { + let mut obj = serde_json::to_value(base)?; let obj = obj.as_object_mut().unwrap(); - let extra = serde_json::to_value(extra).unwrap(); + let extra = serde_json::to_value(extra)?; let extra = extra.as_object().unwrap(); for (key, value) in extra { obj.insert(key.to_owned(), value.to_owned()); } - self.write_json_packet(obj); + self.write_json_packet(obj) } fn read_json_packet(&mut self) -> Result<Option<Value>, String> { @@ -74,7 +84,9 @@ impl JsonPacketStream for TcpStream { Err(_) => return Err("packet length missing / not parsable".to_owned()), }; let mut packet = String::new(); - self.take(packet_len).read_to_string(&mut packet).unwrap(); + self.take(packet_len) + .read_to_string(&mut packet) + .map_err(|e| e.to_string())?; debug!("{}", packet); return match serde_json::from_str(&packet) { Ok(json) => Ok(Some(json)), diff --git a/components/script/dom/mediasession.rs b/components/script/dom/mediasession.rs index f77efe3f54f..49112ba921b 100644 --- a/components/script/dom/mediasession.rs +++ b/components/script/dom/mediasession.rs @@ -20,7 +20,7 @@ use crate::dom::bindings::str::DOMString; use crate::dom::htmlmediaelement::HTMLMediaElement; use crate::dom::mediametadata::MediaMetadata; use crate::dom::window::Window; -use crate::realms::{AlreadyInRealm, InRealm}; +use crate::realms::{enter_realm, InRealm}; use dom_struct::dom_struct; use embedder_traits::MediaMetadata as EmbedderMediaMetadata; use embedder_traits::MediaSessionEvent; @@ -80,8 +80,8 @@ impl MediaSession { if let Some(media) = self.media_instance.get() { match action { MediaSessionActionType::Play => { - let in_realm_proof = AlreadyInRealm::assert(&self.global()); - media.Play(InRealm::Already(&in_realm_proof)); + let realm = enter_realm(self); + media.Play(InRealm::Entered(&realm)); }, MediaSessionActionType::Pause => { media.Pause(); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 76ed789e8ad..a4d27057446 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -614,6 +614,7 @@ pub mod xrinputsourceschangeevent; pub mod xrjointpose; pub mod xrjointspace; pub mod xrlayer; +pub mod xrlayerevent; pub mod xrmediabinding; pub mod xrpose; pub mod xrprojectionlayer; diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs index 9539ac90507..8172b1b6c4e 100644 --- a/components/script/dom/webglframebuffer.rs +++ b/components/script/dom/webglframebuffer.rs @@ -205,6 +205,9 @@ impl WebGLFramebuffer { } pub fn is_deleted(&self) -> bool { + // TODO: if a framebuffer has an attachment which is invalid due to + // being outside a webxr rAF, should this make the framebuffer invalid? + // https://github.com/immersive-web/layers/issues/196 self.is_deleted.get() } @@ -447,6 +450,9 @@ impl WebGLFramebuffer { } else { self.status.get() } + // TODO: if a framebuffer has an attachment which is invalid due to + // being outside a webxr rAF, should this make the framebuffer incomplete? + // https://github.com/immersive-web/layers/issues/196 } pub fn check_status_for_rendering(&self) -> CompleteForRendering { @@ -497,6 +503,10 @@ impl WebGLFramebuffer { self.is_initialized.set(true); } + // TODO: if a framebuffer has an attachment which is invalid due to + // being outside a webxr rAF, should this make the framebuffer incomplete? + // https://github.com/immersive-web/layers/issues/196 + CompleteForRendering::Complete } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 168d31fdf5d..9ef96b1cd19 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -3563,7 +3563,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool { texture.map_or(false, |tex| { - self.validate_ownership(tex).is_ok() && tex.target().is_some() && !tex.is_deleted() + self.validate_ownership(tex).is_ok() && tex.target().is_some() && !tex.is_invalid() }) } diff --git a/components/script/dom/webgltexture.rs b/components/script/dom/webgltexture.rs index 02cedeb5755..7456f772dec 100644 --- a/components/script/dom/webgltexture.rs +++ b/components/script/dom/webgltexture.rs @@ -9,11 +9,13 @@ use crate::dom::bindings::codegen::Bindings::EXTTextureFilterAnisotropicBinding: use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::Dom; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::webgl_validations::types::TexImageTarget; use crate::dom::webglframebuffer::WebGLFramebuffer; use crate::dom::webglobject::WebGLObject; use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; +use crate::dom::xrsession::XRSession; use canvas_traits::webgl::{ webgl_channel, TexDataType, TexFormat, TexParameter, TexParameterBool, TexParameterInt, WebGLResult, WebGLTextureId, @@ -31,10 +33,11 @@ pub enum TexParameterValue { // Textures generated for WebXR are owned by the WebXR device, not by the WebGL thread // so the GL texture should not be deleted when the texture is garbage collected. -#[derive(Clone, Copy, Debug, Eq, JSTraceable, MallocSizeOf, PartialEq)] +#[unrooted_must_root_lint::must_root] +#[derive(JSTraceable, MallocSizeOf)] enum WebGLTextureOwner { WebGL, - WebXR, + WebXR(Dom<XRSession>), } const MAX_LEVEL_COUNT: usize = 31; @@ -71,14 +74,16 @@ impl WebGLTexture { fn new_inherited( context: &WebGLRenderingContext, id: WebGLTextureId, - owner: WebGLTextureOwner, + owner: Option<&XRSession>, ) -> Self { Self { webgl_object: WebGLObject::new_inherited(context), id: id, target: Cell::new(None), is_deleted: Cell::new(false), - owner: owner, + owner: owner + .map(|session| WebGLTextureOwner::WebXR(Dom::from_ref(session))) + .unwrap_or(WebGLTextureOwner::WebGL), immutable_levels: Cell::new(None), face_count: Cell::new(0), base_mipmap_level: 0, @@ -101,22 +106,18 @@ impl WebGLTexture { pub fn new(context: &WebGLRenderingContext, id: WebGLTextureId) -> DomRoot<Self> { reflect_dom_object( - Box::new(WebGLTexture::new_inherited( - context, - id, - WebGLTextureOwner::WebGL, - )), + Box::new(WebGLTexture::new_inherited(context, id, None)), &*context.global(), ) } - pub fn new_webxr(context: &WebGLRenderingContext, id: WebGLTextureId) -> DomRoot<Self> { + pub fn new_webxr( + context: &WebGLRenderingContext, + id: WebGLTextureId, + session: &XRSession, + ) -> DomRoot<Self> { reflect_dom_object( - Box::new(WebGLTexture::new_inherited( - context, - id, - WebGLTextureOwner::WebXR, - )), + Box::new(WebGLTexture::new_inherited(context, id, Some(session))), &*context.global(), ) } @@ -129,7 +130,7 @@ impl WebGLTexture { // NB: Only valid texture targets come here pub fn bind(&self, target: u32) -> WebGLResult<()> { - if self.is_deleted.get() { + if self.is_invalid() { return Err(WebGLError::InvalidOperation); } @@ -246,7 +247,7 @@ impl WebGLTexture { } // We don't delete textures owned by WebXR - if self.owner == WebGLTextureOwner::WebXR { + if let WebGLTextureOwner::WebXR(_) = self.owner { return; } @@ -258,7 +259,13 @@ impl WebGLTexture { } } - pub fn is_deleted(&self) -> bool { + pub fn is_invalid(&self) -> bool { + // https://immersive-web.github.io/layers/#xrwebglsubimagetype + if let WebGLTextureOwner::WebXR(ref session) = self.owner { + if session.is_outside_raf() { + return true; + } + } self.is_deleted.get() } diff --git a/components/script/dom/webidls/XRLayerEvent.webidl b/components/script/dom/webidls/XRLayerEvent.webidl index c90b9bfb27b..9a05272138e 100644 --- a/components/script/dom/webidls/XRLayerEvent.webidl +++ b/components/script/dom/webidls/XRLayerEvent.webidl @@ -3,12 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://immersive-web.github.io/layers/#xrlayerevent-interface - -// [SecureContext, Exposed=Window] -// interface XRLayerEvent : Event { -// constructor(DOMString type, XRLayerEventInit eventInitDict); -// [SameObject] readonly attribute XRLayer layer; -// }; +[SecureContext, Exposed=Window, Pref="dom.webxr.layers.enabled"] +interface XRLayerEvent : Event { + constructor(DOMString type, XRLayerEventInit eventInitDict); + [SameObject] readonly attribute XRLayer layer; +}; dictionary XRLayerEventInit : EventInit { required XRLayer layer; diff --git a/components/script/dom/xrlayerevent.rs b/components/script/dom/xrlayerevent.rs new file mode 100644 index 00000000000..d3485fa1c51 --- /dev/null +++ b/components/script/dom/xrlayerevent.rs @@ -0,0 +1,62 @@ +/* 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 crate::dom::bindings::codegen::Bindings::EventBinding::EventBinding::EventMethods; +use crate::dom::bindings::codegen::Bindings::XRLayerEventBinding::XRLayerEventInit; +use crate::dom::bindings::codegen::Bindings::XRLayerEventBinding::XRLayerEventMethods; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::Dom; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::event::Event; +use crate::dom::window::Window; +use crate::dom::xrlayer::XRLayer; +use dom_struct::dom_struct; +use servo_atoms::Atom; + +// https://w3c.github.io/uievents/#interface-uievent +#[dom_struct] +pub struct XRLayerEvent { + event: Event, + layer: Dom<XRLayer>, +} + +impl XRLayerEvent { + pub fn new_inherited(layer: &XRLayer) -> XRLayerEvent { + XRLayerEvent { + event: Event::new_inherited(), + layer: Dom::from_ref(layer), + } + } + + pub fn new(window: &Window, layer: &XRLayer) -> DomRoot<XRLayerEvent> { + reflect_dom_object(Box::new(XRLayerEvent::new_inherited(layer)), window) + } + + #[allow(non_snake_case)] + pub fn Constructor( + window: &Window, + type_: DOMString, + init: &XRLayerEventInit, + ) -> DomRoot<XRLayerEvent> { + let event = XRLayerEvent::new(window, &init.layer); + let type_ = Atom::from(type_); + let bubbles = init.parent.bubbles; + let cancelable = init.parent.cancelable; + event.event.init_event(type_, bubbles, cancelable); + event + } +} + +impl XRLayerEventMethods for XRLayerEvent { + // https://immersive-web.github.io/layers/#dom-xrlayerevent-layer + fn Layer(&self) -> DomRoot<XRLayer> { + DomRoot::from_ref(&self.layer) + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } +} diff --git a/components/script/dom/xrwebgllayer.rs b/components/script/dom/xrwebgllayer.rs index 7f0a7883961..dd1e4eff57f 100644 --- a/components/script/dom/xrwebgllayer.rs +++ b/components/script/dom/xrwebgllayer.rs @@ -193,10 +193,11 @@ impl XRWebGLLayer { let framebuffer = self.framebuffer.as_ref()?; let context = framebuffer.upcast::<WebGLObject>().context(); let sub_images = frame.get_sub_images(self.layer_id()?)?; + let session = self.session(); // TODO: Cache this texture let color_texture_id = WebGLTextureId::maybe_new(sub_images.sub_image.as_ref()?.color_texture)?; - let color_texture = WebGLTexture::new_webxr(context, color_texture_id); + let color_texture = WebGLTexture::new_webxr(context, color_texture_id, session); let target = self.texture_target(); // Save the current bindings @@ -230,7 +231,8 @@ impl XRWebGLLayer { if let Some(id) = sub_images.sub_image.as_ref()?.depth_stencil_texture { // TODO: Cache this texture let depth_stencil_texture_id = WebGLTextureId::maybe_new(id)?; - let depth_stencil_texture = WebGLTexture::new_webxr(context, depth_stencil_texture_id); + let depth_stencil_texture = + WebGLTexture::new_webxr(context, depth_stencil_texture_id, session); framebuffer .texture2d_even_if_opaque( constants::DEPTH_STENCIL_ATTACHMENT, diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 2bcc5694074..09e6a3ef078 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -144,9 +144,8 @@ mod media_platform { #[cfg(feature = "uwp")] fn set_gstreamer_log_handler() { - use gstreamer::{debug_add_log_function, debug_remove_default_log_function, DebugLevel}; + use gstreamer::{debug_add_log_function, DebugLevel}; - debug_remove_default_log_function(); debug_add_log_function(|cat, level, file, function, line, _, message| { let message = format!( "{:?} {:?} {:?}:{:?}:{:?} {:?}", diff --git a/components/webrender_surfman/Cargo.toml b/components/webrender_surfman/Cargo.toml index 82ae827cbb7..663b30be7b1 100644 --- a/components/webrender_surfman/Cargo.toml +++ b/components/webrender_surfman/Cargo.toml @@ -13,5 +13,5 @@ path = "lib.rs" [dependencies] euclid = "0.20" surfman = "0.3" -surfman-chains = "0.4" +surfman-chains = "0.5" diff --git a/components/webrender_surfman/lib.rs b/components/webrender_surfman/lib.rs index 530ca0ebfc7..533614256c1 100644 --- a/components/webrender_surfman/lib.rs +++ b/components/webrender_surfman/lib.rs @@ -147,7 +147,7 @@ impl WebrenderSurfman { let ref mut device = self.0.device.borrow_mut(); let ref mut context = self.0.context.borrow_mut(); if let Some(ref swap_chain) = self.0.swap_chain { - return swap_chain.swap_buffers(device, context); + return swap_chain.swap_buffers(device, context, surfman_chains::PreserveBuffer::No); } let mut surface = device.unbind_surface_from_context(context)?.unwrap(); device.present_surface(context, &mut surface)?; diff --git a/ports/gstplugin/Cargo.toml b/ports/gstplugin/Cargo.toml index 05816b4e629..4ad10399c0d 100644 --- a/ports/gstplugin/Cargo.toml +++ b/ports/gstplugin/Cargo.toml @@ -30,7 +30,7 @@ log = "0.4" servo-media = { git = "https://github.com/servo/media" } sparkle = "0.1" surfman = "0.3" -surfman-chains = "0.4" +surfman-chains = "0.5" surfman-chains-api = "0.2" webxr = { git = "https://github.com/servo/webxr", features = ["glwindow"] } diff --git a/python/servo/command_base.py b/python/servo/command_base.py index f25721a5652..d1ed5271b67 100644 --- a/python/servo/command_base.py +++ b/python/servo/command_base.py @@ -864,6 +864,7 @@ install them, let us know by filing a bug!") not target or ("armv7" in target and "android" in target) or "x86_64" in target + or "uwp" in target ): media_stack = "gstreamer" else: diff --git a/support/hololens/ServoApp/BrowserPage.cpp b/support/hololens/ServoApp/BrowserPage.cpp index d3f738427c6..42282357834 100644 --- a/support/hololens/ServoApp/BrowserPage.cpp +++ b/support/hololens/ServoApp/BrowserPage.cpp @@ -16,6 +16,7 @@ using namespace winrt::Windows::UI::Core; using namespace winrt::Windows::UI::ViewManagement; using namespace winrt::Windows::ApplicationModel::Core; using namespace winrt::Windows::ApplicationModel::Resources; +using namespace winrt::Windows::ApplicationModel::Resources::Core; using namespace winrt::Windows::UI::Notifications; using namespace winrt::Windows::Data::Json; using namespace winrt::Windows::Data::Xml::Dom; @@ -28,6 +29,17 @@ BrowserPage::BrowserPage() { InitializeComponent(); BindServoEvents(); mLogs = winrt::single_threaded_observable_vector<IInspectable>(); + + auto ctx = ResourceContext::GetForCurrentView(); + auto current = ResourceManager::Current(); + auto tree = current.MainResourceMap().GetSubtree(L"PromotedPrefs"); + for (auto s : tree) { + hstring k = s.Key(); + std::wstring wk = k.c_str(); + std::replace(wk.begin(), wk.end(), '/', '.'); + hstring v = s.Value().Resolve(ctx).ValueAsString(); + mPromotedPrefs.insert(std::pair(wk, v)); + } } void BrowserPage::BindServoEvents() { @@ -69,6 +81,8 @@ void BrowserPage::BindServoEvents() { urlTextbox().GotFocus(std::bind(&BrowserPage::OnURLFocused, this, _1)); servoView().OnMediaSessionMetadata( [=](hstring title, hstring artist, hstring album) {}); + servoView().OnMediaSessionPosition( + [=](double duration, double position, double rate) {}); servoView().OnMediaSessionPlaybackStateChange([=](const auto &, int state) { if (state == Servo::MediaSessionPlaybackState::None) { mediaControls().Visibility(Visibility::Collapsed); @@ -182,17 +196,34 @@ void BrowserPage::UpdatePref(ServoApp::Pref pref, Controls::Control ctrl) { stack.Children().GetAt(2).as<Controls::Button>().IsEnabled(!pref.IsDefault()); } +void BrowserPage::OnSeeAllPrefClicked(IInspectable const &, + RoutedEventArgs const &) { + BuildPrefList(); +} + // Retrieve the preference list from Servo and build the preference table. void BrowserPage::BuildPrefList() { + prefList().Children().Clear(); + bool promoted = !seeAllPrefCheckBox().IsChecked().GetBoolean(); + preferenceSearchbox().Visibility(promoted ? Visibility::Collapsed + : Visibility::Visible); + preferenceSearchbox().Text(L""); // It would be better to use a template and bindings, but the // <ListView> takes too long to generate all the items, and // it's pretty difficiult to have different controls depending // on the pref type. - prefList().Children().Clear(); auto resourceLoader = ResourceLoader::GetForCurrentView(); auto resetStr = resourceLoader.GetString(L"devtoolsPreferenceResetButton/Content"); for (auto pref : servoView().Preferences()) { + std::optional<hstring> description = {}; + if (promoted) { + auto search = mPromotedPrefs.find(pref.Key()); + if (search == mPromotedPrefs.end()) { + continue; + } + description = {search->second}; + } auto value = pref.Value(); auto type = value.as<IPropertyValue>().Type(); std::optional<Controls::Control> ctrl; @@ -243,7 +274,7 @@ void BrowserPage::BuildPrefList() { stack.Padding({4, 4, 4, 4}); stack.Orientation(Controls::Orientation::Horizontal); auto key = Controls::TextBlock(); - key.Text(pref.Key()); + key.Text(promoted ? *description : pref.Key()); key.Width(350); if (!pref.IsDefault()) { auto font = winrt::Windows::UI::Text::FontWeights::Bold(); diff --git a/support/hololens/ServoApp/BrowserPage.h b/support/hololens/ServoApp/BrowserPage.h index a5ad43b4286..2a434d19354 100644 --- a/support/hololens/ServoApp/BrowserPage.h +++ b/support/hololens/ServoApp/BrowserPage.h @@ -34,6 +34,7 @@ public: void OnDevtoolsButtonClicked(IInspectable const &, RoutedEventArgs const &); void OnJSInputEdited(IInspectable const &, Input::KeyRoutedEventArgs const &); void OnURLEdited(IInspectable const &, Input::KeyRoutedEventArgs const &); + void OnSeeAllPrefClicked(IInspectable const &, RoutedEventArgs const &); void OnURLFocused(IInspectable const &); void OnURLKeyboardAccelerator(IInspectable const &, @@ -53,13 +54,13 @@ public: void ClearConsole(); void OnDevtoolsDetached(); Collections::IObservableVector<IInspectable> ConsoleLogs() { return mLogs; }; + void BuildPrefList(); private: void SetTransientMode(bool); void UpdatePref(ServoApp::Pref, Controls::Control); void CheckCrashReport(); void BindServoEvents(); - void BuildPrefList(); void ShowToolbox(); void HideToolbox(); DevtoolsStatus mDevtoolsStatus = DevtoolsStatus::Stopped; @@ -68,6 +69,7 @@ private: bool mPanicking = false; std::unique_ptr<servo::DevtoolsClient> mDevtoolsClient; Collections::IObservableVector<IInspectable> mLogs; + std::map<hstring, hstring> mPromotedPrefs; }; struct ConsoleLog : ConsoleLogT<ConsoleLog> { diff --git a/support/hololens/ServoApp/BrowserPage.xaml b/support/hololens/ServoApp/BrowserPage.xaml index d85947b8807..28d6871a3c9 100644 --- a/support/hololens/ServoApp/BrowserPage.xaml +++ b/support/hololens/ServoApp/BrowserPage.xaml @@ -192,11 +192,13 @@ <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> + <RowDefinition Height="auto"/> </Grid.RowDefinitions> - <TextBox Grid.Row="0" IsTabStop="true" x:Uid="preferenceSearchbox" x:Name="preferenceSearchbox" VerticalAlignment="Center" KeyUp="OnPrefererenceSearchboxEdited" IsSpellCheckEnabled="False" Margin="3"/> + <CheckBox Margin="3" x:Name="seeAllPrefCheckBox" x:Uid="seeAllPrefCheckBox" Click="OnSeeAllPrefClicked" IsChecked="False" Grid.Row="0"/> <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto"> <StackPanel x:Name="prefList"/> </ScrollViewer> + <TextBox Grid.Row="2" IsTabStop="true" x:Uid="preferenceSearchbox" x:Name="preferenceSearchbox" VerticalAlignment="Center" KeyUp="OnPrefererenceSearchboxEdited" IsSpellCheckEnabled="False" Margin="3"/> </Grid> </muxc:TabViewItem> <muxc:TabViewItem x:Uid="crashTab" x:Name="crashTab" IsClosable="False" Visibility="Collapsed"> diff --git a/support/hololens/ServoApp/Resources/PromotedPrefs.resw b/support/hololens/ServoApp/Resources/PromotedPrefs.resw new file mode 100644 index 00000000000..abacabb99fa --- /dev/null +++ b/support/hololens/ServoApp/Resources/PromotedPrefs.resw @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="dom.webxr.hands.enabled"><value>WebXR: Enable articulated hand input</value></data> + <data name="dom.webxr.layers.enabled"><value>WebXR: Enable composition layers</value></data> + <data name="dom.webxr.sessionavailable"><value>WebXR: Enable "sessionavailable" event</value></data> + <data name="dom.webxr.first_person_observer_view"><value>WebXR: Enable first person observer view</value></data> + <data name="dom.webgl2.enabled"><value>Enable WebGL2</value></data> +</root> diff --git a/support/hololens/ServoApp/Strings/en-US/Resources.resw b/support/hololens/ServoApp/Resources/Strings/en-US/Resources.resw index 717ffbff23b..2a271eed980 100644 --- a/support/hololens/ServoApp/Strings/en-US/Resources.resw +++ b/support/hololens/ServoApp/Resources/Strings/en-US/Resources.resw @@ -171,4 +171,7 @@ <data name="ContextMenu.title" xml:space="preserve"> <value>Menu</value> </data> + <data name="seeAllPrefCheckBox.Content" xml:space="preserve"> + <value>See all preferences</value> + </data> </root> diff --git a/support/hololens/ServoApp/Strings/fr-FR/Resources.resw b/support/hololens/ServoApp/Resources/Strings/fr-FR/Resources.resw index 5fd44dbad16..5fd44dbad16 100644 --- a/support/hololens/ServoApp/Strings/fr-FR/Resources.resw +++ b/support/hololens/ServoApp/Resources/Strings/fr-FR/Resources.resw diff --git a/support/hololens/ServoApp/ServoApp.vcxproj b/support/hololens/ServoApp/ServoApp.vcxproj index c5ec6a5317c..ec28c1d9a27 100644 --- a/support/hololens/ServoApp/ServoApp.vcxproj +++ b/support/hololens/ServoApp/ServoApp.vcxproj @@ -87,6 +87,8 @@ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\..\..\..\target\aarch64-uwp-windows-msvc\debug\</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\..\..\..\target\x86_64-uwp-windows-msvc\debug\</AdditionalIncludeDirectories> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">stdcpplatest</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdcpplatest</LanguageStandard> </ClCompile> <Link> <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">OneCore.lib;WindowsApp.lib;%(AdditionalDependencies);simpleservo.dll.lib</AdditionalDependencies> @@ -105,6 +107,8 @@ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\..\..\..\target\aarch64-uwp-windows-msvc\release</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories);$(ProjectDir)\..\..\..\target\x86_64-uwp-windows-msvc\release</AdditionalIncludeDirectories> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">stdcpplatest</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|x64'">stdcpplatest</LanguageStandard> </ClCompile> <Link> <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">OneCore.lib;WindowsApp.lib;%(AdditionalDependencies);simpleservo.dll.lib</AdditionalDependencies> @@ -988,8 +992,9 @@ <None Include="PropertySheet.props" /> </ItemGroup> <ItemGroup> - <PRIResource Include="Strings\en-US\Resources.resw" /> - <PRIResource Include="Strings\fr-FR\Resources.resw" /> + <PRIResource Include="Resources\PromotedPrefs.resw" /> + <PRIResource Include="Resources\Strings\en-US\Resources.resw" /> + <PRIResource Include="Resources\Strings\fr-FR\Resources.resw" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> @@ -1009,4 +1014,4 @@ <Error Condition="!Exists('..\packages\ANGLE.WindowsStore.Servo.2.1.19\build\native\ANGLE.WindowsStore.Servo.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ANGLE.WindowsStore.Servo.2.1.19\build\native\ANGLE.WindowsStore.Servo.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.UI.Xaml.2.4.2\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.UI.Xaml.2.4.2\build\native\Microsoft.UI.Xaml.targets'))" /> </Target> -</Project> +</Project>
\ No newline at end of file diff --git a/support/hololens/ServoApp/ServoApp.vcxproj.filters b/support/hololens/ServoApp/ServoApp.vcxproj.filters index 0eb6821188e..a017554e1ba 100644 --- a/support/hololens/ServoApp/ServoApp.vcxproj.filters +++ b/support/hololens/ServoApp/ServoApp.vcxproj.filters @@ -924,13 +924,16 @@ <Filter Include="ServoControl"> <UniqueIdentifier>{d21a959c-19d1-4a54-b942-692c27e5b3a6}</UniqueIdentifier> </Filter> - <Filter Include="Strings"> + <Filter Include="Resources"> + <UniqueIdentifier>{c05cce0c-d62c-11ea-87d0-0242ac130003}</UniqueIdentifier> + </Filter> + <Filter Include="Resources\Strings"> <UniqueIdentifier>{49e23631-d899-4caf-bf7b-30776fee4d09}</UniqueIdentifier> </Filter> - <Filter Include="Strings\en-US"> + <Filter Include="Resources\Strings\en-US"> <UniqueIdentifier>{c12ff5d4-3730-4a0e-8b16-56ded3138875}</UniqueIdentifier> </Filter> - <Filter Include="Strings\fr-FR"> + <Filter Include="Resources\Strings\fr-FR"> <UniqueIdentifier>{b7d3273d-a27c-4176-87a1-3d5222b796b3}</UniqueIdentifier> </Filter> <Filter Include="Devtools"> @@ -950,11 +953,14 @@ <ApplicationDefinition Include="App.xaml" /> </ItemGroup> <ItemGroup> - <PRIResource Include="Strings\en-US\Resources.resw"> - <Filter>Strings\en-US</Filter> + <PRIResource Include="Resources\PromotedPrefs.resw"> + <Filter>Resources</Filter> + </PRIResource> + <PRIResource Include="Resources\Strings\en-US\Resources.resw"> + <Filter>Resources\Strings\en-US</Filter> </PRIResource> - <PRIResource Include="Strings\fr-FR\Resources.resw"> - <Filter>Strings\fr-FR</Filter> + <PRIResource Include="Resources\Strings\fr-FR\Resources.resw"> + <Filter>Resources\Strings\fr-FR</Filter> </PRIResource> </ItemGroup> </Project> diff --git a/support/hololens/ServoApp/ServoControl/Servo.cpp b/support/hololens/ServoApp/ServoControl/Servo.cpp index 4e3eeedf981..0cc51903f4b 100644 --- a/support/hololens/ServoApp/ServoControl/Servo.cpp +++ b/support/hololens/ServoApp/ServoControl/Servo.cpp @@ -99,6 +99,12 @@ const char *get_clipboard_contents() { return nullptr; } +void on_media_session_set_position_state(double duration, double position, + double playback_rate) { + return sServo->Delegate().OnServoMediaSessionPosition(duration, position, + playback_rate); +} + void on_media_session_metadata(const char *title, const char *album, const char *artist) { return sServo->Delegate().OnServoMediaSessionMetadata( @@ -185,9 +191,18 @@ Servo::Servo(std::optional<hstring> initUrl, hstring args, GLsizei width, std::vector<capi::CPref> cprefs; + // Ensure few things stay in memories long enough as we send raw + // pointers to Rust. + std::vector<std::unique_ptr<char *>> memChar; + std::vector<std::unique_ptr<bool>> memBool; + std::vector<std::unique_ptr<int64_t>> memInt; + std::vector<std::unique_ptr<double>> memDouble; + for (auto pref : prefs.Values()) { - auto key = *hstring2char(pref.Key()); + auto charkey = hstring2char(pref.Key()); + auto key = *charkey.get(); + memChar.push_back(std::move(charkey)); auto value = pref.Value(); auto type = value.as<Windows::Foundation::IPropertyValue>().Type(); @@ -197,20 +212,24 @@ Servo::Servo(std::optional<hstring> initUrl, hstring args, GLsizei width, cpref.value = NULL; if (type == Windows::Foundation::PropertyType::Boolean) { cpref.pref_type = capi::CPrefType::Bool; - auto val = unbox_value<bool>(value); - cpref.value = &val; + auto val = std::make_unique<bool>(unbox_value<bool>(value)); + cpref.value = val.get(); + memBool.push_back(std::move(val)); } else if (type == Windows::Foundation::PropertyType::String) { cpref.pref_type = capi::CPrefType::Str; - auto val = unbox_value<hstring>(value); - cpref.value = *hstring2char(val); + auto val = hstring2char(unbox_value<hstring>(value)); + cpref.value = *val.get(); + memChar.push_back(std::move(val)); } else if (type == Windows::Foundation::PropertyType::Int64) { cpref.pref_type = capi::CPrefType::Int; - auto val = unbox_value<int64_t>(value); - cpref.value = &val; + auto val = std::make_unique<int64_t>(unbox_value<int64_t>(value)); + cpref.value = val.get(); + memInt.push_back(std::move(val)); } else if (type == Windows::Foundation::PropertyType::Double) { cpref.pref_type = capi::CPrefType::Float; - auto val = unbox_value<double>(value); - cpref.value = &val; + auto val = std::make_unique<double>(unbox_value<double>(value)); + cpref.value = val.get(); + memDouble.push_back(std::move(val)); } else if (type == Windows::Foundation::PropertyType::Empty) { cpref.pref_type = capi::CPrefType::Missing; } else { @@ -268,6 +287,10 @@ Servo::Servo(std::optional<hstring> initUrl, hstring args, GLsizei width, sServo = this; // FIXME; auto current = ApplicationData::Current(); + auto gstLog = std::wstring(current.LocalFolder().Path()) + L"\\gst.log"; + SetEnvironmentVariable(L"GST_DEBUG_FILE", gstLog.c_str()); + // SetEnvironmentVariableA("GST_DEBUG", "4"); + auto filePath = std::wstring(current.LocalFolder().Path()) + L"\\stdout.txt"; sLogHandle = CreateFile2(filePath.c_str(), GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr); @@ -283,29 +306,32 @@ Servo::Servo(std::optional<hstring> initUrl, hstring args, GLsizei width, std::to_string(GetLastError())); } - capi::CHostCallbacks c; - c.on_load_started = &on_load_started; - c.on_load_ended = &on_load_ended; - c.on_title_changed = &on_title_changed; - c.on_url_changed = &on_url_changed; - c.on_history_changed = &on_history_changed; - c.on_animating_changed = &on_animating_changed; - c.on_shutdown_complete = &on_shutdown_complete; - c.on_allow_navigation = &on_allow_navigation; - c.on_ime_show = &on_ime_show; - c.on_ime_hide = &on_ime_hide; - c.get_clipboard_contents = &get_clipboard_contents; - c.set_clipboard_contents = &set_clipboard_contents; - c.on_media_session_metadata = &on_media_session_metadata; - c.on_media_session_playback_state_change = - &on_media_session_playback_state_change; - c.prompt_alert = &prompt_alert; - c.prompt_ok_cancel = &prompt_ok_cancel; - c.prompt_yes_no = &prompt_yes_no; - c.prompt_input = &prompt_input; - c.on_devtools_started = &on_devtools_started; - c.show_context_menu = &show_context_menu; - c.on_log_output = &on_log_output; + capi::CHostCallbacks c = capi::CHostCallbacks{ + .on_load_started = &on_load_started, + .on_load_ended = &on_load_ended, + .on_title_changed = &on_title_changed, + .on_allow_navigation = &on_allow_navigation, + .on_url_changed = &on_url_changed, + .on_history_changed = &on_history_changed, + .on_animating_changed = &on_animating_changed, + .on_shutdown_complete = &on_shutdown_complete, + .on_ime_show = &on_ime_show, + .on_ime_hide = &on_ime_hide, + .get_clipboard_contents = &get_clipboard_contents, + .set_clipboard_contents = &set_clipboard_contents, + .on_media_session_metadata = &on_media_session_metadata, + .on_media_session_playback_state_change = + &on_media_session_playback_state_change, + .on_media_session_set_position_state = + &on_media_session_set_position_state, + .prompt_alert = &prompt_alert, + .prompt_ok_cancel = &prompt_ok_cancel, + .prompt_yes_no = &prompt_yes_no, + .prompt_input = &prompt_input, + .on_devtools_started = &on_devtools_started, + .show_context_menu = &show_context_menu, + .on_log_output = &on_log_output, + }; capi::register_panic_handler(&on_panic); @@ -364,8 +390,13 @@ void Servo::GoHome() { ApplicationData::Current().LocalSettings(); auto prefs = localSettings.Containers().Lookup(L"servoUserPrefs"); auto home_pref = prefs.Values().Lookup(L"shell.homepage"); - auto home = - home_pref ? unbox_value<hstring>(home_pref) : FALLBACK_DEFAULT_URL; + auto home = home_pref ? unbox_value<hstring>(home_pref) : +#ifdef OVERRIDE_DEFAULT_URL + OVERRIDE_DEFAULT_URL +#else + FALLBACK_DEFAULT_URL +#endif + ; LoadUri(home); } diff --git a/support/hololens/ServoApp/ServoControl/Servo.h b/support/hololens/ServoApp/ServoControl/Servo.h index fb2c2f5fb22..e0731ff4876 100644 --- a/support/hololens/ServoApp/ServoControl/Servo.h +++ b/support/hololens/ServoApp/ServoControl/Servo.h @@ -124,6 +124,7 @@ public: virtual void OnServoIMEHide() = 0; virtual void OnServoDevtoolsStarted(bool, const unsigned int, hstring) = 0; virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0; + virtual void OnServoMediaSessionPosition(double, double, double) = 0; virtual void OnServoMediaSessionPlaybackStateChange(int) = 0; virtual void OnServoPromptAlert(hstring, bool) = 0; virtual void OnServoShowContextMenu(std::optional<hstring>, diff --git a/support/hololens/ServoApp/ServoControl/ServoControl.cpp b/support/hololens/ServoApp/ServoControl/ServoControl.cpp index b2ff048248f..12684efe149 100644 --- a/support/hololens/ServoApp/ServoControl/ServoControl.cpp +++ b/support/hololens/ServoApp/ServoControl/ServoControl.cpp @@ -548,6 +548,12 @@ void ServoControl::OnServoIMEShow(hstring text, int32_t x, int32_t y, }); } +void ServoControl::OnServoMediaSessionPosition(double duration, double position, + double playback_rate) { + RunOnUIThread( + [=] { mOnMediaSessionPositionEvent(duration, position, playback_rate); }); +} + void ServoControl::OnServoMediaSessionMetadata(hstring title, hstring artist, hstring album) { RunOnUIThread([=] { mOnMediaSessionMetadataEvent(title, artist, album); }); diff --git a/support/hololens/ServoApp/ServoControl/ServoControl.h b/support/hololens/ServoApp/ServoControl/ServoControl.h index be1f8090776..8775127e206 100644 --- a/support/hololens/ServoApp/ServoControl/ServoControl.h +++ b/support/hololens/ServoApp/ServoControl/ServoControl.h @@ -152,6 +152,14 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate { } winrt::event_token + OnMediaSessionPosition(MediaSessionPositionDelegate const &handler) { + return mOnMediaSessionPositionEvent.add(handler); + }; + void OnMediaSessionPosition(winrt::event_token const &token) noexcept { + mOnMediaSessionPositionEvent.remove(token); + } + + winrt::event_token OnMediaSessionMetadata(MediaSessionMetadataDelegate const &handler) { return mOnMediaSessionMetadataEvent.add(handler); }; @@ -187,6 +195,7 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate { virtual void OnServoMediaSessionMetadata(winrt::hstring, winrt::hstring, winrt::hstring); virtual void OnServoMediaSessionPlaybackStateChange(int); + virtual void OnServoMediaSessionPosition(double, double, double); virtual void OnServoPromptAlert(winrt::hstring, bool); virtual void OnServoShowContextMenu(std::optional<winrt::hstring>, std::vector<winrt::hstring>); @@ -210,6 +219,7 @@ private: winrt::event<EventDelegate> mOnCaptureGesturesStartedEvent; winrt::event<EventDelegate> mOnCaptureGesturesEndedEvent; winrt::event<MediaSessionMetadataDelegate> mOnMediaSessionMetadataEvent; + winrt::event<MediaSessionPositionDelegate> mOnMediaSessionPositionEvent; winrt::event<Windows::Foundation::EventHandler<int>> mOnMediaSessionPlaybackStateChangeEvent; diff --git a/support/hololens/ServoApp/ServoControl/ServoControl.idl b/support/hololens/ServoApp/ServoControl/ServoControl.idl index fe83d535322..72f45354095 100644 --- a/support/hololens/ServoApp/ServoControl/ServoControl.idl +++ b/support/hololens/ServoApp/ServoControl/ServoControl.idl @@ -3,6 +3,7 @@ namespace ServoApp { delegate void EventDelegate(); delegate void HistoryChangedDelegate(Boolean back, Boolean forward); delegate void MediaSessionMetadataDelegate(String title, String artist, String album); + delegate void MediaSessionPositionDelegate(Double duration, Double position, Double rate); delegate void DevtoolsStatusChangedDelegate(DevtoolsStatus status, UInt32 port, String token); enum DevtoolsStatus { @@ -42,6 +43,7 @@ namespace ServoApp { event Windows.Foundation.EventHandler<String> OnServoPanic; event Windows.Foundation.EventHandler<String> OnURLChanged; event MediaSessionMetadataDelegate OnMediaSessionMetadata; + event MediaSessionPositionDelegate OnMediaSessionPosition; event Windows.Foundation.EventHandler<int> OnMediaSessionPlaybackStateChange; Windows.Foundation.Collections.IVector<Pref> Preferences { get; }; Pref GetPref(String key); diff --git a/support/hololens/ServoApp/pch.h b/support/hololens/ServoApp/pch.h index 66759332c62..4ca8b52c4ba 100644 --- a/support/hololens/ServoApp/pch.h +++ b/support/hololens/ServoApp/pch.h @@ -31,6 +31,7 @@ #include <winrt/Windows.ApplicationModel.Activation.h> #include <winrt/Windows.ApplicationModel.Core.h> #include <winrt/Windows.ApplicationModel.Resources.h> +#include <winrt/Windows.ApplicationModel.Resources.Core.h> #include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.Metadata.h> diff --git a/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini b/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini index 76b44d9e9cf..3605e8f3fc9 100644 --- a/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini +++ b/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini @@ -4,7 +4,7 @@ expected: TIMEOUT [Opening a blob URL in a new window immediately before revoking it works.] - expected: FAIL + expected: TIMEOUT [Fetching a blob URL immediately before revoking it works in an iframe.] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-flexbox/position-absolute-015.html.ini b/tests/wpt/metadata-layout-2020/css/css-flexbox/position-absolute-015.html.ini new file mode 100644 index 00000000000..67ea4cdfde0 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-flexbox/position-absolute-015.html.ini @@ -0,0 +1,4 @@ +[position-absolute-015.html] + [#abspos 1] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini deleted file mode 100644 index 4c79907309b..00000000000 --- a/tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[CaretPosition-001.html] - [Element at (400, 100)] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/fetch/content-type/response.window.js.ini b/tests/wpt/metadata-layout-2020/fetch/content-type/response.window.js.ini index 56752f6df92..5cbc5f156f4 100644 --- a/tests/wpt/metadata-layout-2020/fetch/content-type/response.window.js.ini +++ b/tests/wpt/metadata-layout-2020/fetch/content-type/response.window.js.ini @@ -315,24 +315,18 @@ [<iframe>: separate response Content-Type: text/plain */*;charset=gbk] expected: FAIL - [<iframe>: combined response Content-Type: text/html;" text/plain] - expected: FAIL - [<iframe>: separate response Content-Type: text/html;" text/plain] expected: FAIL - [<iframe>: separate response Content-Type: text/html */*;charset=gbk] + [<iframe>: combined response Content-Type: text/html;" \\" text/plain] expected: FAIL - [<iframe>: separate response Content-Type: text/html */*] + [<iframe>: combined response Content-Type: */* text/html] expected: FAIL - [<iframe>: separate response Content-Type: text/plain */*] + [<iframe>: separate response Content-Type: text/html */*] expected: FAIL [<iframe>: combined response Content-Type: text/html */*;charset=gbk] expected: FAIL - [<iframe>: separate response Content-Type: text/html;" \\" text/plain] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/fetch/content-type/script.window.js.ini b/tests/wpt/metadata-layout-2020/fetch/content-type/script.window.js.ini index d2df9b78483..258fe374697 100644 --- a/tests/wpt/metadata-layout-2020/fetch/content-type/script.window.js.ini +++ b/tests/wpt/metadata-layout-2020/fetch/content-type/script.window.js.ini @@ -56,3 +56,6 @@ [separate text/javascript x/x] expected: FAIL + [separate text/javascript ] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/fetch/nosniff/parsing-nosniff.window.js.ini b/tests/wpt/metadata-layout-2020/fetch/nosniff/parsing-nosniff.window.js.ini index 97db72e5d96..61682d248e2 100644 --- a/tests/wpt/metadata-layout-2020/fetch/nosniff/parsing-nosniff.window.js.ini +++ b/tests/wpt/metadata-layout-2020/fetch/nosniff/parsing-nosniff.window.js.ini @@ -11,9 +11,6 @@ [X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!] expected: FAIL - [Content-Type-Options%3A%20nosniff] - expected: FAIL - - [X-Content-Type-Options%3A%0D%0AX-Content-Type-Options%3A%20nosniff] + [X-Content-Type-Options%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cnosniff] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/history/the-history-interface/traverse_the_history_4.html.ini index 51f8272a6de..385376c7321 100644 --- a/tests/wpt/metadata/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini +++ b/tests/wpt/metadata-layout-2020/html/browsers/history/the-history-interface/traverse_the_history_4.html.ini @@ -1,4 +1,4 @@ -[traverse_the_history_3.html] +[traverse_the_history_4.html] [Multiple history traversals, last would be aborted] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini new file mode 100644 index 00000000000..735a9a75a2a --- /dev/null +++ b/tests/wpt/metadata-layout-2020/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini @@ -0,0 +1,2 @@ +[cross-origin-objects-on-new-window.html] + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html.ini new file mode 100644 index 00000000000..16fa2c5cfc1 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html.ini @@ -0,0 +1,4 @@ +[creating_browsing_context_test_01.html] + [first argument: absolute url] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/html/browsers/windows/embedded-opener-remove-frame.html.ini b/tests/wpt/metadata-layout-2020/html/browsers/windows/embedded-opener-remove-frame.html.ini index 8e2a63e3b7f..8e2b6af33d8 100644 --- a/tests/wpt/metadata-layout-2020/html/browsers/windows/embedded-opener-remove-frame.html.ini +++ b/tests/wpt/metadata-layout-2020/html/browsers/windows/embedded-opener-remove-frame.html.ini @@ -1,8 +1,8 @@ [embedded-opener-remove-frame.html] - expected: CRASH + expected: TIMEOUT [opener of discarded nested browsing context] expected: FAIL [opener of discarded auxiliary browsing context] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html.ini b/tests/wpt/metadata-layout-2020/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html.ini new file mode 100644 index 00000000000..ac9b670991a --- /dev/null +++ b/tests/wpt/metadata-layout-2020/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html.ini @@ -0,0 +1,2 @@ +[fieldset-flexbox.html] + expected: CRASH diff --git a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini index 09ae27d7480..5c3ac06f42a 100644 --- a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini +++ b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini @@ -171,6 +171,3 @@ [XHTML img usemap="#hash-id"] expected: FAIL - [HTML (standards) IMG usemap="no-hash-name"] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini index 42ebcc9d57e..88f4ddd0e9a 100644 --- a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini +++ b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini @@ -1,5 +1,5 @@ [iframe_sandbox_popups_escaping-1.html] - expected: TIMEOUT + expected: CRASH [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini index 4df5d7a35de..daa518bd0e3 100644 --- a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini +++ b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini @@ -1,4 +1,5 @@ [iframe_sandbox_popups_escaping-2.html] + expected: CRASH [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini index 295031c1812..338d283eb0e 100644 --- a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini +++ b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini @@ -1,5 +1,4 @@ [iframe_sandbox_popups_escaping-3.html] - expected: TIMEOUT [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] - expected: TIMEOUT + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini index d913fcbb129..3080be9afc0 100644 --- a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini +++ b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini @@ -1,5 +1,5 @@ [iframe_sandbox_popups_nonescaping-1.html] - expected: TIMEOUT + expected: CRASH [Check that popups from a sandboxed iframe do not escape the sandbox] expected: NOTRUN diff --git a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini index f4f994c5d6f..7a36937927c 100644 --- a/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini +++ b/tests/wpt/metadata-layout-2020/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini @@ -1,5 +1,4 @@ [iframe_sandbox_popups_nonescaping-3.html] - expected: TIMEOUT [Check that popups from a sandboxed iframe do not escape the sandbox] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini deleted file mode 100644 index 9f416703229..00000000000 --- a/tests/wpt/metadata-layout-2020/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[form-double-submit-3.html] - [<button> should have the same double-submit protection as <input type=submit>] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html.ini new file mode 100644 index 00000000000..5dff48bbfe8 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html.ini @@ -0,0 +1,7 @@ +[base-url-worker-importScripts.html] + [Relative URL-like from same-origin importScripts()] + expected: FAIL + + [Relative URL-like from cross-origin importScripts()] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html.ini b/tests/wpt/metadata-layout-2020/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html.ini new file mode 100644 index 00000000000..df99f59baa0 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html.ini @@ -0,0 +1,7 @@ +[base-url.sub.html] + [Relative URL-like from cross origin classic <script> without crossorigin attribute] + expected: FAIL + + [Relative URL-like from cross origin module <script>] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html.ini b/tests/wpt/metadata-layout-2020/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html.ini deleted file mode 100644 index 6cf9d13975c..00000000000 --- a/tests/wpt/metadata-layout-2020/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[iframe_005.html] - [document.write external script into iframe write back into parent] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html.ini b/tests/wpt/metadata-layout-2020/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html.ini new file mode 100644 index 00000000000..fca4d908c89 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html.ini @@ -0,0 +1,4 @@ +[module-static-import-delayed.html] + [document.write in an imported module] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini b/tests/wpt/metadata-layout-2020/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini index 450c88bd32b..5602ea31dbe 100644 --- a/tests/wpt/metadata-layout-2020/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini +++ b/tests/wpt/metadata-layout-2020/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini @@ -4,5 +4,5 @@ expected: TIMEOUT [The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/streams/readable-streams/tee.any.js.ini b/tests/wpt/metadata-layout-2020/streams/readable-streams/tee.any.js.ini new file mode 100644 index 00000000000..d7c055f3b60 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/streams/readable-streams/tee.any.js.ini @@ -0,0 +1,59 @@ +[tee.any.sharedworker.html] + expected: ERROR + +[tee.any.worker.html] + expected: TIMEOUT + [ReadableStreamTee should not pull more chunks than can fit in the branch queue] + expected: NOTRUN + + [ReadableStream teeing: canceling branch1 should finish when branch2 reads until end of stream] + expected: TIMEOUT + + [ReadableStream teeing: canceling branch1 should finish when original stream errors] + expected: NOTRUN + + [ReadableStreamTee should only pull enough to fill the emptiest queue] + expected: NOTRUN + + [ReadableStreamTee stops pulling when original stream errors while both branches are reading] + expected: NOTRUN + + [ReadableStreamTee stops pulling when original stream errors while branch 1 is reading] + expected: NOTRUN + + [ReadableStreamTee stops pulling when original stream errors while branch 2 is reading] + expected: NOTRUN + + [ReadableStreamTee should not pull when original is already errored] + expected: NOTRUN + + +[tee.any.serviceworker.html] + expected: ERROR + +[tee.any.html] + expected: TIMEOUT + [ReadableStreamTee should not pull more chunks than can fit in the branch queue] + expected: NOTRUN + + [ReadableStream teeing: canceling branch1 should finish when branch2 reads until end of stream] + expected: TIMEOUT + + [ReadableStream teeing: canceling branch1 should finish when original stream errors] + expected: NOTRUN + + [ReadableStreamTee should only pull enough to fill the emptiest queue] + expected: NOTRUN + + [ReadableStreamTee stops pulling when original stream errors while both branches are reading] + expected: NOTRUN + + [ReadableStreamTee stops pulling when original stream errors while branch 1 is reading] + expected: NOTRUN + + [ReadableStreamTee stops pulling when original stream errors while branch 2 is reading] + expected: NOTRUN + + [ReadableStreamTee should not pull when original is already errored] + expected: NOTRUN + diff --git a/tests/wpt/metadata-layout-2020/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html.ini b/tests/wpt/metadata-layout-2020/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html.ini new file mode 100644 index 00000000000..ae39ac39151 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html.ini @@ -0,0 +1,49 @@ +[exception-in-onerror.html] + [Throw in setTimeout(string): classic: listener] + expected: FAIL + + [Throw in setTimeout(function): module: listener] + expected: FAIL + + [Throw in worker initialization: classic: handler] + expected: FAIL + + [Throw in message handler: classic: listener] + expected: FAIL + + [Throw in setTimeout(function): classic: handler] + expected: FAIL + + [Throw in message handler: module: handler] + expected: FAIL + + [Throw in setTimeout(function): module: handler] + expected: FAIL + + [Throw in worker initialization: module: handler] + expected: FAIL + + [Throw in setTimeout(string): module: listener] + expected: FAIL + + [Throw in message handler: classic: handler] + expected: FAIL + + [Throw in worker initialization: classic: listener] + expected: FAIL + + [Throw in message handler: module: listener] + expected: FAIL + + [Throw in setTimeout(string): module: handler] + expected: FAIL + + [Throw in setTimeout(function): classic: listener] + expected: FAIL + + [Throw in worker initialization: module: listener] + expected: FAIL + + [Throw in setTimeout(string): classic: handler] + expected: FAIL + diff --git a/tests/wpt/metadata/FileAPI/url/url-in-tags-revoke.window.js.ini b/tests/wpt/metadata/FileAPI/url/url-in-tags-revoke.window.js.ini index d4f62ed7113..faa00f45ecf 100644 --- a/tests/wpt/metadata/FileAPI/url/url-in-tags-revoke.window.js.ini +++ b/tests/wpt/metadata/FileAPI/url/url-in-tags-revoke.window.js.ini @@ -7,7 +7,7 @@ expected: FAIL [Opening a blob URL in a new window immediately before revoking it works.] - expected: FAIL + expected: TIMEOUT [Opening a blob URL in a noopener about:blank window immediately before revoking it works.] expected: TIMEOUT diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index a06c1a2d561..ceca26c219e 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -17086,6 +17086,41 @@ null, {} ] + ], + "protocol-handler-fragment-manual.https.html": [ + "15617865691878af2069dfbbf4d397bf3a442600", + [ + null, + {} + ] + ], + "protocol-handler-fragment-nosw-manual.https.html": [ + "be3a6be6665246ccffa1d60f0e11f376b3280cb5", + [ + null, + {} + ] + ], + "protocol-handler-path-manual.https.html": [ + "085c5723ec412d027ef0ccca13855839477ffe70", + [ + null, + {} + ] + ], + "protocol-handler-query-manual.https.html": [ + "8ce65a5bad8826326dc4b5c85c6476174bbd5954", + [ + null, + {} + ] + ], + "protocol-handler-query-nosw-manual.https.html": [ + "9b4473fb8952ba93c79d17b71725b97c6bf2b208", + [ + null, + {} + ] ] } }, @@ -124256,6 +124291,32 @@ {} ] ], + "forced-break-at-fragmentainer-start-000.html": [ + "d5796ba641b524b6379c1ab9f37dad6408bc5a10", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square-only.html", + "==" + ] + ], + {} + ] + ], + "forced-break-at-fragmentainer-start-001.html": [ + "b3ad80ca9a0dfac7574362e6ff8528ee0c39e8e9", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "form-control.html": [ "8ef6eedae252f36de3bfeef8c3a6db23bf6f3d89", [ @@ -129834,6 +129895,19 @@ ], {} ] + ], + "content-visibility-074.html": [ + "ff6381ce3d0950c0b3c3822171c2b81d851ded15", + [ + null, + [ + [ + "/css/css-contain/content-visibility/content-visibility-074-ref.html", + "==" + ] + ], + {} + ] ] }, "counter-scoping-001.html": [ @@ -144074,6 +144148,136 @@ {} ] ], + "grid-item-no-aspect-ratio-stretch-1.html": [ + "1b5ae0a6ad80610515ed53f5ea34e812fe040d27", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-10.html": [ + "6d8d7e848c24d6da176334b56f6b8af03341d374", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-2.html": [ + "5371e560a5f80eae466485ccd89486c33d741190", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-3.html": [ + "e2b05bd21664eaa61e4ec3a2abb74f431c8fdc1c", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-4.html": [ + "1cf2849dad3d067b7d097ab76a775e213e836d10", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-5.html": [ + "3000e9d97a1002b41753ca651a0313e090f42bf8", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-6.html": [ + "655040eb446497916470875c7312bfa81d343148", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-7.html": [ + "29dec490b79ac5ee7c21af8013c0917b0642221b", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-8.html": [ + "933ddd0504efe9a3202d3ded7f661b5cacd1a2c4", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html", + "==" + ] + ], + {} + ] + ], + "grid-item-no-aspect-ratio-stretch-9.html": [ + "367adae9eb74d5975170ec94893ae84ad1046f0d", + [ + null, + [ + [ + "/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html", + "==" + ] + ], + {} + ] + ], "grid-item-self-baseline-001.html": [ "ccc50c764975906b59f54aa1205a4513149e85f2", [ @@ -155386,6 +155590,19 @@ {} ] ], + "increase-prev-sibling-height.html": [ + "109fe89952e923222071fea33ed9f92b69cf323f", + [ + null, + [ + [ + "/css/css-multicol/increase-prev-sibling-height-ref.html", + "==" + ] + ], + {} + ] + ], "inline-block-and-column-span-all.html": [ "c968a0875cd7cc41c3c1dd83abee425ba0e639bb", [ @@ -159957,6 +160174,19 @@ {} ] ], + "paint-function-this-value.https.html": [ + "216913899a931e8144d8a647eccb8ba01037f737", + [ + null, + [ + [ + "/css/css-paint-api/parse-input-arguments-ref.html", + "==" + ] + ], + {} + ] + ], "paint2d-composite.https.html": [ "d654ba49aad6048f88e6ddb6750554dc22b860fb", [ @@ -248170,7 +248400,7 @@ [] ], ".taskcluster.yml": [ - "526fc13212e9b2ac9abfb54fcdcddea63a63c80a", + "b1a8162db4b21c594b3060ce6109d98bf6f98d31", [] ], ".well-known": { @@ -270664,6 +270894,10 @@ "6d1eedb1fcbfda2bf27f74af1b34763adc62d599", [] ], + "not-embeddable-frame.py": [ + "50f9c83cf5c2343535f36348459ab8144dbe8cd7", + [] + ], "redirect-throw-function.sub.py": [ "1bc89abf7176bbada8f0c8c35fd699f52bb0e8a9", [] @@ -270710,6 +270944,10 @@ }, "sandbox": { "support": { + "post-origin-on-load-worker.js": [ + "21ce5748ab8b1edbfd04c8f77a3fba54739a73d5", + [] + ], "sandboxed-data-iframe.sub.html": [ "fafd4dc7707ab0d26ed2c67f34c26920649795f8", [] @@ -293092,6 +293330,10 @@ "b2232a4a72546b12a05f4ab84d41ef85c20959dc", [] ], + "content-visibility-074-ref.html": [ + "82a6c263fa906d70f76dc78fcd6d7b6c3b461b86", + [] + ], "inline-container-with-child-ref.html": [ "af51cbd5db8f3eff56411345860548e95fe4d354", [] @@ -302430,6 +302672,38 @@ "d167a5f1542b5a71b51ce06b806a2f9425d7f6ab", [] ], + "grid-item-no-aspect-ratio-stretch-1-ref.html": [ + "6a7862c9acb626a72fda6138fdabd05a37e865ac", + [] + ], + "grid-item-no-aspect-ratio-stretch-2-ref.html": [ + "69d59e85a3c7134064f516d9e1a32ccb00dc1960", + [] + ], + "grid-item-no-aspect-ratio-stretch-3-ref.html": [ + "7ee8a2d6bdccc5f50b9f1444cb92a23febe9f81d", + [] + ], + "grid-item-no-aspect-ratio-stretch-4-ref.html": [ + "9edb8aa76f8fd40302a8dcac27db19b51f2c56b0", + [] + ], + "grid-item-no-aspect-ratio-stretch-5-ref.html": [ + "6a3446534a1a5e42952f521c9b1634284c4e594b", + [] + ], + "grid-item-no-aspect-ratio-stretch-6-ref.html": [ + "33f472e71b853651285ff7248226ed4be6c1a603", + [] + ], + "grid-item-no-aspect-ratio-stretch-7-ref.html": [ + "0d60375f560f3a2d379bd90eac4a062e0a7f420a", + [] + ], + "grid-item-no-aspect-ratio-stretch-8-ref.html": [ + "09dd27321ab5790927104ccadb4d002dba4f67a7", + [] + ], "grid-item-self-baseline-001-ref.html": [ "6eadf9a02a55822a54fa44715d80bece93997b0f", [] @@ -304593,6 +304867,10 @@ "7c33b48f5e6077ad87a230d8ca54976e55785445", [] ], + "increase-prev-sibling-height-ref.html": [ + "963f584f17d502f6d693dfe8d662ec2b3978f4ff", + [] + ], "multicol-block-no-clip-001-ref.xht": [ "1313948514e9cd6c96ad45ff60ba9c327a0b9838", [] @@ -322644,7 +322922,7 @@ [] ], "from-local-system.md": [ - "8c71e535baa907f1ef86641ee6211adb7d0c2423", + "91823444d5d4813afb020d00b2fca188f4a22f21", [] ], "from-web.md": [ @@ -322714,7 +322992,7 @@ [] ], "index.md": [ - "90a67c21fce2d1546544c0540d3158fd5bf16a26", + "c1d2fe17a31508d65a9b46da5bb93b3329648ad9", [] ], "lint-tool.md": [ @@ -322784,7 +323062,7 @@ [] ], "testharness.md": [ - "1e9772a401796da1a9a15f1fea9958843c64dc7e", + "1037eaca03c702383c0b9caab07fa98cdec4b4c0", [] ], "tools.md": [ @@ -330324,7 +330602,7 @@ [] ], "dispatcher.py": [ - "2617361b0b369bd9efd3aedd42a88f8f46906fe3", + "e77583e479f9506b3f6a5b53293ae25dc3cae8f1", [] ], "executor.html": [ @@ -337409,6 +337687,36 @@ [] ], "dynamic-import": { + "alpha": { + "import.js": [ + "b2ac52df2a18d4bdcf49310352c2bafbeb0269c4", + [] + ], + "worker-importScripts.sub.js": [ + "904d32f9bfd7110c1cb1c7104f54118c86aba30e", + [] + ] + }, + "beta": { + "import.js": [ + "7de1c68182571b80afabd79661f3fed025a2a34f", + [] + ], + "redirect.py": [ + "f2fd1ebd51d4ad5f4ef0582510600eb3731fd2c7", + [] + ] + }, + "gamma": { + "base-url.sub.js": [ + "ec7784983d1182831d3ab910bca7eb54322eec62", + [] + ], + "import.js": [ + "435c1369be2f7a214b003ee8efa734ba8478cc7b", + [] + ] + }, "propagate-nonce-external.js": [ "3b97d2f40e914af5d22ecc338913dcb0591927ba", [] @@ -338885,6 +339193,24 @@ [] ] }, + "system-state-and-capabilities": { + "the-navigator-object": { + "resources": { + "handler-sw.js": [ + "5fd915d17f98d8fa9c3c81451f46d71bc6cd024e", + [] + ], + "handler-tools.js": [ + "073287265cfe5f5b219ad6f61ffed89556a1e5cb", + [] + ], + "handler.html": [ + "552e5417842e67bb5b917268e0250a951614267c", + [] + ] + } + } + }, "the-windoworworkerglobalscope-mixin": { "README.md": [ "10ae3e5f03601a67e247f771cf73fdfddfdda12f", @@ -340437,7 +340763,7 @@ [] ], "web-share.idl": [ - "c29a29d0b4ede94153a75860344ab4824e91b6d5", + "b66bcd81fe9c951c2e70a58596380353b7046d97", [] ], "webaudio.idl": [ @@ -342776,7 +343102,7 @@ [] ], "child-frame-script.mjs": [ - "2c6760a3e3707f06b5f3f904eec5e0aa21ec9586", + "783e36092daae6a3998a2701d110add2f55c3a0f", [] ], "child-frame-script.mjs.headers": [ @@ -342784,7 +343110,7 @@ [] ], "helpers.mjs": [ - "e8425c240b5e5821bc16708044863f73bf5f4697", + "73f9406b476bb4afa708d043fb8bb68e6618d946", [] ], "helpers.mjs.headers": [ @@ -350528,11 +350854,11 @@ [] ], "run_tc.py": [ - "a5a8ca7db29ab484fe397475f9b5cfc412d6517f", + "3a3b0d8090baf7ab60f364dfd6908b6be7011f8e", [] ], "taskcluster-run.py": [ - "61d05689287a33c3470089e929d0c8871b6a46ef", + "245ee7fa5925e20df7eead0c86993494475a55e4", [] ], "tc": { @@ -350545,13 +350871,17 @@ [] ], "decision.py": [ - "0820a6798b5b233581eb561a973cb97ff2af44e1", + "f52f5b093f0f41632613578f449188f82bc03ab9", [] ], "download.py": [ "359ec33405048c134ddcfac7a564ea9a17ac1b14", [] ], + "github_checks_output.py": [ + "16d399002984e0b9daf8a0239b49bedbea50914b", + [] + ], "sink_task.py": [ "ba76d27640c86197736d7edc9e8ed86fe8ec4bd0", [] @@ -350562,7 +350892,7 @@ ], "tasks": { "test.yml": [ - "dd14fc71b915e90f0625dc1120aed3c86a787e53", + "b1534c9d230d0b443ba2ecf83aacc58598221ae8", [] ] }, @@ -350594,7 +350924,7 @@ [] ], "test_valid.py": [ - "88ffdb2951133474884c89c2dfa0a2e029c9ca19", + "472f83f86a0fbb154502f48dfb5cfcc09cf8459f", [] ] } @@ -351154,7 +351484,7 @@ [] ], "commands.json": [ - "a5457b55a3308d47fac178aedd6282d93b36120f", + "ed1d72e60ec33c3b550308bc55eacbf96e0be4c4", [] ], "serve.py": [ @@ -351162,7 +351492,7 @@ [] ], "test_functional.py": [ - "894479d111c90dad925c683cb0e93ac13fc68619", + "5a918c2ee473e55886dfc70878d3df17b728e6f1", [] ], "test_serve.py": [ @@ -356950,7 +357280,7 @@ [] ], "commands.json": [ - "a47ab40d72847d5f536e0ac2e02eb3fe0fea233a", + "51f66d0decb09d7a17493ca72a5f13f829d04f3b", [] ], "create.py": [ @@ -357019,7 +357349,7 @@ [] ], "test_wpt.py": [ - "b5ae79d13112bec1a4f264686338204ba3d62abc", + "0b6aff42d57d7dc8bb4ba936c86b913f58cba791", [] ] }, @@ -357040,7 +357370,7 @@ [] ], "wpt.py": [ - "395db0394da67caa8bdc1ad9e689e583175192b0", + "efa985c7cc1740eb0fd6e5a80f9e89e24cf75d37", [] ] }, @@ -357563,7 +357893,7 @@ [] ], "stability.py": [ - "3f8989729fde0ee45cf783bc698307e38c844d90", + "605b40281a1b559e47d6aa795bb5cbd4b8715177", [] ], "testdriver-extra.js": [ @@ -357697,7 +358027,7 @@ [] ], "wptcommandline.py": [ - "f322d77fbddefabc8fd187ebbc8cadaea8ea1086", + "ea586825616132b410127124ad2152ae88554cde", [] ], "wptlogging.py": [ @@ -358038,7 +358368,7 @@ [] ], "test_stash.py": [ - "1c4c9ad933a37746623a0195f553ad8cfe70688f", + "0b960c1e9f8609dcbd82dbb333434fe0d2700185", [] ] }, @@ -359954,7 +360284,7 @@ [] ], "keys.py": [ - "a62318814f3617bab6376f960c8ffc82d22b8d9b", + "2d38e1dff8760ff6d29b6e06d0ba3ac510471e1e", [] ], "mouse.py": [ @@ -363424,7 +363754,7 @@ }, "onerror": { "exception-in-onerror.js": [ - "2bf4124cb74b5fa34302ffddce7e44b953d0d472", + "61a95dda51f7e74c3fdff46fcfd09d7f2305dbe5", [] ], "handled.js": [ @@ -384835,6 +385165,20 @@ } ] ], + "report-frame-ancestors-with-x-frame-options.sub.html": [ + "0c58a5efd54ceda6ad5f906b02862976c40f65cd", + [ + null, + {} + ] + ], + "report-frame-ancestors.sub.html": [ + "cd7bbcb9731009651c1646907e55e137e9d5fdb5", + [ + null, + {} + ] + ], "report-multiple-violations-01.html": [ "7a92f1b955639eb26bfd4a737ee1a930fdec6592", [ @@ -385004,6 +385348,13 @@ {} ] ], + "meta-element.sub.html": [ + "cd8da8f14c4ad775b9034131315867dec08cd2e8", + [ + null, + {} + ] + ], "sandbox-allow-scripts-subframe.sub.html": [ "1d6db3cde71cddb66f5536720a632b537465cbff", [ @@ -393503,6 +393854,13 @@ {} ] ], + "position-absolute-015.html": [ + "1c0f3bc4c2888a12d20e2ab704f835f359366b81", + [ + null, + {} + ] + ], "position-relative-percentage-top-001.html": [ "3ec47c1d42d69330c616a87f0b22208f8d6196a6", [ @@ -393511,7 +393869,7 @@ ] ], "quirks-auto-block-size-with-percentage-item.html": [ - "966f39f173952f01268dcd26a9a5892bf4a689a9", + "f5c3d7c1818366c403896bc1da44af11ed4e0f5b", [ null, {} @@ -415404,7 +415762,7 @@ }, "events": { "AddEventListenerOptions-once.html": [ - "ae750702c79d0e2f493537ade1c78558987f536e", + "361c838b736258642c0bf5f26f243544edf2224b", [ null, {} @@ -417044,6 +417402,13 @@ {} ] ], + "MutationObserver-callback-arguments.html": [ + "d64758cb4fa00d25ad3dafe576a0098c902922b7", + [ + null, + {} + ] + ], "MutationObserver-characterData.html": [ "addaef03da1ac64ce9e6a6b1c5d244933b2c7f7b", [ @@ -457830,10 +458195,12 @@ ] ], "reporting-observer.html": [ - "4d1eda941ae50ce4482c692dafe749ef9e455ac6", + "decad097b6d29acabb6252497f68658002c3478e", [ null, - {} + { + "timeout": "long" + } ] ] }, @@ -457945,7 +458312,7 @@ ] ], "reporting-redirect-with-same-origin-allow-popups.https.html": [ - "7dba76c4ef5434d49d7800da14eb980f79728f44", + "f807561f17488c1b05ea779f6b0aa64c7a10d197", [ null, { @@ -460625,7 +460992,7 @@ ] ], "fieldset-flexbox.html": [ - "07c73554e59ccb576e590192e923de6b7100e1ae", + "9e1c9ed152fb6597891b310f6a492d57ae136441", [ null, {} @@ -461271,7 +461638,7 @@ "pragma-directives": { "attr-meta-http-equiv-refresh": { "allow-scripts-flag-changing-1.html": [ - "3bf8cfb6cc8949ec30f3491660f01fb452307ccb", + "196f6d0409a9cc97104f6c15fc7855fb549bfebe", [ null, {} @@ -469554,6 +469921,29 @@ ] ], "dynamic-import": { + "alpha": { + "base-url-worker-importScripts.html": [ + "817cf6d5ddfdc186454de0e3a672a865e2ced416", + [ + null, + {} + ] + ], + "base-url-worker.sub.html": [ + "a12204281cc43e64c7f1f4f7cdaf37958567b516", + [ + null, + {} + ] + ], + "base-url.sub.html": [ + "f7d4927a104c78e2f3d41e0899c7deadb5ca3d1c", + [ + null, + {} + ] + ] + }, "dynamic-imports-credentials.sub.html": [ "b939a3ef1639db8027519ac004cd3197e254c0b9", [ @@ -476172,6 +476562,24 @@ {} ] ], + "input-events-get-target-ranges-backspace.tentative.html": [ + "cf512269b07e0177214f7d9059150a77b85eb225", + [ + null, + { + "testdriver": true + } + ] + ], + "input-events-get-target-ranges-forwarddelete.tentative.html": [ + "3780324cf92bfa0c712045d89e43e903e3a1c9c6", + [ + null, + { + "testdriver": true + } + ] + ], "input-events-typing.html": [ "a894beea9bd381313b999e45abe0b4b8f61c804b", [ @@ -476391,6 +476799,13 @@ {} ] ], + "observer-callback-arguments.html": [ + "6e816969d00641af4da1c86d7b7bafc4a3ffbc3b", + [ + null, + {} + ] + ], "observer-exceptions.html": [ "126790f290d9480dcda7eed6e1b2989ffcc6a70f", [ @@ -477048,7 +477463,7 @@ ] ], "invisible-images-composited-1.html": [ - "495645ab41eccb80464a2ceb7de83a2136ecfbf7", + "7723d2f2bea5b622395e09ae1bd7fd4bb6cf87bc", [ null, {} @@ -477189,6 +477604,20 @@ {} ] ], + "child-shift-with-parent-overflow-hidden.html": [ + "d69e55a51b8088ca0a94f3975155646d21792d88", + [ + null, + {} + ] + ], + "child-shift-with-parent.html": [ + "7271af6d4a04fbbafebe2b20b372f6f41b31b3a9", + [ + null, + {} + ] + ], "clip-negative-bottom-margin.html": [ "2c329d9fcd66d86bf8c350c2ee419e28ef6596f4", [ @@ -477227,14 +477656,14 @@ ] ], "inline-flow-shift-vertical-rl.html": [ - "06bc34c37dd0559bfe6bedcc956ec2e2734c14b3", + "a89cad145b277bb7a92b30781a63de18fb76c42f", [ null, {} ] ], "inline-flow-shift.html": [ - "39550da6588eeb7aca7001ab592ee0177d26ca56", + "0385f29c2f9796818daf38ba5cad2aec7ba4b550", [ null, {} @@ -487051,7 +487480,7 @@ "pointerevents": { "compat": { "pointerevent_compat-mouse-events-when-removing-nodes.html": [ - "4388b2db308d70a24b70d94ed2441a468c2267ee", + "88d03b7965efc241893b8fbfc4c8f2e2b0967bf5", [ null, { @@ -487060,7 +487489,7 @@ ] ], "pointerevent_mouse-on-object.html": [ - "d8912a2652b405e6a5882b21f657308142c5e77b", + "fa0e97ff30c234c3311eadacef094e3c0b385acf", [ null, { @@ -487077,6 +487506,15 @@ } ] ], + "pointerevent_mouse-pointer-preventdefault.html": [ + "a727325a5b1d0e7d4f02899a98cfc5d457e106a4", + [ + null, + { + "testdriver": true + } + ] + ], "pointerevent_mouse-pointer-updown-events.html": [ "a5433aab535f075b58844481a44588cb578a6bf0", [ @@ -507954,7 +508392,7 @@ ] ], "tee.any.js": [ - "b6a95368aa5e64df6ca9e15c034a1b4288883622", + "55d4532ff83929b53db7791be52de8a98743d0f8", [ null, { @@ -512910,7 +513348,7 @@ ] ], "SVGGeometryElement.getPointAtLength-03.svg": [ - "61737efb7da187cb8bcb18c3eff49e5e204cf1c0", + "8d3f7e36123200a55f6d0232d4753d0b29ce1854", [ null, {} @@ -516250,6 +516688,17 @@ {} ] ], + "case-sensitivity.any.js": [ + "1c0b0dcac361fe02eefc1dd5035955d10dc37151", + [ + "user-timing/case-sensitivity.any.html", + {} + ], + [ + "user-timing/case-sensitivity.any.worker.html", + {} + ] + ], "clearMarks.html": [ "92c60a3bbb856bd05bf13f12bde09dac7eefb6e6", [ @@ -531528,6 +531977,13 @@ {} ] ], + "render_state_update.https.html": [ + "2f28d442ec9889ae9cf6d54b70e1c4c64b862a0f", + [ + null, + {} + ] + ], "render_state_vertical_fov_immersive.https.html": [ "485438cabf6a633df42a0f94c04922e7274013e1", [ @@ -533524,7 +533980,7 @@ }, "onerror": { "exception-in-onerror.html": [ - "840433d3d894b1a298bbd5bbe2ea14f9fdd81ef4", + "d8aa02a01b8c841b2db575a1b7a77388d66d3dda", [ null, {} @@ -561705,7 +562161,7 @@ ] ], "response.py": [ - "dc1328998810faacf74f1b94b54ed59f48e84e4d", + "cbe9e7d7fb14680f027d6fdc15d409ac19a7320c", [ null, {} diff --git a/tests/wpt/metadata/css/css-flexbox/position-absolute-015.html.ini b/tests/wpt/metadata/css/css-flexbox/position-absolute-015.html.ini new file mode 100644 index 00000000000..67ea4cdfde0 --- /dev/null +++ b/tests/wpt/metadata/css/css-flexbox/position-absolute-015.html.ini @@ -0,0 +1,4 @@ +[position-absolute-015.html] + [#abspos 1] + expected: FAIL + diff --git a/tests/wpt/metadata/css/cssom-view/CaretPosition-001.html.ini b/tests/wpt/metadata/css/cssom-view/CaretPosition-001.html.ini deleted file mode 100644 index 4c79907309b..00000000000 --- a/tests/wpt/metadata/css/cssom-view/CaretPosition-001.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[CaretPosition-001.html] - [Element at (400, 100)] - expected: FAIL - diff --git a/tests/wpt/metadata/fetch/content-type/response.window.js.ini b/tests/wpt/metadata/fetch/content-type/response.window.js.ini index 1c98035f7f7..8556333fa92 100644 --- a/tests/wpt/metadata/fetch/content-type/response.window.js.ini +++ b/tests/wpt/metadata/fetch/content-type/response.window.js.ini @@ -315,24 +315,18 @@ [<iframe>: separate response Content-Type: text/plain */*;charset=gbk] expected: FAIL - [<iframe>: combined response Content-Type: text/html;" text/plain] - expected: FAIL - [<iframe>: separate response Content-Type: text/html;" text/plain] expected: FAIL - [<iframe>: separate response Content-Type: text/html */*;charset=gbk] + [<iframe>: combined response Content-Type: text/html;" \\" text/plain] expected: FAIL - [<iframe>: separate response Content-Type: text/html */*] + [<iframe>: combined response Content-Type: */* text/html] expected: FAIL - [<iframe>: separate response Content-Type: text/plain */*] + [<iframe>: separate response Content-Type: text/html */*] expected: FAIL [<iframe>: combined response Content-Type: text/html */*;charset=gbk] expected: FAIL - [<iframe>: separate response Content-Type: text/html;" \\" text/plain] - expected: FAIL - diff --git a/tests/wpt/metadata/fetch/content-type/script.window.js.ini b/tests/wpt/metadata/fetch/content-type/script.window.js.ini index d2df9b78483..258fe374697 100644 --- a/tests/wpt/metadata/fetch/content-type/script.window.js.ini +++ b/tests/wpt/metadata/fetch/content-type/script.window.js.ini @@ -56,3 +56,6 @@ [separate text/javascript x/x] expected: FAIL + [separate text/javascript ] + expected: FAIL + diff --git a/tests/wpt/metadata/fetch/nosniff/parsing-nosniff.window.js.ini b/tests/wpt/metadata/fetch/nosniff/parsing-nosniff.window.js.ini index 97db72e5d96..61682d248e2 100644 --- a/tests/wpt/metadata/fetch/nosniff/parsing-nosniff.window.js.ini +++ b/tests/wpt/metadata/fetch/nosniff/parsing-nosniff.window.js.ini @@ -11,9 +11,6 @@ [X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!] expected: FAIL - [Content-Type-Options%3A%20nosniff] - expected: FAIL - - [X-Content-Type-Options%3A%0D%0AX-Content-Type-Options%3A%20nosniff] + [X-Content-Type-Options%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cnosniff] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini b/tests/wpt/metadata/html/browsers/history/the-history-interface/traverse_the_history_4.html.ini index 51f8272a6de..385376c7321 100644 --- a/tests/wpt/metadata-layout-2020/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-history-interface/traverse_the_history_4.html.ini @@ -1,4 +1,4 @@ -[traverse_the_history_3.html] +[traverse_the_history_4.html] [Multiple history traversals, last would be aborted] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini b/tests/wpt/metadata/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini new file mode 100644 index 00000000000..735a9a75a2a --- /dev/null +++ b/tests/wpt/metadata/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini @@ -0,0 +1,2 @@ +[cross-origin-objects-on-new-window.html] + expected: TIMEOUT diff --git a/tests/wpt/metadata/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html.ini b/tests/wpt/metadata/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html.ini new file mode 100644 index 00000000000..16fa2c5cfc1 --- /dev/null +++ b/tests/wpt/metadata/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html.ini @@ -0,0 +1,4 @@ +[creating_browsing_context_test_01.html] + [first argument: absolute url] + expected: FAIL + diff --git a/tests/wpt/metadata/html/browsers/windows/embedded-opener-remove-frame.html.ini b/tests/wpt/metadata/html/browsers/windows/embedded-opener-remove-frame.html.ini index 2532dceabac..fd369192a02 100644 --- a/tests/wpt/metadata/html/browsers/windows/embedded-opener-remove-frame.html.ini +++ b/tests/wpt/metadata/html/browsers/windows/embedded-opener-remove-frame.html.ini @@ -1,5 +1,5 @@ [embedded-opener-remove-frame.html] - expected: CRASH + expected: TIMEOUT [opener and "removed" embedded documents] expected: FAIL @@ -7,5 +7,5 @@ expected: FAIL [opener of discarded auxiliary browsing context] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html.ini b/tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html.ini new file mode 100644 index 00000000000..ac9b670991a --- /dev/null +++ b/tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html.ini @@ -0,0 +1,2 @@ +[fieldset-flexbox.html] + expected: CRASH diff --git a/tests/wpt/metadata/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini index 98163335e40..1de3f8d68b2 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini @@ -172,6 +172,3 @@ [XHTML img usemap="http://example.org/#garbage-before-hash-id"] expected: FAIL - [HTML (standards) IMG usemap="no-hash-name"] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini index fc37df7e3fa..2a166bb97b7 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html.ini @@ -1,6 +1,6 @@ [iframe_sandbox_popups_escaping-1.html] type: testharness - expected: TIMEOUT + expected: CRASH [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] expected: TIMEOUT diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini index 4df5d7a35de..daa518bd0e3 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html.ini @@ -1,4 +1,5 @@ [iframe_sandbox_popups_escaping-2.html] + expected: CRASH [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini index 5f60c78e73c..f6a7aca3306 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini @@ -1,6 +1,5 @@ [iframe_sandbox_popups_escaping-3.html] type: testharness - expected: TIMEOUT [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] - expected: TIMEOUT + expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini index 9df1ac56f2a..963d4cd20ef 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini @@ -1,6 +1,6 @@ [iframe_sandbox_popups_nonescaping-1.html] type: testharness - expected: TIMEOUT + expected: CRASH [Check that popups from a sandboxed iframe do not escape the sandbox] expected: NOTRUN diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini index e440b1e38c6..dc856a3d5a3 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini @@ -1,6 +1,5 @@ [iframe_sandbox_popups_nonescaping-3.html] type: testharness - expected: TIMEOUT [Check that popups from a sandboxed iframe do not escape the sandbox] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini deleted file mode 100644 index 9f416703229..00000000000 --- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[form-double-submit-3.html] - [<button> should have the same double-submit protection as <input type=submit>] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html.ini b/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html.ini new file mode 100644 index 00000000000..5dff48bbfe8 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html.ini @@ -0,0 +1,7 @@ +[base-url-worker-importScripts.html] + [Relative URL-like from same-origin importScripts()] + expected: FAIL + + [Relative URL-like from cross-origin importScripts()] + expected: FAIL + diff --git a/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html.ini b/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html.ini new file mode 100644 index 00000000000..df99f59baa0 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html.ini @@ -0,0 +1,7 @@ +[base-url.sub.html] + [Relative URL-like from cross origin classic <script> without crossorigin attribute] + expected: FAIL + + [Relative URL-like from cross origin module <script>] + expected: FAIL + diff --git a/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html.ini b/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html.ini deleted file mode 100644 index 6cf9d13975c..00000000000 --- a/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[iframe_005.html] - [document.write external script into iframe write back into parent] - expected: FAIL - diff --git a/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html.ini b/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html.ini new file mode 100644 index 00000000000..fca4d908c89 --- /dev/null +++ b/tests/wpt/metadata/html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html.ini @@ -0,0 +1,4 @@ +[module-static-import-delayed.html] + [document.write in an imported module] + expected: FAIL + diff --git a/tests/wpt/metadata/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini b/tests/wpt/metadata/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini index 01f7b72cbe7..edae5e5adcd 100644 --- a/tests/wpt/metadata/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini +++ b/tests/wpt/metadata/html/webappapis/scripting/events/compile-event-handler-settings-objects.html.ini @@ -5,5 +5,5 @@ expected: TIMEOUT [The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document] - expected: FAIL + expected: TIMEOUT diff --git a/tests/wpt/metadata/streams/readable-streams/tee.any.js.ini b/tests/wpt/metadata/streams/readable-streams/tee.any.js.ini index 75b63daffbc..953e38f599b 100644 --- a/tests/wpt/metadata/streams/readable-streams/tee.any.js.ini +++ b/tests/wpt/metadata/streams/readable-streams/tee.any.js.ini @@ -2,34 +2,58 @@ expected: ERROR [tee.any.worker.html] - expected: ERROR + expected: TIMEOUT [ReadableStreamTee should not pull more chunks than can fit in the branch queue] - expected: FAIL + expected: NOTRUN [ReadableStreamTee stops pulling when original stream errors while branch 1 is reading] - expected: FAIL + expected: NOTRUN [ReadableStreamTee stops pulling when original stream errors while both branches are reading] - expected: FAIL + expected: NOTRUN [ReadableStreamTee stops pulling when original stream errors while branch 2 is reading] - expected: FAIL + expected: NOTRUN + + [ReadableStream teeing: canceling branch1 should finish when branch2 reads until end of stream] + expected: TIMEOUT + + [ReadableStream teeing: canceling branch1 should finish when original stream errors] + expected: NOTRUN + + [ReadableStreamTee should only pull enough to fill the emptiest queue] + expected: NOTRUN + + [ReadableStreamTee should not pull when original is already errored] + expected: NOTRUN [tee.any.serviceworker.html] expected: ERROR [tee.any.html] - expected: ERROR + expected: TIMEOUT [ReadableStreamTee should not pull more chunks than can fit in the branch queue] - expected: FAIL + expected: NOTRUN [ReadableStreamTee stops pulling when original stream errors while branch 1 is reading] - expected: FAIL + expected: NOTRUN [ReadableStreamTee stops pulling when original stream errors while both branches are reading] - expected: FAIL + expected: NOTRUN [ReadableStreamTee stops pulling when original stream errors while branch 2 is reading] - expected: FAIL + expected: NOTRUN + + [ReadableStream teeing: canceling branch1 should finish when branch2 reads until end of stream] + expected: TIMEOUT + + [ReadableStream teeing: canceling branch1 should finish when original stream errors] + expected: NOTRUN + + [ReadableStreamTee should only pull enough to fill the emptiest queue] + expected: NOTRUN + + [ReadableStreamTee should not pull when original is already errored] + expected: NOTRUN diff --git a/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html.ini b/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html.ini new file mode 100644 index 00000000000..ae39ac39151 --- /dev/null +++ b/tests/wpt/metadata/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html.ini @@ -0,0 +1,49 @@ +[exception-in-onerror.html] + [Throw in setTimeout(string): classic: listener] + expected: FAIL + + [Throw in setTimeout(function): module: listener] + expected: FAIL + + [Throw in worker initialization: classic: handler] + expected: FAIL + + [Throw in message handler: classic: listener] + expected: FAIL + + [Throw in setTimeout(function): classic: handler] + expected: FAIL + + [Throw in message handler: module: handler] + expected: FAIL + + [Throw in setTimeout(function): module: handler] + expected: FAIL + + [Throw in worker initialization: module: handler] + expected: FAIL + + [Throw in setTimeout(string): module: listener] + expected: FAIL + + [Throw in message handler: classic: handler] + expected: FAIL + + [Throw in worker initialization: classic: listener] + expected: FAIL + + [Throw in message handler: module: listener] + expected: FAIL + + [Throw in setTimeout(string): module: handler] + expected: FAIL + + [Throw in setTimeout(function): classic: listener] + expected: FAIL + + [Throw in worker initialization: module: listener] + expected: FAIL + + [Throw in setTimeout(string): classic: handler] + expected: FAIL + diff --git a/tests/wpt/mozilla/meta-layout-2020/mozilla/hit_test_multiple_sc.html.ini b/tests/wpt/mozilla/meta-layout-2020/mozilla/hit_test_multiple_sc.html.ini deleted file mode 100644 index 8ed8088a59f..00000000000 --- a/tests/wpt/mozilla/meta-layout-2020/mozilla/hit_test_multiple_sc.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[hit_test_multiple_sc.html] - [Hit testing works for following stacking contexts] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index b4d358816e2..3fc5519a4cc 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -14718,6 +14718,13 @@ {} ] ], + "layerevents.html": [ + "6a0319366ec842a9b94d5897978975ebe3eb6f7f", + [ + null, + {} + ] + ], "layers.html": [ "b6749f208a8f3c5ae78fbe72cfa7364829abb127", [ diff --git a/tests/wpt/mozilla/meta/mozilla/getBoundingClientRect.html.ini b/tests/wpt/mozilla/meta/mozilla/getBoundingClientRect.html.ini new file mode 100644 index 00000000000..dcd88accd43 --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/getBoundingClientRect.html.ini @@ -0,0 +1,4 @@ +[getBoundingClientRect.html] + [getBoundingClientRect 1] + expected: FAIL + diff --git a/tests/wpt/mozilla/meta/mozilla/hit-test-background.html.ini b/tests/wpt/mozilla/meta/mozilla/hit-test-background.html.ini deleted file mode 100644 index 7b9e9d1430f..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/hit-test-background.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[hit-test-background.html] - [Hit testing backgrounds of content should report the same element as the content] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/mozilla/hit_test_multiple_sc.html.ini b/tests/wpt/mozilla/meta/mozilla/hit_test_multiple_sc.html.ini deleted file mode 100644 index 8ed8088a59f..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/hit_test_multiple_sc.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[hit_test_multiple_sc.html] - [Hit testing works for following stacking contexts] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/mozilla/hit_test_pos_fixed.html.ini b/tests/wpt/mozilla/meta/mozilla/hit_test_pos_fixed.html.ini deleted file mode 100644 index 9f5b10bd6af..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/hit_test_pos_fixed.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[hit_test_pos_fixed.html] - [Hit-test of an element with position: fixed should discard scroll offset] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/webxr/layerevents.html.ini b/tests/wpt/mozilla/meta/webxr/layerevents.html.ini new file mode 100644 index 00000000000..72e1e4fc0b2 --- /dev/null +++ b/tests/wpt/mozilla/meta/webxr/layerevents.html.ini @@ -0,0 +1 @@ +prefs: [dom.webxr.layers.enabled:true] diff --git a/tests/wpt/mozilla/tests/webxr/layerevents.html b/tests/wpt/mozilla/tests/webxr/layerevents.html new file mode 100644 index 00000000000..6a0319366ec --- /dev/null +++ b/tests/wpt/mozilla/tests/webxr/layerevents.html @@ -0,0 +1,36 @@ +<html> + <head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="./resources/webxr-util.js"></script> + </head> + <body> + <canvas id="canvas" width="640" height="480"></canvas> + + <script> + let canvas = document.getElementById("canvas"); + let gl = canvas.getContext('webgl'); + promise_test(async function() { + let mock = await navigator.xr.test.simulateDeviceConnection({ + supportedModes: ["immersive-vr"], + views: TEST_VIEWS, + viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] } + }); + let sessionPromise; + navigator.xr.test.simulateUserActivation(() => { + sessionPromise = navigator.xr.requestSession("immersive-vr"); + }); + let session = await sessionPromise; + let glLayerFactory = new XRWebGLBinding(session, gl); + let layer = new XRWebGLLayer(session, gl); + let event = new XRLayerEvent("foo", { layer: layer, bubbles: true }); + + assert_equals(event.type, "foo", "XRLeventLayer construxctor sets type"); + assert_equals(event.bubbles, true, "XRLeventLayer construxctor sets bubbles"); + assert_equals(event.cancelable, false, "XRLeventLayer construxctor sets cancelable"); + assert_equals(event.isTrusted, false, "XRLeventLayer construxctor sets trusted"); + assert_equals(event.layer, layer, "XRLeventLayer construxctor sets layer"); + }); + </script> + </body> +</html> diff --git a/tests/wpt/web-platform-tests/.taskcluster.yml b/tests/wpt/web-platform-tests/.taskcluster.yml index 526fc13212e..b1a8162db4b 100644 --- a/tests/wpt/web-platform-tests/.taskcluster.yml +++ b/tests/wpt/web-platform-tests/.taskcluster.yml @@ -7,7 +7,7 @@ tasks: run_task: $if: 'tasks_for == "github-push"' then: - $if: 'event.ref in ["refs/heads/master", "refs/heads/epochs/daily", "refs/heads/epochs/weekly", "refs/heads/triggers/chrome_stable", "refs/heads/triggers/chrome_beta", "refs/heads/triggers/chrome_dev", "refs/heads/triggers/firefox_stable", "refs/heads/triggers/firefox_beta", "refs/heads/triggers/firefox_nightly", "refs/heads/triggers/webkitgtk_minibrowser_stable", "refs/heads/triggers/webkitgtk_minibrowser_nightly", "refs/heads/triggers/servo_nightly"]' + $if: 'event.ref in ["refs/heads/master", "refs/heads/epochs/daily", "refs/heads/epochs/weekly", "refs/heads/triggers/chrome_stable", "refs/heads/triggers/chrome_beta", "refs/heads/triggers/chrome_dev", "refs/heads/triggers/chrome_nightly", "refs/heads/triggers/firefox_stable", "refs/heads/triggers/firefox_beta", "refs/heads/triggers/firefox_nightly", "refs/heads/triggers/webkitgtk_minibrowser_stable", "refs/heads/triggers/webkitgtk_minibrowser_nightly", "refs/heads/triggers/servo_nightly"]' then: true else: false else: diff --git a/tests/wpt/web-platform-tests/content-security-policy/reporting/report-frame-ancestors-with-x-frame-options.sub.html b/tests/wpt/web-platform-tests/content-security-policy/reporting/report-frame-ancestors-with-x-frame-options.sub.html new file mode 100644 index 00000000000..0c58a5efd54 --- /dev/null +++ b/tests/wpt/web-platform-tests/content-security-policy/reporting/report-frame-ancestors-with-x-frame-options.sub.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <title>Reporting works with report-only frame-ancestors even if frame is blocked by X-Frame-Options</title> +</head> +<body> + <iframe src="./support/not-embeddable-frame.py?reportID={{$id:uuid()}}&reportOnly=true&xFrameOptions=DENY"></iframe> + <script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-ancestors&reportID={{$id}}'></script> +</body> +</html> diff --git a/tests/wpt/web-platform-tests/content-security-policy/reporting/report-frame-ancestors.sub.html b/tests/wpt/web-platform-tests/content-security-policy/reporting/report-frame-ancestors.sub.html new file mode 100644 index 00000000000..cd7bbcb9731 --- /dev/null +++ b/tests/wpt/web-platform-tests/content-security-policy/reporting/report-frame-ancestors.sub.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <title>Reporting works with frame-ancestors</title> +</head> +<body> + <iframe src="./support/not-embeddable-frame.py?reportID={{$id:uuid()}}"></iframe> + <script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-ancestors&reportID={{$id}}'></script> +</body> +</html> diff --git a/tests/wpt/web-platform-tests/content-security-policy/reporting/support/not-embeddable-frame.py b/tests/wpt/web-platform-tests/content-security-policy/reporting/support/not-embeddable-frame.py new file mode 100644 index 00000000000..50f9c83cf5c --- /dev/null +++ b/tests/wpt/web-platform-tests/content-security-policy/reporting/support/not-embeddable-frame.py @@ -0,0 +1,10 @@ +def main(request, response): + headers = [] + if request.GET.first(b'xFrameOptions', None): + headers.append((b'X-Frame-Options', request.GET[b'xFrameOptions'])) + + csp_header = b'Content-Security-Policy-Report-Only' \ + if request.GET.first(b'reportOnly', None) == 'true' else b'Content-Security-Policy' + headers.append((csp_header, b"frame-ancestors 'none'; report-uri ../../support/report.py?op=put&reportID=" + request.GET[b'reportID'])) + + return headers, b'{}' diff --git a/tests/wpt/web-platform-tests/content-security-policy/sandbox/meta-element.sub.html b/tests/wpt/web-platform-tests/content-security-policy/sandbox/meta-element.sub.html new file mode 100644 index 00000000000..cd8da8f14c4 --- /dev/null +++ b/tests/wpt/web-platform-tests/content-security-policy/sandbox/meta-element.sub.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<meta content="sandbox allow-scripts" http-equiv="Content-Security-Policy"> +<body> +<iframe id="iframe"></iframe> +<script> +// According to +// https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-security-policy +// `sandbox` directives must be ignored when delivered via `<meta>`. +test(() => { + assert_equals(location.origin, "{{location[scheme]}}://{{location[host]}}"); +}, "Document shouldn't be sandboxed by <meta>"); + +// Note: sandbox directive for workers are not yet specified. +// https://github.com/w3c/webappsec-csp/issues/279 +// Anyway workers shouldn't be affected by sandbox directives in `<meta>`. +async_test(t => { + const worker = new Worker("support/post-origin-on-load-worker.js"); + worker.onerror = t.unreached_func("Worker construction failed"); + worker.onmessage = t.step_func_done(e => { + assert_equals(e.data, "{{location[scheme]}}://{{location[host]}}"); + }); +}, "Worker shouldn't be sandboxed by inheriting <meta>"); + +parent.async_test(t => { + // Although <iframe about:blank> should inherit parent's CSP, + // sandbox directives in <meta> should be ignored in the first place, + // so workers created from such <iframe>s shouldn't also be sandboxed. + const iframeDocument = document.querySelector("#iframe").contentDocument; + const script = iframeDocument.createElement("script"); + script.innerText = ` + const worker = new Worker("support/post-origin-on-load-worker.js"); + worker.onerror = () => parent.postMessage("onerror", "*"); + worker.onmessage = (e) => parent.postMessage(e.data, "*"); + `; + iframeDocument.body.appendChild(script); + + // Receive message from <iframe>. + onmessage = t.step_func_done(e => { + assert_equals(e.data, "{{location[scheme]}}://{{location[host]}}"); + }); +}, "Worker shouldn't be sandboxed when created <iframe> inheriting parent's CSP with sandbox <meta>"); +</script> +</body> diff --git a/tests/wpt/web-platform-tests/content-security-policy/sandbox/support/post-origin-on-load-worker.js b/tests/wpt/web-platform-tests/content-security-policy/sandbox/support/post-origin-on-load-worker.js new file mode 100644 index 00000000000..21ce5748ab8 --- /dev/null +++ b/tests/wpt/web-platform-tests/content-security-policy/sandbox/support/post-origin-on-load-worker.js @@ -0,0 +1 @@ +postMessage(self.origin); diff --git a/tests/wpt/web-platform-tests/css/css-break/forced-break-at-fragmentainer-start-000.html b/tests/wpt/web-platform-tests/css/css-break/forced-break-at-fragmentainer-start-000.html new file mode 100644 index 00000000000..d5796ba641b --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-break/forced-break-at-fragmentainer-start-000.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-break-3/#forced-breaks"> +<link rel="help" href="https://www.w3.org/TR/css-break-3/#unforced-breaks"> +<meta name="assert" content="A forced break at the block-start of the fragmentainer is allowed, as long as it's otherwise at a valid class A break point"> +<link rel="match" href="../reference/ref-filled-green-100px-square-only.html"> +<p>Test passes if there is a filled green square.</p> +<div style="columns:1; column-fill:auto; width:100px; height:100px; background:green;"> + <div style="break-after:column;"></div> + <div style="height:99px; background:white;"></div> +</div> diff --git a/tests/wpt/web-platform-tests/css/css-break/forced-break-at-fragmentainer-start-001.html b/tests/wpt/web-platform-tests/css/css-break/forced-break-at-fragmentainer-start-001.html new file mode 100644 index 00000000000..b3ad80ca9a0 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-break/forced-break-at-fragmentainer-start-001.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-break-3/#forced-breaks"> +<link rel="help" href="https://www.w3.org/TR/css-break-3/#unforced-breaks"> +<meta name="assert" content="A forced break at the block-start of the fragmentainer is not allowed unless it's at a valid class A break point"> +<link rel="match" href="../reference/ref-filled-green-100px-square.xht"> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="columns:2; column-fill:auto; column-gap:0; width:100px; height:100px; background:red;"> + <div> + <div style="break-before:column; height:200px; background:green;"></div> + </div> +</div> diff --git a/tests/wpt/web-platform-tests/css/css-contain/content-visibility/content-visibility-074-ref.html b/tests/wpt/web-platform-tests/css/css-contain/content-visibility/content-visibility-074-ref.html new file mode 100644 index 00000000000..82a6c263fa9 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-contain/content-visibility/content-visibility-074-ref.html @@ -0,0 +1,35 @@ +<!doctype HTML> +<html> +<meta charset="utf8"> +<title>CSS Content Visibility: toggling auto with composited descedant (reference)</title> +<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility"> + +<style> +#a { will-change: transform; } +#b { height: 15000px; } +#c { + width: 800px; + height: 600px; +} +#d { + will-change: transform; + top: 0px; + width: 500px; + height: 500px; + background: green; +} +.contain { + contain: layout style paint; +} + +</style> + +<div id="a"> +</div> +<div id="b"> + <div id="c" class=contain> + <div id="d"></div> + </div> + </div> +</div> diff --git a/tests/wpt/web-platform-tests/css/css-contain/content-visibility/content-visibility-074.html b/tests/wpt/web-platform-tests/css/css-contain/content-visibility/content-visibility-074.html new file mode 100644 index 00000000000..ff6381ce3d0 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-contain/content-visibility/content-visibility-074.html @@ -0,0 +1,69 @@ +<!doctype HTML> +<html class="reftest-wait"> +<meta charset="utf8"> +<title>CSS Content Visibility: toggling auto with composited descedant</title> +<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility"> +<link rel="match" href="content-visibility-074-ref.html"> +<meta name="assert" content="after toggling content-visibility auto a few times, composited descedant is visible."> + +<script src="/common/reftest-wait.js"></script> + +<style> +#a { will-change: transform; } +#b { height: 15000px; } +#c { + width: 800px; + height: 600px; +} +#d { + will-change: transform; + top: 0px; + width: 500px; + height: 500px; + background: green; +} +.auto { + content-visibility: auto; +} + +</style> + +<div id="a"> +</div> +<div id="b"> + <div id="c" class=auto> + <div id="d"></div> + </div> + </div> +</div> + +<script> +function runTest(step) { + if (step % 2 == 0) { + requestAnimationFrame(() => runTest(step + 1)); + return; + } + + switch(step) { + case 1: + document.getElementById("c").classList.remove("auto"); + break; + case 3: + document.getElementById("c").classList.add("auto"); + break; + case 5: + document.getElementById("c").classList.remove("auto"); + break; + case 7: + document.getElementById("c").classList.add("auto"); + break; + case 9: + takeScreenshot(); + return; + } + requestAnimationFrame(() => runTest(step + 1)); +} + +window.onload = () => requestAnimationFrame(() => runTest(0)); +</script> diff --git a/tests/wpt/web-platform-tests/css/css-flexbox/position-absolute-015.html b/tests/wpt/web-platform-tests/css/css-flexbox/position-absolute-015.html new file mode 100644 index 00000000000..1c0f3bc4c28 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-flexbox/position-absolute-015.html @@ -0,0 +1,19 @@ +<!doctype html> +<title>abspos flex children with top margins</title> +<link rel="author" title="Manuel Rego" href="mailto:rego@igalia.com"> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#abspos-items"> +<link rel="bookmark" href="https://crbug.com/886592"> +<meta name="assert" content="Check abspos position of flex children with margins in justify-content: flex-end container."> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> + +<div style="display: flex; width: 200px; height: 100px; justify-content: flex-end; border: solid thick; position: relative;"> + <div style="background: cyan; margin: 20px; position: absolute; width: 30px; height: 40px;" data-offset-x="150" id="abspos"></div> +</div> + +<script> +checkLayout('#abspos'); +</script> diff --git a/tests/wpt/web-platform-tests/css/css-flexbox/quirks-auto-block-size-with-percentage-item.html b/tests/wpt/web-platform-tests/css/css-flexbox/quirks-auto-block-size-with-percentage-item.html index 966f39f1739..f5c3d7c1818 100644 --- a/tests/wpt/web-platform-tests/css/css-flexbox/quirks-auto-block-size-with-percentage-item.html +++ b/tests/wpt/web-platform-tests/css/css-flexbox/quirks-auto-block-size-with-percentage-item.html @@ -2,6 +2,14 @@ <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> <link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#the-height-property"> <meta name="assert" content="The percentage height resolution quirk isn't applied to flexboxes."> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> + +<!-- Have to wait for onload to fire to ensure the image is loaded. --> +<body onload="checkLayout('#container')"> + <p>There should be a green square to the left of a blue square, and no red.</p> <div id="container" style="width:200px; height:456px;"> <div style="display:flex; background:blue;" data-expected-height="100"> @@ -9,9 +17,3 @@ <div style="width: 50px; height: 100%; background: red;" data-expected-height="0"></div> </div> </div> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/check-layout-th.js"></script> -<script> - checkLayout("#container"); -</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1-ref.html new file mode 100644 index 00000000000..6a7862c9acb --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1-ref.html @@ -0,0 +1,39 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test Reference</title> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: purple; + margin: 10px; + } + + svg { + display: block; + background: green; + } + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <svg class="align justify"></svg> +</div> +<div> + <svg class="align"></svg> +</div> +<div> + <svg class="justify"></svg> +</div> +<div> + <svg></svg> +</div> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1.html new file mode 100644 index 00000000000..1b5ae0a6ad8 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1.html @@ -0,0 +1,45 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-1-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: purple; + margin: 10px; + vertical-align: top; + } + + svg { + background: green; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <svg class="align justify"></svg> +</div> +<div> + <svg class="align"></svg> +</div> +<div> + <svg class="justify"></svg> +</div> +<div> + <svg></svg> +</div> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html new file mode 100644 index 00000000000..6d8d7e848c2 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2-ref.html new file mode 100644 index 00000000000..69d59e85a3c --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2-ref.html @@ -0,0 +1,53 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + img { + display: block; + width: 300px; + height: 150px; + } + + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align" style="width:20px"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img style="width:20px"> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2.html new file mode 100644 index 00000000000..5371e560a5f --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-2-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3-ref.html new file mode 100644 index 00000000000..7ee8a2d6bdc --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3-ref.html @@ -0,0 +1,53 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + img { + display: block; + width: 300px; + height: 150px; + } + + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify" style="height:20px"> +</div> +<div> + <img style="height:20px"> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3.html new file mode 100644 index 00000000000..e2b05bd2166 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-3-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html new file mode 100644 index 00000000000..9edb8aa76f8 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4-ref.html @@ -0,0 +1,53 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + img { + display: block; + width: 300px; + height: 150px; + } + + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align" style="width:10px"> +</div> +<div> + <img class="justify" style="height:20px"> +</div> +<div> + <img style="width:10px; height:20px"> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html new file mode 100644 index 00000000000..1cf2849dad3 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-4-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html new file mode 100644 index 00000000000..6a3446534a1 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5-ref.html @@ -0,0 +1,53 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + img { + display: block; + width: 300px; + height: 150px; + } + + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align" style="width:20px"> +</div> +<div> + <img class="justify" style="width:350px; height:40px"> +</div> +<div> + <img style="width:20px; height:40px"> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html new file mode 100644 index 00000000000..3000e9d97a1 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-5-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" viewBox="0 0 50 100"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html new file mode 100644 index 00000000000..33f472e71b8 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6-ref.html @@ -0,0 +1,54 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + img { + display: block; + width: 300px; + height: 150px; + background: blue; + } + + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align" style="width:0px"> +</div> +<div> + <img class="justify" style="height:20px"> +</div> +<div> + <img style="width:0px; height:20px"> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html new file mode 100644 index 00000000000..655040eb446 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html @@ -0,0 +1,51 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-6-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + img { background: blue; } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="20px" viewBox="0 0 50 100"></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html new file mode 100644 index 00000000000..0d60375f560 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7-ref.html @@ -0,0 +1,54 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + img { + display: block; + width: 300px; + height: 150px; + background: blue; + } + + .justify { + width: 350px; + } + .align { + height: 250px; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align" style="width:20px"> +</div> +<div> + <img class="justify" style="height:0px"> +</div> +<div> + <img style="width:20px; height:0px"> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html new file mode 100644 index 00000000000..29dec490b79 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html @@ -0,0 +1,51 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-7-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + img { background: blue; } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="0px" viewBox="0 0 50 100"></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html new file mode 100644 index 00000000000..09dd27321ab --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8-ref.html @@ -0,0 +1,27 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reference: stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + body { + line-height: 0; + } + + div { + display: inline-block; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } +</style> +<div> +</div> +<div> +</div> +<div> +</div> +<div> +</div> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html new file mode 100644 index 00000000000..933ddd0504e --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="0px" height="20px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html new file mode 100644 index 00000000000..367adae9eb7 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html @@ -0,0 +1,50 @@ +<!doctype html> +<meta charset="utf-8"> +<title>stretching works for replaced items with no aspect ratio</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-grid"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1656281"> +<link rel="match" href="grid-item-no-aspect-ratio-stretch-8-ref.html"> +<style> + body { + line-height: 0; + } + + div { + display: inline-grid; + height: 250px; + width: 350px; + background: grey; + margin: 10px; + vertical-align: top; + } + + .justify { + justify-self: stretch; + } + .align { + align-self: stretch; + } +</style> +<div> + <img class="align justify"> +</div> +<div> + <img class="align"> +</div> +<div> + <img class="justify"> +</div> +<div> + <img> +</div> + +<script> +var url = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="0px"><circle cx="50%" cy="50%" r="50%" fill="blue"/></svg>' +var imgs = document.querySelectorAll('img'); +for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + img.src = url; +} +</script> diff --git a/tests/wpt/web-platform-tests/css/css-multicol/increase-prev-sibling-height-ref.html b/tests/wpt/web-platform-tests/css/css-multicol/increase-prev-sibling-height-ref.html new file mode 100644 index 00000000000..963f584f17d --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-multicol/increase-prev-sibling-height-ref.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<p>The word "PASS" should be seen below.</p> +<div style="height:2em; background:black;"></div> +PASS diff --git a/tests/wpt/web-platform-tests/css/css-multicol/increase-prev-sibling-height.html b/tests/wpt/web-platform-tests/css/css-multicol/increase-prev-sibling-height.html new file mode 100644 index 00000000000..109fe89952e --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-multicol/increase-prev-sibling-height.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-multicol-1/"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1066640"> +<link rel="match" href="increase-prev-sibling-height-ref.html"> +<p>The word "PASS" should be seen below.</p> +<div id="elm" style="background:black;"></div> +<div style="columns:2;"> + PASS +</div> +<script> + requestAnimationFrame(()=>{ + requestAnimationFrame(()=>{ + elm.style.height = "2em"; + }); + }); +</script> diff --git a/tests/wpt/web-platform-tests/css/css-paint-api/paint-function-this-value.https.html b/tests/wpt/web-platform-tests/css/css-paint-api/paint-function-this-value.https.html new file mode 100644 index 00000000000..216913899a9 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-paint-api/paint-function-this-value.https.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>Paint callback is invoked with `this` value of `paintInstance`</title> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api-1/#invoke-a-paint-callback"> +<link rel="match" href="parse-input-arguments-ref.html"> +<style> +.container { + width: 100px; + height: 100px; +} + +#canvas-geometry { + background-image: paint(geometry); +} +</style> +<script src="/common/reftest-wait.js"></script> +<script src="/common/worklet-reftest.js"></script> +<body> +<div id="canvas-geometry" class="container"></div> + +<script id="code" type="text/worklet"> +let paintInstance; + +registerPaint('geometry', class { + constructor() { + paintInstance = this; + } + paint(ctx, geom) { + if (this === paintInstance) + ctx.fillStyle = 'green'; + else + ctx.fillStyle = 'red'; + ctx.fillRect(0, 0, geom.width, geom.height); + } +}); +</script> + +<script> + importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent); +</script> +</body> +</html> diff --git a/tests/wpt/web-platform-tests/docs/running-tests/from-local-system.md b/tests/wpt/web-platform-tests/docs/running-tests/from-local-system.md index 8c71e535baa..91823444d5d 100644 --- a/tests/wpt/web-platform-tests/docs/running-tests/from-local-system.md +++ b/tests/wpt/web-platform-tests/docs/running-tests/from-local-system.md @@ -39,9 +39,6 @@ wherever you currently set your PATH. See also [additional setup required to run Safari](safari.md). ### Windows Setup -**Note:** In general, Windows Subsystem for Linux will provide the smoothest -user experience for running web-platform-tests on Windows, where installation -and usage are similar to Linux. Download and install [Python 2.7](https://www.python.org/downloads). The installer includes `pip` by default. @@ -63,6 +60,15 @@ started using: python wpt serve ``` +#### Windows Subsystem for Linux + +Optionally on Windows you can use the [Windows Subsystem for +Linux](https://docs.microsoft.com/en-us/windows/wsl/about) (WSL). If doing so, +installation and usage are similar to the Linux instructions. Be aware that WSL +may attempt to override `/etc/hosts` each time it is launched, which would then +require you to re-run [`hosts` File Setup](#hosts-file-setup). This behavior +[can be configured](https://docs.microsoft.com/en-us/windows/wsl/wsl-config#network). + ### `hosts` File Setup To get the tests running, you need to set up the test domains in your diff --git a/tests/wpt/web-platform-tests/docs/writing-tests/index.md b/tests/wpt/web-platform-tests/docs/writing-tests/index.md index 90a67c21fce..c1d2fe17a31 100644 --- a/tests/wpt/web-platform-tests/docs/writing-tests/index.md +++ b/tests/wpt/web-platform-tests/docs/writing-tests/index.md @@ -8,43 +8,14 @@ testing plan](making-a-testing-plan) will help you decide what to write. There's also a load of [general guidelines](general-guidelines) that apply to all tests. -```eval_rst -.. toctree:: - :maxdepth: 1 +## Test Types - general-guidelines - ahem - assumptions - crashtest - css-metadata - css-user-styles - file-names - h2tests - lint-tool - making-a-testing-plan - manual - reftest-tutorial - reftests - print-reftests - rendering - server-features - submission-process - testdriver - testdriver-extension-tutorial - testharness - testharness-tutorial - tools - visual - wdspec - test-templates - github-intro -``` - -## Test Type +There are various different ways of writing tests: -Tests in this project use a few different approaches to verify expected -behavior. The tests can be classified based on the way they express -expectations: +* [JavaScript tests (testharness.js)](testharness) are preferred for testing APIs and may be used + for other features too. They are built with the testharness.js unit testing framework, and consist + of assertions written in JavaScript. A high-level [testharness.js tutorial](testharness-tutorial) + is available. * Rendering tests should be used to verify that the browser graphically displays pages as expected. See the [rendering test guidelines](rendering) @@ -53,7 +24,8 @@ expectations: * [Reftests](reftests) should be used to test rendering and layout. They consist of two or more pages with assertions as to whether they render - identically or not. + identically or not. A high-level [reftest tutorial](reftest-tutorial) is available. A + [print reftests](print-reftests) variant is available too. * [Visual tests](visual) should be used for checking rendering where there is a large number of conforming renderings such that reftests are impractical. @@ -61,10 +33,6 @@ expectations: screenshot can be taken and compared to an expected rendering for that user agent on that platform. -* [testharness.js](testharness) tests should be used (where possible!) for - testing everything else. They are built with the testharness.js unit testing - framework, and consist of assertions written in JavaScript. - * [Crashtests](crashtest) tests are used to check that the browser is able to load a given document without crashing or experiencing other low-level issues (asserts, leaks, etc.). They pass if the load @@ -78,14 +46,6 @@ expectations: tested using any of the above. They consist of a page that needs manual interaction or verification of the final result. -In general, there is a strong preference towards reftests and testharness.js -tests types (as they can be easily run without human interaction), so they -should be used in preference to the others even if it results in a -somewhat cumbersome test; there is a far weaker preference between the -two test types, and it is at times advisable to use testharness.js tests -for things which would typically be tested using reftests but for -which it would be overly cumbersome. - See [file names](file-names) for test types and features determined by the file names, and [server features](server-features) for advanced testing features. @@ -94,3 +54,37 @@ and [server features](server-features) for advanced testing features. Once you've written tests, please submit them using the [typical GitHub Pull Request workflow](submission-process); please make sure you run the [`lint` script](lint-tool) before opening a pull request! + +## Table of Contents + +```eval_rst +.. toctree:: + :maxdepth: 1 + + general-guidelines + making-a-testing-plan + testharness + testharness-tutorial + rendering + reftests + reftest-tutorial + print-reftests + visual + crashtest + wdspec + manual + file-names + server-features + submission-process + lint-tool + ahem + assumptions + css-metadata + css-user-styles + h2tests + testdriver + testdriver-extension-tutorial + tools + test-templates + github-intro +``` diff --git a/tests/wpt/web-platform-tests/docs/writing-tests/testharness.md b/tests/wpt/web-platform-tests/docs/writing-tests/testharness.md index 1e9772a4017..1037eaca03c 100644 --- a/tests/wpt/web-platform-tests/docs/writing-tests/testharness.md +++ b/tests/wpt/web-platform-tests/docs/writing-tests/testharness.md @@ -1,4 +1,4 @@ -# testharness.js Tests +# JavaScript Tests (testharness.js) ```eval_rst .. toctree:: diff --git a/tests/wpt/web-platform-tests/dom/events/AddEventListenerOptions-once.html b/tests/wpt/web-platform-tests/dom/events/AddEventListenerOptions-once.html index ae750702c79..361c838b736 100644 --- a/tests/wpt/web-platform-tests/dom/events/AddEventListenerOptions-once.html +++ b/tests/wpt/web-platform-tests/dom/events/AddEventListenerOptions-once.html @@ -78,4 +78,21 @@ test(function() { assert_equals(invoked_count, 0, "The handler should have been removed"); }, "Once listener should be added / removed like normal listeners"); +test(function() { + var invoked_count = 0; + + for (let n = 4; n > 0; n--) { + document.addEventListener('test', (e) => { + invoked_count++; + e.stopImmediatePropagation(); + }, {once: true}); + } + + for (let n = 4; n > 0; n--) { + document.dispatchEvent(new Event('test')); + } + + assert_equals(invoked_count, 4, "The listeners should be invoked"); +}, "Multiple once listeners should be invoked even if the stopImmediatePropagation is set"); + </script> diff --git a/tests/wpt/web-platform-tests/dom/nodes/MutationObserver-callback-arguments.html b/tests/wpt/web-platform-tests/dom/nodes/MutationObserver-callback-arguments.html new file mode 100644 index 00000000000..d64758cb4fa --- /dev/null +++ b/tests/wpt/web-platform-tests/dom/nodes/MutationObserver-callback-arguments.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>MutationObserver: callback arguments</title> +<link rel="help" href="https://dom.spec.whatwg.org/#notify-mutation-observers"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="mo-target"></div> +<div id="log"></div> +<script> +"use strict"; + +async_test(t => { + const moTarget = document.querySelector("#mo-target"); + const mo = new MutationObserver(function(records, observer) { + t.step(() => { + assert_equals(this, mo); + assert_equals(arguments.length, 2); + assert_true(Array.isArray(records)); + assert_equals(records.length, 1); + assert_true(records[0] instanceof MutationRecord); + assert_equals(observer, mo); + + mo.disconnect(); + t.done(); + }); + }); + + mo.observe(moTarget, {attributes: true}); + moTarget.className = "trigger-mutation"; +}, "Callback is invoked with |this| value of MutationObserver and two arguments"); +</script> diff --git a/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html b/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html index 4d1eda941ae..decad097b6d 100644 --- a/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html +++ b/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html @@ -1,3 +1,6 @@ +<!doctype html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> <title> Check the ReportingObserver(s) are notified about the coop-access-violation events. @@ -12,8 +15,10 @@ const directory = "/html/cross-origin-opener-policy/reporting"; const executor_path = directory + "/resources/executor.html?pipe="; -const https = get_host_info().HTTPS_ORIGIN; +const same_origin = get_host_info().HTTPS_ORIGIN; +const cross_site = get_host_info().HTTPS_NOTSAMESITE_ORIGIN; const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)'; +const corp_header = '|header(Cross-Origin-Resource-Policy,cross-origin)'; promise_test(async t => { // This test window. @@ -22,13 +27,13 @@ promise_test(async t => { // The "opener" window, using COOP-Report-Only and a reporter. const opener_token = token(); const opener_reportTo = reportToHeaders(token()); - const opener_url = https + executor_path + opener_reportTo.header + + const opener_url = same_origin + executor_path + opener_reportTo.header + opener_reportTo.coopReportOnlySameOriginHeader + coep_header + `&uuid=${opener_token}`; // The "openee" window, NOT using COOP. const openee_token = token(); - const openee_url = https + executor_path + `&uuid=${openee_token}`; + const openee_url = same_origin + executor_path + `&uuid=${openee_token}`; // 1. Create the opener window. window.open(opener_url); @@ -82,12 +87,12 @@ promise_test(async t => { // The "opener" window, NOT using COOP. const opener_token = token(); - const opener_url = https + executor_path + `&uuid=${opener_token}`; + const opener_url = same_origin + executor_path + `&uuid=${opener_token}`; // The "openee" window, using COOP-Report-Only and a reporter. const openee_token = token(); const openee_reportTo = reportToHeaders(token()); - const openee_url = https + executor_path + openee_reportTo.header + + const openee_url = same_origin + executor_path + openee_reportTo.header + openee_reportTo.coopReportOnlySameOriginHeader + coep_header + `&uuid=${openee_token}`; @@ -132,4 +137,126 @@ promise_test(async t => { let report_access_to = JSON.parse(await receive(this_window_token)); assert_equals(report_access_to.length, 0, "Unexpected report received."); }, "Openee COOP"); + +promise_test(async t => { + // This test window. + const this_window_token = token(); + + // The "opener" window, using COOP-Report-Only and a reporter. + const opener_token = token(); + const opener_reportTo = reportToHeaders(token()); + const opener_url = same_origin + executor_path + opener_reportTo.header + + opener_reportTo.coopReportOnlySameOriginHeader + coep_header + + `&uuid=${opener_token}`; + + // The "opener's iframe", same-origin with its parent. + const opener_iframe_token = token(); + const opener_iframe_url = same_origin + executor_path + coep_header + + `&uuid=${opener_iframe_token}`; + + // The "openee" window, NOT using COOP. + const openee_token = token(); + const openee_url = same_origin + executor_path + coep_header + + `&uuid=${openee_token}`; + + // 1. Create the opener window. + window.open(opener_url); + t.add_cleanup(() => send(opener_token, "window.close();")); + + // 2. The opener opens an iframe, and install a ReportingObserver to catch + // future accesses. + send(opener_token, ` + iframe = document.createElement("iframe"); + iframe.src = "${opener_iframe_url}"; + document.body.appendChild(iframe); + + let observer = new ReportingObserver(reports => { + send("${this_window_token}", JSON.stringify(reports)); + observer.disconnect(); + }); + observer.observe(); + `); + + // 3. The iframe opens the openee. + send(opener_iframe_token, `openee = window.open('${openee_url}');`); + t.add_cleanup(() => send(openee_token, `window.close();`)); + + // 4. Wait for the openee to load its document. + send(openee_token, `send("${this_window_token}", "Ready");`); + assert_equals(await receive(this_window_token), "Ready"); + + // 4. The opener's iframe tries to access the openee. This is an + // "access-from-coop-page" from a same-origin iframe, so the + // ReportingObserver(s) are notified. + send(opener_iframe_token, `tryAccess(openee);`); + + let reports = await receive(this_window_token); + assert_not_equals(reports, "timeout", "No report received."); + reports = JSON.parse(reports); + assert_equals(reports.length, 1, "No report received."); + assert_equals(reports[0].type, "coop-access-violation"); + assert_equals(reports[0].url, opener_url.replace(/"/g, '%22')); + assert_true(reports[0].body.sourceFile.includes("try-access.js")); + assert_equals(reports[0].body.lineNumber, 6); + assert_equals(reports[0].body.columnNumber, 7); + assert_equals(reports[0].body.property, "blur"); +}, "Access from same-origin iframe") + +promise_test(async t => { + // This test window. + const this_window_token = token(); + + // The "opener" window, using COOP-Report-Only and a reporter. + const opener_token = token(); + const opener_reportTo = reportToHeaders(token()); + const opener_url = same_origin + executor_path + opener_reportTo.header + + opener_reportTo.coopReportOnlySameOriginHeader + coep_header + + `&uuid=${opener_token}`; + + // The "opener's iframe", same-origin with its parent. + const opener_iframe_token = token(); + const opener_iframe_url = cross_site + executor_path + coep_header + + corp_header + + `&uuid=${opener_iframe_token}`; + + // The "openee" window, NOT using COOP. + const openee_token = token(); + const openee_url = same_origin + executor_path + coep_header + + `&uuid=${openee_token}`; + + // 1. Create the opener window. + window.open(opener_url); + t.add_cleanup(() => send(opener_token, "window.close();")); + + // 2. The opener opens an iframe, and install a ReportingObserver to catch + // future accesses. + send(opener_token, ` + iframe = document.createElement("iframe"); + iframe.src = "${opener_iframe_url}"; + document.body.appendChild(iframe); + + let observer = new ReportingObserver(reports => { + send("${this_window_token}", JSON.stringify(reports)); + observer.disconnect(); + }); + observer.observe(); + `); + + // 3. The iframe opens the openee. + send(opener_iframe_token, `openee = window.open('${openee_url}');`); + t.add_cleanup(() => send(openee_token, `window.close();`)); + + // 4. Wait for the openee to load its document. + send(openee_token, `send("${this_window_token}", "Ready");`); + assert_equals(await receive(this_window_token), "Ready"); + + // 5. The opener's iframe tries to access the openee. This is an + // "access-from-coop-page" from a cross-site iframe. The ReportingObservers + // from the main document aren't notified. + send(opener_iframe_token, `tryAccess(openee);`); + + let reports = await receive(this_window_token); + assert_equals(reports, "timeout", "Unexpected report received."); +}, "Access from cross-site iframe") + </script> diff --git a/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html b/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html index 7dba76c4ef5..f807561f174 100644 --- a/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html +++ b/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-redirect-with-same-origin-allow-popups.https.html @@ -1,6 +1,5 @@ <title> - COOP reports are to the opener when the opener used COOP-RO+COEP and then its - cross-origin openee tries to access it. + Tests the redirect interaction with COOP same-origin-allow-popups. </title> <meta name=timeout content=long> <script src=/resources/testharness.js></script> @@ -8,77 +7,109 @@ <script src=/common/get-host-info.sub.js></script> <script src="/common/utils.js"></script> <script src="../resources/dispatcher.js"></script> -<script src="../resources/try-access.js"></script> <script> const directory = "/html/cross-origin-opener-policy/reporting"; const executor_path = directory + "/resources/executor.html?pipe="; -const same_origin = get_host_info().HTTPS_ORIGIN; -const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN; +const same_origin = { + host: get_host_info().HTTPS_ORIGIN, + name: "Same origin" +}; +const cross_origin = { + host: get_host_info().HTTPS_REMOTE_ORIGIN, + name: "Cross origin" +}; const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)'; -promise_test(async t => { - // The test window. - const this_window_token = token(); +// Tests the redirect interaction with COOP same-origin-allow-popups and +// reporting: +// 1 - open the opener document on origin same_origin wit COOP +// same-origin-allow-popups. +// 2 - opener opens popup with document on origin popup_origin, no COOP and a +// redirect header (HTTP 302, location). +// 3 - redirection to a document with origin same_origin and COOP +// same-origin-allow-popups. +// +// The navigation (2) to the first document of the popup stays in the same +// browsing context group due to the same-origin-allow-popups COOP of the +// opener. +// The redirect (3) to the final document does since it compares the +// popup_origin/unsafe-none document with the +// same-origin/same-origin-allow-popups document. +// +// A opens B, B redirects to C. +// +// Document Origin COOP +// -------- ------------ ------------------------ +// A same-origin same-origin-allow-popups +// B popup-origin unsafe-none +// C same-origin same-origin-allow-popups +function redirect_test( popup_origin ) { + promise_test(async t => { + // The test window. + const this_window_token = token(); - // The "opener" window. This has COOP same-origin-allow-popups and a reporter. - const opener_report_token= token(); - const opener_token = token(); - const opener_reportTo = reportToHeaders(opener_report_token); - const opener_url = same_origin + executor_path + opener_reportTo.header + - opener_reportTo.coopSameOriginAllowPopupsHeader + - `&uuid=${opener_token}`; + // The "opener" window. This has COOP same-origin-allow-popups and a + // reporter. + const opener_report_token= token(); + const opener_token = token(); + const opener_reportTo = reportToHeaders(opener_report_token); + const opener_url = same_origin.host + executor_path + + opener_reportTo.header + opener_reportTo.coopSameOriginAllowPopupsHeader + + `&uuid=${opener_token}`; - // The "openee" window. - // The initial URL is cross-origin with the opener, and redirects to a - // same-origin page with same-origin-allow-popups. - // The navigation to the cross-origin page stays in the same browsing context - // group due to the same-origin-allow-popups policy, but the redirect to the - // final page does since it compares the cross-origin/unsafe-none document - // with the same-origin/same-origin-allow-popups document. - const openee_token = token(); - const openee_redirect_url = same_origin + executor_path + - opener_reportTo.header + opener_reportTo.coopSameOriginAllowPopupsHeader + - `&uuid=${openee_token}`; - const redirect_header = 'status(302)' + - `|header(Location,${encodeURIComponent( - openee_redirect_url - .replace(/,/g, "\\,") - .replace(/\\\\,/g, "\\\\\\,") - .replace(/\(/g, "%28") - .replace(/\)/g, "%29"))})`; - const openee_url = cross_origin + executor_path + redirect_header + - `&uuid=${openee_token}`; - // 1. Create the opener window. - let opener_window_proxy = window.open(opener_url); - t.add_cleanup(() => send(opener_token, "window.close()")); + // The "openee" window. + // The initial document does not have COOP and is on popup_origin, it + // redirects to a same-origin (with the opener) document with COOP + // same-origin-allow-popups. + const openee_token = token(); + const openee_redirect_url = same_origin.host + executor_path + + opener_reportTo.header + opener_reportTo.coopSameOriginAllowPopupsHeader + + `&uuid=${openee_token}`; + const redirect_header = 'status(302)' + + `|header(Location,${encodeURIComponent( + openee_redirect_url + .replace(/,/g, "\\,") + .replace(/\\\\,/g, "\\\\\\,") + .replace(/\(/g, "%28") + .replace(/\)/g, "%29"))})`; + const openee_url = popup_origin.host + executor_path + redirect_header + + `&uuid=${openee_token}`; + // 1. Create the opener window. + let opener_window_proxy = window.open(opener_url); + t.add_cleanup(() => send(opener_token, "window.close()")); - // 2. The opener opens its openee. - send(opener_token, ` - openee = window.open("${openee_url}"); - `); - t.add_cleanup(() => send(openee_token, "window.close()")); + // 2. The opener opens its openee. + send(opener_token, ` + openee = window.open("${openee_url}"); + `); + t.add_cleanup(() => send(openee_token, "window.close()")); - // 3. Check the opener status on the openee. - send(openee_token, ` - send("${this_window_token}", opener !== null); - `); - assert_equals(await receive(this_window_token), "false", "opener"); + // 3. Check the opener status on the openee. + send(openee_token, ` + send("${this_window_token}", opener !== null); + `); + assert_equals(await receive(this_window_token), "false", "opener"); - // 4. Check the openee status on the opener. - send(opener_token, ` - send("${this_window_token}", openee.closed); - `); - assert_equals(await receive(this_window_token), "true", "openee.closed"); + // 4. Check the openee status on the opener. + send(opener_token, ` + send("${this_window_token}", openee.closed); + `); + assert_equals(await receive(this_window_token), "true", "openee.closed"); - // 5. Check a report sent to the openee. - let report = await receiveReport(opener_report_token, "navigation-to-document") - assert_not_equals(report, "timeout", "Report not received"); - assert_equals(report.type, "coop"); - assert_equals(report.url, openee_redirect_url.replace(/"/g, '%22')); - assert_equals(report.body["disposition"], "enforce"); - assert_equals(report.body["effective-policy"], "same-origin-allow-popups"); - assert_equals(report.body["document-uri"], openee_url); -}, "Cross-origin openee redirected to same-origin with same-origin-allow-popups"); + // 5. Check a report sent to the openee. + let report = await receiveReport( + opener_report_token, + "navigation-to-document"); + assert_not_equals(report, "timeout", "Report not received"); + assert_equals(report.type, "coop"); + assert_equals(report.url, openee_redirect_url.replace(/"/g, '%22')); + assert_equals(report.body["disposition"], "enforce"); + assert_equals(report.body["effective-policy"], "same-origin-allow-popups"); + assert_equals(report.body["document-uri"], openee_url); + }, `${popup_origin.name} openee redirected to same-origin with same-origin-allow-popups`); +} +redirect_test(same_origin); +redirect_test(cross_origin); </script> diff --git a/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/resources/dispatcher.py b/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/resources/dispatcher.py index 2617361b0b3..e77583e479f 100644 --- a/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/resources/dispatcher.py +++ b/tests/wpt/web-platform-tests/html/cross-origin-opener-policy/reporting/resources/dispatcher.py @@ -9,12 +9,19 @@ def main(request, response): return b'' uuid = request.GET[b'uuid'] + stash = request.server.stash; + with stash.lock: + queue = stash.take(uuid) + if queue is None: + queue = [] - if request.method == u'POST': - return request.server.stash.put(uuid, request.body) - else: - body = request.server.stash.take(uuid) - if body is None: - return b'not ready' + if request.method == u'POST': + queue.append(request.body) + ret = b'done' else: - return body + if len(queue) == 0: + ret = b'not ready' + else: + ret = queue.pop(0) + stash.put(uuid, queue) + return ret; diff --git a/tests/wpt/web-platform-tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html b/tests/wpt/web-platform-tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html index 07c73554e59..9e1c9ed152f 100644 --- a/tests/wpt/web-platform-tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html +++ b/tests/wpt/web-platform-tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-flexbox.html @@ -81,4 +81,15 @@ legend { testElm.querySelector('div').offsetTop, 'offsetTop') }, "Inline flex"); + +test(() => { + const testElm = document.getElementById('test'); + testElm.style.flexDirection = 'row'; + const item0 = testElm.querySelectorAll('div')[0]; + const item1 = testElm.querySelectorAll('div')[1]; + assert_equals(item0.offsetTop, item1.offsetTop); + + testElm.style.flexDirection = 'column'; + assert_true(item0.offsetTop < item1.offsetTop); +}, "Dynamic change of flex-direction"); </script> diff --git a/tests/wpt/web-platform-tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html b/tests/wpt/web-platform-tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html index 3bf8cfb6cc8..196f6d0409a 100644 --- a/tests/wpt/web-platform-tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html +++ b/tests/wpt/web-platform-tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html @@ -33,7 +33,10 @@ destIFrame.onload = () => { ++destLoadCount; if (destLoadCount === 2) { - assert_unreached("The iframe into which the meta was moved must not refresh"); + // destIFrame doesn't have the sandboxed automatic features browsing context + // flag sets, thus navigated. + assert_equals(destIFrame.contentDocument.body.textContent.trim(), "foo"); + done(); } maybeStartTest(); @@ -41,9 +44,6 @@ destIFrame.onload = () => { function maybeStartTest() { if (sourceLoadCount === 1 && destLoadCount === 1) { - // Test that no refreshes occur within 3 seconds - step_timeout(done, 3000); - const meta = sourceIFrame.contentDocument.querySelector("meta"); destIFrame.contentDocument.body.appendChild(meta); } diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html new file mode 100644 index 00000000000..817cf6d5ddf --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker-importScripts.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<title>Base URLs used in resolving specifiers in dynamic imports from importScripts()</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +fetch_tests_from_worker(new Worker("./worker-importScripts.sub.js")); +</script> diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html new file mode 100644 index 00000000000..a12204281cc --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url-worker.sub.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<title>Base URLs used in resolving specifiers in dynamic imports from workers</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +fetch_tests_from_worker(new Worker( + "../beta/redirect.py?location=http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js")); +</script> diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html new file mode 100644 index 00000000000..f7d4927a104 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/base-url.sub.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<title>Base URLs used in resolving specifiers in dynamic imports</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> +self.testName = "same origin classic <script>"; +self.baseUrlSanitized = false; +</script> +<script src="../beta/redirect.py?location=http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"></script> + +<script> +self.testName = "cross origin classic <script> without crossorigin attribute"; +self.baseUrlSanitized = true; +</script> +<script src="../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"></script> + +<script> +self.testName = "cross origin classic <script> with crossorigin attribute"; +self.baseUrlSanitized = false; +</script> +<script src="../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js%3Fpipe=header(Access-Control-Allow-Origin,*)" crossorigin></script> + +<script> +self.testName = "cross origin module <script>"; +self.baseUrlSanitized = false; +</script> +<script src="../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js%3Fpipe=header(Access-Control-Allow-Origin,*)" type="module"></script> diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js new file mode 100644 index 00000000000..b2ac52df2a1 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/import.js @@ -0,0 +1 @@ +export const A = { "from": "alpha/import.js" }; diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js new file mode 100644 index 00000000000..904d32f9bfd --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/alpha/worker-importScripts.sub.js @@ -0,0 +1,15 @@ +"use strict"; + +importScripts("/resources/testharness.js"); + +// CORS-same-origin +self.testName = "same-origin importScripts()"; +self.baseUrlSanitized = false; +importScripts("../beta/redirect.py?location=http://{{host}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"); + +// CORS-cross-origin +self.testName = "cross-origin importScripts()"; +self.baseUrlSanitized = true; +importScripts("../beta/redirect.py?location=http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js"); + +done(); diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js new file mode 100644 index 00000000000..7de1c681825 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/import.js @@ -0,0 +1 @@ +export const A = { "from": "beta/import.js" }; diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py new file mode 100644 index 00000000000..f2fd1ebd51d --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/beta/redirect.py @@ -0,0 +1,19 @@ +def main(request, response): + """Simple handler that causes redirection. + + The request should typically have two query parameters: + status - The status to use for the redirection. Defaults to 302. + location - The resource to redirect to. + """ + status = 302 + if b"status" in request.GET: + try: + status = int(request.GET.first(b"status")) + except ValueError: + pass + + response.status = status + + location = request.GET.first(b"location") + + response.headers.set(b"Location", location) diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js new file mode 100644 index 00000000000..ec7784983d1 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/base-url.sub.js @@ -0,0 +1,58 @@ +"use strict"; + +// This script triggers import(), and thus the base URL of this script +// (either loaded by `<script>` or `importScripts()`) is used as the base URL +// of resolving relative URL-like specifiers in `import()`. + +// The following fields should be set by the callers of this script +// (unless loaded as the worker top-level script): +// - self.testName (string) +// - self.baseUrlSanitized (boolean) + +// When this script is loaded as the worker top-level script: +if ('DedicatedWorkerGlobalScope' in self && + self instanceof DedicatedWorkerGlobalScope && + !self.testName) { + importScripts("/resources/testharness.js"); + self.testName = 'worker top-level script'; + // Worker top-level scripts are always same-origin. + self.baseUrlSanitized = false; +} + +{ + // This could change by the time the test is executed, so we save it now. + // As this script is loaded multiple times, savedBaseUrlSanitized is scoped. + const savedBaseUrlSanitized = self.baseUrlSanitized; + + promise_test(() => { + const promise = import("./import.js?pipe=header(Access-Control-Allow-Origin,*)&label=relative-" + self.testName); + if (savedBaseUrlSanitized) { + // The base URL is "about:blank" and thus import() here should fail. + return promise.then(module => { + // This code should be unreached, but assert_equals() is used here + // to log `module.A["from"]` in case of unexpected resolution. + assert_equals(module.A["from"], "(unreached)", + "Relative URL-like specifier resolution should fail"); + assert_unreached(); + }, + () => {}); + } else { + // The base URL is the response URL of this script, i.e. + // `.../gamma/base-url.sub.js`. + return promise.then(module => { + assert_equals(module.A["from"], "gamma/import.js"); + }); + } + }, + "Relative URL-like from " + self.testName); +} + +promise_test(() => { + return import("http://{{hosts[alt][]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js?pipe=header(Access-Control-Allow-Origin,*)&label=absolute-" + self.testName) + .then(module => { + assert_equals(module.A["from"], "gamma/import.js"); + }) + }, + "Absolute URL-like from " + self.testName); + +done(); diff --git a/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js new file mode 100644 index 00000000000..435c1369be2 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/scripting-1/the-script-element/module/dynamic-import/gamma/import.js @@ -0,0 +1 @@ +export const A = { "from": "gamma/import.js" }; diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html new file mode 100644 index 00000000000..15617865691 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html @@ -0,0 +1,20 @@ +<!doctype html> +<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed --> +<meta charset=windows-1254> +<meta name=timeout content=long> +<title>registerProtocolHandler() and a handler with %s in the fragment</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script> +<script> +// Configure expectations for individual test +window.type = "fragment"; +window.noSW = false; +</script> +<script src=resources/handler-tools.js></script> +<ol> + <li><p>First, register the handler: <button onclick='register()'>Register</button>. + <li><p>Then, run the test: <button onclick='runTest()'>Run</button>. + <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>. +</ol> +<div id=log></div> diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html new file mode 100644 index 00000000000..be3a6be6665 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html @@ -0,0 +1,20 @@ +<!doctype html> +<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed --> +<meta charset=windows-1254> +<meta name=timeout content=long> +<title>registerProtocolHandler() and a handler with %s in the fragment (does not use a service worker)</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script> +<script> +// Configure expectations for individual test +window.type = "fragment"; +window.noSW = true; +</script> +<script src=resources/handler-tools.js></script> +<ol> + <li><p>First, register the handler: <button onclick='register()'>Register</button>. + <li><p>Then, run the test: <button onclick='runTest()'>Run</button>. + <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>. +</ol> +<div id=log></div> diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html new file mode 100644 index 00000000000..085c5723ec4 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html @@ -0,0 +1,20 @@ +<!doctype html> +<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed --> +<meta charset=windows-1254> +<meta name=timeout content=long> +<title>registerProtocolHandler() and a handler with %s in the path</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script> +<script> +// Configure expectations for individual test +window.type = "path"; +window.noSW = false; +</script> +<script src=resources/handler-tools.js></script> +<ol> + <li><p>First, register the handler: <button onclick='register()'>Register</button>. + <li><p>Then, run the test: <button onclick='runTest()'>Run</button>. + <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>. +</ol> +<div id=log></div> diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html new file mode 100644 index 00000000000..8ce65a5bad8 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html @@ -0,0 +1,20 @@ +<!doctype html> +<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed --> +<meta charset=windows-1254> +<meta name=timeout content=long> +<title>registerProtocolHandler() and a handler with %s in the query</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script> +<script> +// Configure expectations for individual test +window.type = "query"; +window.noSW = false; +</script> +<script src=resources/handler-tools.js></script> +<ol> + <li><p>First, register the handler: <button onclick='register()'>Register</button>. + <li><p>Then, run the test: <button onclick='runTest()'>Run</button>. + <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>. +</ol> +<div id=log></div> diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html new file mode 100644 index 00000000000..9b4473fb895 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html @@ -0,0 +1,20 @@ +<!doctype html> +<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed --> +<meta charset=windows-1254> +<meta name=timeout content=long> +<title>registerProtocolHandler() and a handler with %s in the query (does not use a service worker)</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script> +<script> +// Configure expectations for individual test +window.type = "query"; +window.noSW = true; +</script> +<script src=resources/handler-tools.js></script> +<ol> + <li><p>First, register the handler: <button onclick='register()'>Register</button>. + <li><p>Then, run the test: <button onclick='runTest()'>Run</button>. + <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>. +</ol> +<div id=log></div> diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js new file mode 100644 index 00000000000..5fd915d17f9 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js @@ -0,0 +1,3 @@ +onfetch = e => { + e.respondWith(fetch("handler.html")); +} diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js new file mode 100644 index 00000000000..073287265cf --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js @@ -0,0 +1,53 @@ +// These can be used in an environment that has these global variables defined: +// * type (one of "path", "query", or "fragment") +// * noSW (a boolean) + +if (type === "path" && noSW) { + throw new Error("There is no support for a path handler without a service worker."); +} + +const swString = noSW ? "" : "sw"; +const handler = { + "path": "PSS%sPSE/?QES\u2020QEE#FES\u2020FEE", + "query": "?QES\u2020QEEPSS%sPSE#FES\u2020FEE", + "fragment": "?QES\u2020QEE#FES\u2020FEEPSS%sPSE" +}[type]; +const scheme = `web+wpt${type}${swString}`; + +function register() { + const handlerURL = noSW ? `resources/handler.html${handler}${type}` : `resources/handler/${type}/${handler}`; + navigator.registerProtocolHandler(scheme, handlerURL, `WPT ${type} handler${noSW ? ", without service worker" : ""}`); +} + +function runTest({ includeNull = false } = {}) { + promise_test(async t => { + const bc = new BroadcastChannel(`protocol-handler-${type}${swString}`); + if (!noSW) { + const reg = await service_worker_unregister_and_register(t, "resources/handler-sw.js", "resources/handler/"); + t.add_cleanup(async () => await reg.unregister()); + await wait_for_state(t, reg.installing, 'activated'); + } + const a = document.body.appendChild(document.createElement("a")); + const codePoints = []; + let i = includeNull ? 0 : 1; + for (; i < 0x82; i++) { + codePoints.push(String.fromCharCode(i)); + } + a.href = `${scheme}:${codePoints.join("")}`; + a.target = "_blank"; + a.click(); + await new Promise(resolve => { + bc.onmessage = t.step_func(e => { + resultingURL = e.data; + assert_equals(stringBetweenMarkers(resultingURL, "QES", "QEE"), "%86", "query baseline"); + assert_equals(stringBetweenMarkers(resultingURL, "FES", "FEE"), "%E2%80%A0", "fragment baseline"); + assert_equals(stringBetweenMarkers(resultingURL, "PSS", "PSE"), `${encodeURIComponent(scheme)}%3A${includeNull ? "%2500" : ""}%2501%2502%2503%2504%2505%2506%2507%2508%250B%250C%250E%250F%2510%2511%2512%2513%2514%2515%2516%2517%2518%2519%251A%251B%251C%251D%251E%251F%20!%22%23%24%25%26${type === "query" ? "%27" : "'"}()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%257F%25C2%2580%25C2%2581`, "actual test"); + resolve(); + }); + }); + }); +} + +function stringBetweenMarkers(string, start, end) { + return string.substring(string.indexOf(start) + start.length, string.indexOf(end)); +} diff --git a/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html new file mode 100644 index 00000000000..552e5417842 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html @@ -0,0 +1,23 @@ +<!doctype html> +<p>This popup can be closed if it does not close itself. +<p> +<script> +// This resource either gets navigated to through a service worker as a result of a URL that looks +// like: +// https://.../html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler/{type}/... +// (the host is excluded to not upset the lint tool) +// or it gets navigated to directly with the type appended to the end of the URL. In that case type +// can only be fragment or query. + +let type = null; +let swString = null; +if (new URL(document.URL).pathname.endsWith("handler.html")) { + swString = ""; + type = (document.URL.endsWith("fragment")) ? "fragment" : "query"; +} else { + type = document.URL.split("/")[9]; + swString = "sw"; +} +new BroadcastChannel(`protocol-handler-${type}${swString}`).postMessage(document.URL); +window.close(); +</script> diff --git a/tests/wpt/web-platform-tests/input-events/input-events-get-target-ranges-backspace.tentative.html b/tests/wpt/web-platform-tests/input-events/input-events-get-target-ranges-backspace.tentative.html new file mode 100644 index 00000000000..cf512269b07 --- /dev/null +++ b/tests/wpt/web-platform-tests/input-events/input-events-get-target-ranges-backspace.tentative.html @@ -0,0 +1,811 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>InputEvent.getTargetRanges() at Backspace</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<div contenteditable></div> +<script> +const kBackspaceKey = "\uE003"; +const kShift = "\uE008"; +const kMeta = "\uE03d"; +const kControl = "\uE009"; +const kAlt = "\uE00A"; + +let selection = getSelection(); +let editor = document.querySelector("div[contenteditable]"); +let beforeinput = []; +let input = []; +editor.addEventListener("beforeinput", (e) => { + // NOTE: Blink makes `getTargetRanges()` return empty range after propagaion, + // but this test wants to check the result during propagation. + // Therefore, we need to cache the result, but will assert if + // `getTargetRanges()` returns different ranges after checking the + // cached ranges. + e.cachedRanges = e.getTargetRanges(); + beforeinput.push(e); +}); +editor.addEventListener("input", (e) => { + e.cachedRanges = e.getTargetRanges(); + input.push(e); +}); + +function reset() { + editor.focus(); + beforeinput = []; + input = []; +} + +function getRangeDescription(range) { + function getNodeDescription(node) { + if (!node) { + return "null"; + } + switch (node.nodeType) { + case Node.TEXT_NODE: + case Node.COMMENT_NODE: + case Node.CDATA_SECTION_NODE: + return `${node.nodeName} "${node.data}"`; + case Node.ELEMENT_NODE: + return `<${node.nodeName.toLowerCase()}>`; + default: + return `${node.nodeName}`; + } + } + if (range === null) { + return "null"; + } + if (range === undefined) { + return "undefined"; + } + return range.startContainer == range.endContainer && range.startOffset == range.endOffset + ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})` + : `(${getNodeDescription(range.startContainer)}, ${range.startOffset}) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`; +} + +function getArrayOfRangesDescription(arrayOfRanges) { + if (arrayOfRanges === null) { + return "null"; + } + if (arrayOfRanges === undefined) { + return "undefined"; + } + if (!Array.isArray(arrayOfRanges)) { + return "Unknown Object"; + } + if (arrayOfRanges.length === 0) { + return "[]"; + } + let result = "["; + for (let range of arrayOfRanges) { + result += `{${getRangeDescription(range)}},`; + } + result += "]"; + return result; +} + +function sendBackspaceKey(modifier) { + if (!modifier) { + return new test_driver.Actions() + .keyDown(kBackspaceKey) + .keyUp(kBackspaceKey) + .send(); + } + return new test_driver.Actions() + .keyDown(modifier) + .keyDown(kBackspaceKey) + .keyUp(kBackspaceKey) + .keyUp(modifier) + .send(); +} + +function checkGetTargetRangesKeepReturningSameValue(event) { + // https://github.com/w3c/input-events/issues/114 + assert_equals(getArrayOfRangesDescription(event.getTargetRanges()), + getArrayOfRangesDescription(event.cachedRanges), + `${event.type}.getTargetRanges() should keep returning the same array of ranges even after its propagation finished`); +} + +function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRange) { + assert_equals(beforeinput.length, 1, + "One beforeinput event should be fired if the key operation deletes something"); + assert_true(Array.isArray(beforeinput[0].cachedRanges), + "beforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation"); + // Before checking the length of array of ranges, we should check first range + // first because the first range data is more important than whether there + // are additional unexpected ranges. + if (beforeinput[0].cachedRanges.length > 0) { + assert_equals( + getRangeDescription(beforeinput[0].cachedRanges[0]), + getRangeDescription(expectedRange), + `beforeinput.getTargetRanges() should return expected range (inputType is "${beforeinput[0].inputType}")`); + assert_equals(beforeinput[0].cachedRanges.length, 1, + "beforeinput.getTargetRanges() should return one range within an array"); + } + assert_equals(beforeinput[0].cachedRanges, 1, + "One range should be returned from getTargetRanges() when the key operation deletes something"); + checkGetTargetRangesKeepReturningSameValue(beforeinput[0]); +} + +function checkGetTargetRangesOfInputOnDeleteSomething() { + assert_equals(input.length, 1, + "One input event should be fired if the key operation deletes something"); + // https://github.com/w3c/input-events/issues/113 + assert_true(Array.isArray(input[0].cachedRanges), + "input[0].getTargetRanges() should return an array of StaticRange instances during propagation"); + assert_equals(input[0].cachedRanges.length, 0, + "input[0].getTargetRanges() should return empty array during propagation"); + checkGetTargetRangesKeepReturningSameValue(input[0]); +} + +function checkBeforeinputAndInputEventsOnNOOP() { + assert_equals(beforeinput.length, 0, + "beforeinput event shouldn't be fired when the key operation does not cause modifying the DOM tree"); + assert_equals(input.length, 0, + "input event shouldn't be fired when the key operation does not cause modifying the DOM tree"); +} + +// Simply deletes the previous ASCII character of caret position. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc</p>"; + selection.collapse(editor.firstChild.firstChild, 1); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>bc</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 0, + endContainer: editor.firstChild.firstChild, + endOffset: 1, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>a[]bc</p>"'); + +// Simply deletes the previous ASCII character of caret position. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc</p>"; + selection.collapse(editor.firstChild.firstChild, 2); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>ac</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 1, + endContainer: editor.firstChild.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>ab[]c</p>"'); + +// Simply deletes the previous ASCII character of caret position. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc</p>"; + selection.collapse(editor.firstChild.firstChild, 3); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>ab</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 2, + endContainer: editor.firstChild.firstChild, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc[]</p>"'); + +// Should delete the `<span>` element becase it becomes empty. +// However, we need discussion whether the `<span>` element should be +// contained by a range of `getTargetRanges()`. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>a<span>b</span>c</p>"; + let c = editor.querySelector("span").nextSibling; + selection.collapse(c, 0); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>ac</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild, + startOffset: 1, + endContainer: c, + endOffset: 0, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>a<span>b</span>[]c</p>"'); + +// Should delete the `<span>` element becase it becomes empty. +// However, we need discussion whether the `<span>` element should be +// contained by a range of `getTargetRanges()`. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>a<span>b</span>c</p>"; + let b = editor.querySelector("span").firstChild; + selection.collapse(b, 1); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>ac</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild, + startOffset: 1, + endContainer: editor.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>a<span>b[]</span>c</p>"'); + +// Invisible leading white-space may be deleted when the first visible +// character is deleted. If it's deleted, it should be contained by +// the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p> abc</p>"; + selection.collapse(editor.firstChild.firstChild, 2); + await sendBackspaceKey(); + assert_in_array(editor.innerHTML, ["<p>bc</p>", "<p> bc</p>"]); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: editor.firstChild.firstChild.length == 2 ? 0 : 1, + endContainer: editor.firstChild.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p> a[]bc</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(def, 3); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc </p><p> []def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(def, 2); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc </p><p> [] def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(def, 1); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc </p><p> [] def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(def, 0); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc </p><p>[] def</p>"'); + +// Invisible leading white-spaces in current block and invisible trailing +// white-spaces in the previous block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.setBaseAndExtent(abc, 6, def, 0); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc [</p><p>] def</p>"'); + +// Invisible leading white-spaces in the current block should be deleted +// for avoiding they becoming visible when the blocks are joined, but +// preformatted trailing white-spaces in the first block shouldn't be +// deleted. Perhaps, the invisible white-spaces should be contained by +// the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<pre>abc </pre><p> def</p>"; + let pre = editor.firstChild; + let abc = pre.firstChild; + let p = pre.nextSibling; + let def = p.firstChild; + selection.collapse(def, 3); + await sendBackspaceKey(); + // https://github.com/w3c/input-events/issues/112 + // Shouldn't make the invisible white-spaces visible. + assert_equals(editor.innerHTML, "<pre>abc def</pre>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<pre>abc </pre><p> []def</p>"'); + +// Invisible leading/trailing white-spaces in the current block should be +// deleted for avoiding they becoming visible when the blocks are joined, but +// preformatted trailing white-spaces in the first block shouldn't be +// deleted. Perhaps, the invisible leading white-spaces should be contained +// by the range of `getTargetRanges()`, but needs discussion. +// And also not sure whether the trailing white-spaces should be contained +// by additional range of `getTargetRanges()` or not because of the +// implementation cost and runtime cost. Needs discuss. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<pre>abc </pre><p> def </p>"; + let pre = editor.firstChild; + let abc = pre.firstChild; + let p = pre.nextSibling; + let def = p.firstChild; + selection.collapse(def, 3); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<pre>abc def </pre>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<pre>abc </pre><p> []def </p>"'); + +// Invisible trailing white-spaces in the first block should be deleted +// when the block is joined with the preformated following block, but +// the leading white-spaces in the preformatted block shouldn't be +// removed. So, in this case, the invisible trailing white-spaces should +// be in the range of `getTargetRanges()`, but not so for the preformatted +// visible leading white-spaces. But needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><pre> def</pre>"; + let p = editor.firstChild; + let abc = p.firstChild; + let pre = p.nextSibling; + let def = pre.firstChild; + selection.collapse(def, 0); + await sendBackspaceKey(); + assert_in_array(editor.innerHTML, ["<p>abc def</p>", + "<p>abc def</p>", + "<p>abc def</p>", + "<p>abc def</p>"]); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc </p><pre>[] def</pre>"'); + +// If the first block has invisible `<br>` element and joining it with +// the following block, the invisible trailing `<br>` element should be +// deleted and join the blocks. Therefore, the target range should contain +// the `<br>` element and block boundaries. But maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br></p><p>def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(def, 0); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p1, + startOffset: 1, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc<br></p><p>[]def</p>"'); + +// If the first block has invisible `<br>` element for empty last line and +// joining it with the following block, the invisible trailing `<br>` element +// should be deleted and join the blocks. Therefore, the target range should +// contain the `<br>` element and block boundaries. But maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br><br></p><p>def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(def, 0); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abc<br>def</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p1, + startOffset: 2, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc<br><br></p><p>[]def</p>"'); + +// Deleting visible `<br>` element should be contained by a range of +// `getTargetRanges()`. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br>def</p>"; + let def = editor.querySelector("br").nextSibling; + selection.collapse(def, 0); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild, + startOffset: 1, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc<br>[]def</p>"'); + +// Deleting visible `<br>` element should be contained by a range of +// `getTargetRanges()`. However, when only the `<br>` element is selected, +// the range shouldn't start from nor end by surrounding text nodes? +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br>def</p>"; + selection.setBaseAndExtent(editor.firstChild, 1, editor.firstChild, 2); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild, + startOffset: 1, + endContainer: editor.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<p>abc{<br>}def</p>"'); + +// Joining parent block and child block should remove invisible preceding +// white-spaces of the child block and invisible leading white-spaces in +// the child block, and they should be contained in a range of +// `getTargetRanges()`, but maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<div>abc <p> def<br>ghi</p></div>"; + let p = editor.querySelector("p"); + let def = p.firstChild; + let abc = editor.firstChild.firstChild; + selection.collapse(def, 3); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<div>abcdef<p>ghi</p></div>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<div>abc <p> []def<br>ghi</p></div>"'); + +// Joining child block and parent block should remove invisible trailing +// white-spaces of the child block and invisible following white-spaces +// in the parent block, and they should be contained by a range of +// `getTaregetRanges()`, but maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<div><p>abc </p> def</div>"; + let abc = editor.querySelector("p").firstChild; + let def = editor.querySelector("p").nextSibling; + selection.collapse(def, 3); + await sendBackspaceKey(); + assert_equals(editor.innerHTML, "<div><p>abcdef</p></div>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInput(); +}, 'Backspace at "<div><p>abc </p> []def</div>"'); + +// The following tests check whether the range returned from +// `beforeinput[0].getTargetRanges()` is modified or different range is +// modified instead. I.e., they don't test which type of deletion should +// occur. Therefore, their result depends on browser's key bindings, +// system settings and running OS. + +function getFirstDifferentOffset(currentString, originalString) { + for (let i = 0; i < currentString.length; i++) { + if (currentString.charAt(i) !== originalString.charAt(i) && + (originalString.charAt(i) !== " " || !currentString.charAt("\u00A0"))) { + return i; + } + } + return currentString.length; +} + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kShift); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Shift + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kControl); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Control + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kAlt); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Alt + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc def".length); + await sendBackspaceKey(kMeta); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Meta + Backspace at "<p>abc def[] ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p> ${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kShift); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Shift + Backspace at "<p> abc[] def</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p> ${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kControl); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Control + Backspace at "<p> abc[] def</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p> ${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kAlt); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Alt + Backspace at "<p> abc[] def</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p> ${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc".length); + await sendBackspaceKey(kMeta); + let visibleText = p.firstChild.data.replace(/^\s+/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${invisibleWhiteSpaces + kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInput(); +}, 'Meta + Backspace at "<p> abc[] def</p>"'); + +</script> diff --git a/tests/wpt/web-platform-tests/input-events/input-events-get-target-ranges-forwarddelete.tentative.html b/tests/wpt/web-platform-tests/input-events/input-events-get-target-ranges-forwarddelete.tentative.html new file mode 100644 index 00000000000..3780324cf92 --- /dev/null +++ b/tests/wpt/web-platform-tests/input-events/input-events-get-target-ranges-forwarddelete.tentative.html @@ -0,0 +1,808 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>InputEvent.getTargetRanges() at Delete (forward delete)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<div contenteditable></div> +<script> +const kDeleteKey = "\uE017"; +const kShift = "\uE008"; +const kMeta = "\uE03d"; +const kControl = "\uE009"; +const kAlt = "\uE00A"; + +let selection = getSelection(); +let editor = document.querySelector("div[contenteditable]"); +let beforeinput = []; +let input = []; +editor.addEventListener("beforeinput", (e) => { + // NOTE: Blink makes `getTargetRanges()` return empty range after propagaion, + // but this test wants to check the result during propagation. + // Therefore, we need to cache the result, but will assert if + // `getTargetRanges()` returns different ranges after checking the + // cached ranges. + e.cachedRanges = e.getTargetRanges(); + beforeinput.push(e); +}); +editor.addEventListener("input", (e) => { + e.cachedRanges = e.getTargetRanges(); + input.push(e); +}); + +function reset() { + editor.focus(); + beforeinput = []; + input = []; +} + +function getRangeDescription(range) { + function getNodeDescription(node) { + if (!node) { + return "null"; + } + switch (node.nodeType) { + case Node.TEXT_NODE: + case Node.COMMENT_NODE: + case Node.CDATA_SECTION_NODE: + return `${node.nodeName} "${node.data}"`; + case Node.ELEMENT_NODE: + return `<${node.nodeName.toLowerCase()}>`; + default: + return `${node.nodeName}`; + } + } + if (range === null) { + return "null"; + } + if (range === undefined) { + return "undefined"; + } + return range.startContainer == range.endContainer && range.startOffset == range.endOffset + ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})` + : `(${getNodeDescription(range.startContainer)}, ${range.startOffset}) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`; +} + +function getArrayOfRangesDescription(arrayOfRanges) { + if (arrayOfRanges === null) { + return "null"; + } + if (arrayOfRanges === undefined) { + return "undefined"; + } + if (!Array.isArray(arrayOfRanges)) { + return "Unknown Object"; + } + if (arrayOfRanges.length === 0) { + return "[]"; + } + let result = "["; + for (let range of arrayOfRanges) { + result += `{${getRangeDescription(range)}},`; + } + result += "]"; + return result; +} + +function sendDeleteKey(modifier) { + if (!modifier) { + return new test_driver.Actions() + .keyDown(kDeleteKey) + .keyUp(kDeleteKey) + .send(); + } + return new test_driver.Actions() + .keyDown(modifier) + .keyDown(kDeleteKey) + .keyUp(kDeleteKey) + .keyUp(modifier) + .send(); +} + +function checkGetTargetRangesKeepReturningSameValue(event) { + // https://github.com/w3c/input-events/issues/114 + assert_equals(getArrayOfRangesDescription(event.getTargetRanges()), + getArrayOfRangesDescription(event.cachedRanges), + `${event.type}.getTargetRanges() should keep returning the same array of ranges even after its propagation finished`); +} + +function checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedRange) { + assert_equals(beforeinput.length, 1, + "One beforeinput event should be fired if the key operation deletes something"); + assert_true(Array.isArray(beforeinput[0].cachedRanges), + "beforeinput[0].getTargetRanges() should return an array of StaticRange instances during propagation"); + // Before checking the length of array of ranges, we should check the first + // range first because the first range data is more important than whether + // there are additional unexpected ranges. + if (beforeinput[0].cachedRanges.length > 0) { + assert_equals( + getRangeDescription(beforeinput[0].cachedRanges[0]), + getRangeDescription(expectedRange), + `beforeinput.getTargetRanges() should return expected range (inputType is "${beforeinput[0].inputType}")`); + assert_equals(beforeinput[0].cachedRanges.length, 1, + "beforeinput.getTargetRanges() should return one range within an array"); + } + assert_equals(beforeinput[0].cachedRanges, 1, + "One range should be returned from getTargetRanges() when the key operation deletes something"); + checkGetTargetRangesKeepReturningSameValue(beforeinput[0]); +} + +function checkGetTargetRangesOfInputOnDeleteSomething() { + assert_equals(input.length, 1, + "One input event should be fired if the key operation deletes something"); + // https://github.com/w3c/input-events/issues/113 + assert_true(Array.isArray(input[0].cachedRanges), + "input[0].getTargetRanges() should return an array of StaticRange instances during propagation"); + assert_equals(input[0].cachedRanges.length, 0, + "input[0].getTargetRanges() should return empty array during propagation"); + checkGetTargetRangesKeepReturningSameValue(input[0]); +} + +function checkBeforeinputAndInputEventsOnNOOP() { + assert_equals(beforeinput.length, 0, + "beforeinput event shouldn't be fired when the key operation does not cause modifying the DOM tree"); + assert_equals(input.length, 0, + "input event shouldn't be fired when the key operation does not cause modifying the DOM tree"); +} + +// Simply deletes the next ASCII character of caret position. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc</p>"; + selection.collapse(editor.firstChild.firstChild, 2); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>ab</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 2, + endContainer: editor.firstChild.firstChild, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>ab[]c</p>"'); + +// Simply deletes the next ASCII character of caret position. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc</p>"; + selection.collapse(editor.firstChild.firstChild, 1); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>ac</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 1, + endContainer: editor.firstChild.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>a[]bc</p>"'); + +// Simply deletes the next ASCII character of caret position. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc</p>"; + selection.collapse(editor.firstChild.firstChild, 0); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>bc</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 0, + endContainer: editor.firstChild.firstChild, + endOffset: 1, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>[]abc</p>"'); + +// Should delete the `<span>` element becase it becomes empty. +// However, we need discussion whether the `<span>` element should be +// contained by a range of `getTargetRanges()`. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>a<span>b</span>c</p>"; + let a = editor.querySelector("span").previousSibling; + selection.collapse(a, 1); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>ac</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: a, + startOffset: 1, + endContainer: editor.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>a[]<span>b</span>c</p>"'); + +// Should delete the `<span>` element becase it becomes empty. +// However, we need discussion whether the `<span>` element should be +// contained by a range of `getTargetRanges()`. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>a<span>b</span>c</p>"; + let b = editor.querySelector("span").firstChild; + selection.collapse(b, 0); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>ac</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild, + startOffset: 1, + endContainer: editor.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>a<span>[]b</span>c</p>"'); + +// Invisible trailing white-space may be deleted when the last visible +// character is deleted. If it's deleted, it should be contained by +// the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p>"; + selection.collapse(editor.firstChild.firstChild, 2); + await sendDeleteKey(); + assert_in_array(editor.innerHTML, ["<p>ab</p>", "<p>ab </p>"]); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild.firstChild, + startOffset: 2, + endContainer: editor.firstChild.firstChild, + endOffset: editor.firstChild.firstChild.data.length == 2 ? 4 : 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>ab[]c </p>"'); + +// Invisible trailing white-spaces in current block and invisible leading +// white-spaces in the following block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(abc, 3); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc[] </p><p> def</p>"'); + +// Invisible trailing white-spaces in current block and invisible leading +// white-spaces in the following block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(abc, 4); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc [] </p><p> def</p>"'); + +// Invisible trailing white-spaces in current block and invisible leading +// white-spaces in the following block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(abc, 5); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc [] </p><p> def</p>"'); + +// Invisible trailing white-spaces in current block and invisible leading +// white-spaces in the following block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(abc, 6); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc []</p><p> def</p>"'); + +// Invisible trailing white-spaces in current block and invisible leading +// white-spaces in the following block should be deleted for avoiding they +// becoming visible when the blocks are joined. Perhaps, they should be +// contained by the range of `getTargetRanges()`, but needs discussion. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><p> def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.setBaseAndExtent(abc, 6, def, 0); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc [</p><p>] def</p>"'); + +// Invisible leading white-spaces in the following block should be deleted +// for avoiding they becoming visible when the blocks are joined, but +// preformatted trailing white-spaces in the first block shouldn't be +// deleted. Perhaps, the invisible white-spaces should be contained by +// the range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<pre>abc </pre><p> def</p>"; + let pre = editor.firstChild; + let abc = pre.firstChild; + let p = pre.nextSibling; + let def = p.firstChild; + selection.collapse(abc, 6); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<pre>abc def</pre>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<pre>abc []</pre><p> def</p>"'); + +// Invisible leading/trailing white-spaces in the following block should be +// deleted for avoiding they becoming visible when the blocks are joined, but +// preformatted trailing white-spaces in the first block shouldn't be +// deleted. Perhaps, the invisible leading white-spaces should be contained +// by the range of `getTargetRanges()`, but needs discussion. +// And also not sure whether the trailing white-spaces should be contained +// by additional range of `getTargetRanges()` or not because of the +// implementation cost and runtime cost. Needs discuss. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<pre>abc </pre><p> def</p>"; + let pre = editor.firstChild; + let abc = pre.firstChild; + let p = pre.nextSibling; + let def = p.firstChild; + selection.collapse(abc, 6); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<pre>abc def</pre>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 6, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<pre>abc []</pre><p> def </p>"'); + +// Invisible trailing white-spaces in the first block should be deleted +// when the block is joined with the preformated following block, but +// the leading white-spaces in the preformatted block shouldn't be +// removed. So, in this case, the invisible trailing white-spaces should +// be in the range of `getTargetRanges()`, but not so for the preformatted +// visible leading white-spaces. But needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc </p><pre> def</pre>"; + let p = editor.firstChild; + let abc = p.firstChild; + let pre = p.nextSibling; + let def = pre.firstChild; + selection.collapse(abc, 3); + await sendDeleteKey(); + assert_in_array(editor.innerHTML, ["<p>abc def</p>", + "<p>abc def</p>", + "<p>abc def</p>", + "<p>abc def</p>"]); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc[] </p><pre> def</pre>"'); + +// Deleting from before invisible trailing `<br>` element of a block +// should delete the `<br>` element and join the blocks. Therefore, +// the target range should contain the `<br>` element and block boundaries. +// But maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br></p><p>def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(abc, 3); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc[]<br></p><p>def</p>"'); + +// Deleting from last empty line in the first block should delete the +// invisible `<br>` element for the last empty line and join the blocks. +// In this case, the invisible `<br>` element should be contained in the +// range of `getTargetRanges()`, but needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br><br></p><p>def</p>"; + let p1 = editor.firstChild; + let abc = p1.firstChild; + let p2 = p1.nextSibling; + let def = p2.firstChild; + selection.collapse(p1, 2); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abc<br>def</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p1, + startOffset: 2, + endContainer: def, + endOffset: 0, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc<br>{}<br></p><p>def</p>"'); + +// Deleting visible `<br>` element should be contained by a range of +// `getTargetRanges()`. +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br>def</p>"; + let abc = editor.firstChild.firstChild; + selection.collapse(abc, 3); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: editor.querySelector("p"), + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc[]<br>def</p>"'); + +// Deleting visible `<br>` element should be contained by a range of +// `getTargetRanges()`. However, when only the `<br>` element is selected, +// the range shouldn't start from nor end by surrounding text nodes? +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<p>abc<br>def</p>"; + selection.setBaseAndExtent(editor.firstChild, 1, editor.firstChild, 2); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<p>abcdef</p>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: editor.firstChild, + startOffset: 1, + endContainer: editor.firstChild, + endOffset: 2, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<p>abc{<br>}def</p>"'); + +// Joining parent block and child block should remove invisible preceding +// white-spaces of the child block and invisible leading white-spaces in +// the child block, and they should be contained in a range of +// `getTargetRanges()`, but maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<div>abc <p> def<br>ghi</p></div>"; + let p = editor.querySelector("p"); + let def = p.firstChild; + let abc = editor.firstChild.firstChild; + selection.collapse(abc, 3); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<div>abcdef<p>ghi</p></div>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<div>abc[] <p> def<br>ghi</p></div>"'); + +// Joining child block and parent block should remove invisible trailing +// white-spaces of the child block and invisible following white-spaces +// in the parent block, and they should be contained by a range of +// `getTaregetRanges()`, but maybe needs discussion. +// https://github.com/w3c/input-events/issues/112 +promise_test(async () => { + reset(); + editor.innerHTML = "<div><p>abc </p> def</div>"; + let abc = editor.querySelector("p").firstChild; + let def = editor.querySelector("p").nextSibling; + selection.collapse(abc, 3); + await sendDeleteKey(); + assert_equals(editor.innerHTML, "<div><p>abcdef</p></div>"); + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: abc, + startOffset: 3, + endContainer: def, + endOffset: 3, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Delete at "<div><p>abc[] </p> def</div>"'); + +// The following tests check whether the range returned from +// `beforeinput[0].getTargetRanges()` is modified or different range is +// modified instead. I.e., they don't test which type of deletion should +// occur. Therefore, their result depends on browser's key bindings, +// system settings and running OS. + +function getFirstDifferentOffset(currentString, originalString) { + for (let i = 0; i < currentString.length; i++) { + if (currentString.charAt(i) !== originalString.charAt(i) && + (originalString.charAt(i) !== " " || !currentString.charAt("\u00A0"))) { + return i; + } + } + return currentString.length; +} + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kShift); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Shift + Delete at "<p>abc []def ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kControl); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Control + Delete at "<p>abc []def ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kAlt); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Alt + Delete at "<p>abc []def ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def ghi"; + editor.innerHTML = `<p>${kText}</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kMeta); + let startOffset = getFirstDifferentOffset(p.firstChild.data, kText); + let length = kText.length - p.firstChild.data.length; + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length)}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Meta + Delete at "<p>abc []def ghi</p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p>${kText} </p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kShift); + let visibleText = p.firstChild.data.replace(/%s+$/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Shift + Delete at "<p>abc []def </p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p>${kText} </p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kControl); + let visibleText = p.firstChild.data.replace(/%s+$/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Control + Delete at "<p>abc []def </p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p>${kText} </p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kAlt); + let visibleText = p.firstChild.data.replace(/%s+$/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Alt + Delete at "<p>abc []def </p>"'); + +promise_test(async () => { + reset(); + const kText = "abc def"; + editor.innerHTML = `<p>${kText} s</p>`; + let p = editor.querySelector("p"); + selection.collapse(p.firstChild, "abc ".length); + await sendDeleteKey(kMeta); + let visibleText = p.firstChild.data.replace(/%s+$/, ""); + let invisibleWhiteSpaces = " ".repeat(p.firstChild.data.length - visibleText.length); + let startOffset = invisibleWhiteSpaces.length + getFirstDifferentOffset(visibleText, kText); + let length = kText.length + 3 - p.firstChild.data.length; + // If invisible white-spaces are deleted, they should be contained in the target range. + assert_equals(editor.innerHTML.replace(/ /g, " "), + `<p>${kText.substr(0, startOffset) + kText.substr(startOffset + length) + invisibleWhiteSpaces}</p>`); + if (startOffset === kText.length) { + checkBeforeinputAndInputEventsOnNOOP(); + return; + } + checkGetTargetRangesOfBeforeinputOnDeleteSomething({ + startContainer: p.firstChild, + startOffset: startOffset, + endContainer: p.firstChild, + endOffset: startOffset + length, + }); + checkGetTargetRangesOfInputOnDeleteSomething(); +}, 'Meta + Delete at "<p>abc []def</p>"'); + +</script> diff --git a/tests/wpt/web-platform-tests/interfaces/web-share.idl b/tests/wpt/web-platform-tests/interfaces/web-share.idl index c29a29d0b4e..b66bcd81fe9 100644 --- a/tests/wpt/web-platform-tests/interfaces/web-share.idl +++ b/tests/wpt/web-platform-tests/interfaces/web-share.idl @@ -8,7 +8,7 @@ partial interface Navigator { }; dictionary ShareData { - FrozenArray<File> files; + sequence<File> files; USVString title; USVString text; USVString url; diff --git a/tests/wpt/web-platform-tests/intersection-observer/observer-callback-arguments.html b/tests/wpt/web-platform-tests/intersection-observer/observer-callback-arguments.html new file mode 100644 index 00000000000..6e816969d00 --- /dev/null +++ b/tests/wpt/web-platform-tests/intersection-observer/observer-callback-arguments.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>IntersectionObserver: callback arguments</title> +<link rel="help" href="https://w3c.github.io/IntersectionObserver/#notify-intersection-observers-algo"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +"use strict"; + +async_test(t => { + const io = new IntersectionObserver(function(entries, observer) { + t.step(() => { + assert_equals(this, io); + assert_equals(arguments.length, 2); + assert_true(Array.isArray(entries)); + assert_equals(entries.length, 1); + assert_true(entries[0] instanceof IntersectionObserverEntry); + assert_equals(observer, io); + + io.disconnect(); + t.done(); + }); + }); + + io.observe(document.body); +}, "Callback is invoked with |this| value of IntersectionObserver and two arguments"); +</script> diff --git a/tests/wpt/web-platform-tests/largest-contentful-paint/invisible-images-composited-1.html b/tests/wpt/web-platform-tests/largest-contentful-paint/invisible-images-composited-1.html index 495645ab41e..7723d2f2bea 100644 --- a/tests/wpt/web-platform-tests/largest-contentful-paint/invisible-images-composited-1.html +++ b/tests/wpt/web-platform-tests/largest-contentful-paint/invisible-images-composited-1.html @@ -13,13 +13,17 @@ .displayNone { display: none; } - .composited { + .willChangeTransform { will-change: transform; } + .willChangeOpacity { + will-change: opacity; + } </style> -<img src='/images/blue.png' class='opacity0 composited' id='opacity0'/> -<img src='/images/green.png' class='visibilityHidden composited' id='visibilityHidden'/> -<img src='/images/red.png' class='displayNone composited' id='displayNone'/> +<img src='/images/blue.png' class='opacity0 willChangeTransform' id='opacity0-willChangeTransform'/> +<img src='/images/green.png' class='visibilityHidden willChangeTransform' id='visibilityHidden'/> +<img src='/images/red.png' class='displayNone willChangeTransform' id='displayNone'/> +<img src='/images/blue.png' class='opacity0 willChangeOpacity' id='opacity0-willChangeOpacity'/> <div class='opacity0 composited'><img src='/images/yellow.png' id='divOpacity0'/></div> <div class='visibilityHidden composited'><img src='/images/yellow.png' id='divVisibilityHidden'/></div> <div class='displayNone composited'><img src='/images/yellow.png' id='divDisplayNone'/></div> diff --git a/tests/wpt/web-platform-tests/layout-instability/child-shift-with-parent-overflow-hidden.html b/tests/wpt/web-platform-tests/layout-instability/child-shift-with-parent-overflow-hidden.html new file mode 100644 index 00000000000..d69e55a51b8 --- /dev/null +++ b/tests/wpt/web-platform-tests/layout-instability/child-shift-with-parent-overflow-hidden.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<title>Layout Instability: parent (with overflow:hidden) and child moved together</title> +<link rel="help" href="https://wicg.github.io/layout-instability/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/util.js"></script> +<div id="parent" style="position: relative; width: 200px; height: 200px; + border: 50px solid blue; overflow: hidden"> + <div id="child" style="height: 400px"></div> +</div> +<script> + +promise_test(async () => { + const watcher = new ScoreWatcher; + + // Wait for the initial render to complete. + await waitForAnimationFrames(2); + + // Modify the position of the div. + const parent = document.querySelector("#parent"); + parent.style.top = '100px'; + + // Only the parent area should be reported. + const expectedScore = computeExpectedScore(300 * (300 + 100), 100); + + // Observer fires after the frame is painted. + assert_equals(watcher.score, 0); + await watcher.promise; + assert_equals(watcher.score, expectedScore); +}, 'Parent (with overflow:hidden) and child moved together.'); + +</script> diff --git a/tests/wpt/web-platform-tests/layout-instability/child-shift-with-parent.html b/tests/wpt/web-platform-tests/layout-instability/child-shift-with-parent.html new file mode 100644 index 00000000000..7271af6d4a0 --- /dev/null +++ b/tests/wpt/web-platform-tests/layout-instability/child-shift-with-parent.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title>Layout Instability: parent/child moved together</title> +<link rel="help" href="https://wicg.github.io/layout-instability/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/util.js"></script> +<div id="parent" style="position: relative; width: 100px; height: 100px; border: 100px solid blue"> + <div id="child" style="height: 300px"></div> +</div> +<script> + +promise_test(async () => { + const watcher = new ScoreWatcher; + + // Wait for the initial render to complete. + await waitForAnimationFrames(2); + + // Modify the position of the div. + const parent = document.querySelector("#parent"); + parent.style.top = '100px'; + + // If the implementation reports child and parent separately + // (overlapping are should be excluded): + const expectedScoreMin = computeExpectedScore(300 * (300 + 100) + 100 * 100, 100); + // If the implementation reports parent bounding box (including child): + const expectedScoreMax = computeExpectedScore(300 * (400 + 100), 100); + + // Observer fires after the frame is painted. + assert_equals(watcher.score, 0); + await watcher.promise; + assert_between_inclusive(watcher.score, expectedScoreMin, expectedScoreMax); +}, 'Parent/child movement.'); + +</script> diff --git a/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift-vertical-rl.html b/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift-vertical-rl.html index 06bc34c37dd..a89cad145b2 100644 --- a/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift-vertical-rl.html +++ b/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift-vertical-rl.html @@ -1,5 +1,5 @@ <!DOCTYPE html> -<title>Layout Instability: simple block movement is detected</title> +<title>Layout Instability: vertical-rl inline/text movement is detected</title> <link rel="help" href="https://wicg.github.io/layout-instability/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -38,7 +38,7 @@ promise_test(async () => { assert_equals(watcher.score, 0); await watcher.promise; assert_between_exclusive(watcher.score, expectedScoreMin, expectedScoreMax); -}, 'Inline flow movement.'); +}, 'Vertical-rl inline flow movement.'); </script> </body> diff --git a/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift.html b/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift.html index 39550da6588..0385f29c2f9 100644 --- a/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift.html +++ b/tests/wpt/web-platform-tests/layout-instability/inline-flow-shift.html @@ -1,5 +1,5 @@ <!DOCTYPE html> -<title>Layout Instability: simple block movement is detected</title> +<title>Layout Instability: inline/text movement is detected</title> <link rel="help" href="https://wicg.github.io/layout-instability/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> diff --git a/tests/wpt/web-platform-tests/origin-isolation/resources/child-frame-script.mjs b/tests/wpt/web-platform-tests/origin-isolation/resources/child-frame-script.mjs index 2c6760a3e37..783e36092da 100644 --- a/tests/wpt/web-platform-tests/origin-isolation/resources/child-frame-script.mjs +++ b/tests/wpt/web-platform-tests/origin-isolation/resources/child-frame-script.mjs @@ -30,6 +30,14 @@ window.onmessage = async (e) => { } catch (e) { parent.postMessage(e.name, "*"); } + } else if (e.data.command === "access frameElement") { + if (frameElement === null) { + parent.postMessage("null", "*"); + } else if (frameElement?.constructor?.name === "HTMLIFrameElement") { + parent.postMessage("frameElement accessed successfully", "*"); + } else { + parent.postMessage("something wierd happened", "*"); + } } else if (e.data.command === "get originIsolationRestricted") { parent.postMessage(self.originIsolationRestricted, "*"); } diff --git a/tests/wpt/web-platform-tests/origin-isolation/resources/helpers.mjs b/tests/wpt/web-platform-tests/origin-isolation/resources/helpers.mjs index e8425c240b5..73f9406b476 100644 --- a/tests/wpt/web-platform-tests/origin-isolation/resources/helpers.mjs +++ b/tests/wpt/web-platform-tests/origin-isolation/resources/helpers.mjs @@ -79,12 +79,18 @@ export function testSameAgentCluster(testFrames, testLabelPrefix) { promise_test(async () => { const frameWindow = frames[testFrames[1]]; + const frameElement = document.querySelectorAll("iframe")[testFrames[1]]; // Must not throw frameWindow.document; // Must not throw frameWindow.location.href; + + assert_not_equals(frameElement.contentDocument, null, "contentDocument"); + + const whatHappened = await accessFrameElement(frameWindow); + assert_equals(whatHappened, "frameElement accessed successfully"); }, `${prefix}setting document.domain must give sync access`); } else { // Between the two children at the index given by testFrames[0] and @@ -101,6 +107,9 @@ export function testSameAgentCluster(testFrames, testLabelPrefix) { const whatHappened2 = await accessLocationHrefBetween(testFrames); assert_equals(whatHappened2, "accessed location.href successfully"); + + // We don't test contentDocument/frameElement for these because accessing + // those via siblings has to go through the parent anyway. }, `${prefix}setting document.domain must give sync access`); } } @@ -130,13 +139,20 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) { promise_test(async () => { const frameWindow = frames[testFrames[1]]; + const frameElement = document.querySelectorAll("iframe")[testFrames[1]]; assert_throws_dom("SecurityError", DOMException, () => { frameWindow.document; }); + assert_throws_dom("SecurityError", DOMException, () => { frameWindow.location.href; }); + + assert_equals(frameElement.contentDocument, null, "contentDocument"); + + const whatHappened = await accessFrameElement(frameWindow); + assert_equals(whatHappened, "null"); }, `${prefix}setting document.domain must not give sync access`); } else { // Between the two children at the index given by testFrames[0] and @@ -153,6 +169,9 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) { const whatHappened2 = await accessLocationHrefBetween(testFrames); assert_equals(whatHappened2, "SecurityError"); + + // We don't test contentDocument/frameElement for these because accessing + // those via siblings has to go through the parent anyway. }, `${prefix}setting document.domain must not give sync access`); } } @@ -263,6 +282,11 @@ async function accessLocationHrefBetween(testFrames) { return waitForMessage(sourceFrame); } +async function accessFrameElement(frameWindow) { + frameWindow.postMessage({ command: "access frameElement" }, "*"); + return waitForMessage(frameWindow); +} + function waitForMessage(expectedSource) { return new Promise(resolve => { const handler = e => { diff --git a/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html b/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html index 4388b2db308..88d03b7965e 100644 --- a/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html +++ b/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_compat-mouse-events-when-removing-nodes.html @@ -64,18 +64,16 @@ function createGreenBoxAndAddListeners() { function performActions() { var rect = document.getElementById("green").getBoundingClientRect(); - var x1 = rect.left + 5; - var y1 = rect.top + 5; + var x1 = Math.ceil(rect.left + 5); + var y1 = Math.ceil(rect.top + 5); return new test_driver.Actions() .pointerMove(0, 0) - .pointerMove(rect.left + 5, rect.top + 5) - .pointerDown(0) - .pointerUp(0) + .pointerMove(x1, y1) + .pointerDown() + .pointerUp() .send() - .then( () => { - return resolveWhen( () => receivedEvents.length == 6 ); - }); + .then( () => resolveWhen( () => receivedEvents.length == 6 ) ); } function testScenario(targetEvent, expectedEvents, description) { diff --git a/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-on-object.html b/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-on-object.html index d8912a2652b..fa0e97ff30c 100644 --- a/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-on-object.html +++ b/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-on-object.html @@ -39,10 +39,13 @@ promise_test(async() => { done_clicked = false; receivedEvents = []; - await new test_driver.Actions().pointerMove(rect.left+5, rect.top+5).pointerDown(0).pointerUp(0).send().then(() => { - return clickInTarget("mouse", done); - }); - await resolveWhen(()=>{return done_clicked;}); + await new test_driver.Actions() + .pointerMove(Math.ceil(rect.left+5), Math.ceil(rect.top+5)) + .pointerDown() + .pointerUp() + .send() + .then(() => clickInTarget("mouse", done)); + await resolveWhen(() => done_clicked); assert_array_equals(receivedEvents, ["pointermove", "mousemove", "pointerdown", "mousedown", "pointerup", "mouseup"], "Click on object should result in the correct sequence of events"); @@ -52,10 +55,14 @@ promise_test(async() => { done_clicked = false; receivedEvents = []; - await new test_driver.Actions().pointerMove(rect.left+5, rect.top+5).pointerDown(0).pointerMove(rect.left-5, rect.top-5).pointerUp(0).send().then(() => { - return clickInTarget("mouse", done); - }); - await resolveWhen(()=>{return done_clicked;}); + await new test_driver.Actions() + .pointerMove(Math.ceil(rect.left+5), Math.ceil(rect.top+5)) + .pointerDown() + .pointerMove(Math.ceil(rect.left-5), Math.ceil(rect.top-5)) + .pointerUp() + .send() + .then(() => clickInTarget("mouse", done)); + await resolveWhen(() => done_clicked); assert_array_equals(receivedEvents, ["pointermove", "mousemove", "pointerdown", "mousedown", "pointermove", "mousemove", "pointerup", "mouseup"], "Drag from object should result in the correct sequence of events"); diff --git a/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html b/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html new file mode 100644 index 00000000000..a727325a5b1 --- /dev/null +++ b/tests/wpt/web-platform-tests/pointerevents/compat/pointerevent_mouse-pointer-preventdefault.html @@ -0,0 +1,199 @@ +<!DOCTYPE HTML> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script type="text/javascript" src="../pointerevent_support.js"></script> +<style> +div.box { + margin: 10px; + padding: 50px; + float: left; +} +</style> +<h1>Verifies the effect of pointer event prevent-default on following pointer & mouse events.</h1> +<div id="target" class="box" style="background-color:green;"> +</div> + +<script> +var receivedEvents = []; +var mouseEventList = ["mousedown", "mouseup", "mouseenter", "mouseleave", "mouseover", "mouseout", "mousemove"]; +var pointerEventList = ["pointerdown", "pointerup", "pointerenter", "pointerleave", "pointerover", "pointerout", "pointermove"]; + +var preventDefaultList = ["none"].concat(pointerEventList); +var eventToPreventDefault = ""; +var seqNo = 0; + +function init() { + var targetDiv = document.getElementById("target"); + + mouseEventList.forEach(function(eventName) { + targetDiv.addEventListener(eventName, function(event) { + ++seqNo; + receivedEvents.push(`${seqNo}.${eventName}@target`); + }); + }); + + pointerEventList.forEach(function(eventName) { + targetDiv.addEventListener(eventName, function(event) { + var preventDefaultMsg = ""; + if (event.type == eventToPreventDefault) { + event.preventDefault(); + preventDefaultMsg = "_prevented_default"; + } + ++seqNo; + receivedEvents.push(`${seqNo}.${eventName}@target${preventDefaultMsg}`); + }); + }); +} + +var expectedEvents = new Map([ + ["none", ["1.pointerover@target", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target", "14.mousemove@target", "15.pointerout@target", + "16.pointerleave@target", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target", "20.pointerenter@target", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target", "24.mousemove@target", + "25.pointerup@target", "26.mouseup@target", "27.pointerout@target", + "28.pointerleave@target", "29.mouseout@target", "30.mouseleave@target" + ]], + ["pointerdown", ["1.pointerover@target", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target_prevented_default", "8.pointerup@target", + "9.pointerdown@target_prevented_default", + "10.pointermove@target", "11.pointerout@target", "12.pointerleave@target", + "13.mouseout@target", "14.mouseleave@target", "15.pointerover@target", + "16.pointerenter@target", "17.mouseover@target", "18.mouseenter@target", + "19.pointermove@target", "20.pointerup@target", "21.pointerout@target", + "22.pointerleave@target", "23.mouseout@target", "24.mouseleave@target" + ]], + ["pointerup", ["1.pointerover@target", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target_prevented_default", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target", "14.mousemove@target", "15.pointerout@target", + "16.pointerleave@target", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target", "20.pointerenter@target", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target", "24.mousemove@target", + "25.pointerup@target_prevented_default", "26.mouseup@target", "27.pointerout@target", + "28.pointerleave@target", "29.mouseout@target", "30.mouseleave@target"]], + ["pointerenter", ["1.pointerover@target", "2.pointerenter@target_prevented_default", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target", "14.mousemove@target", "15.pointerout@target", + "16.pointerleave@target", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target", "20.pointerenter@target_prevented_default", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target", "24.mousemove@target", + "25.pointerup@target", "26.mouseup@target", "27.pointerout@target", + "28.pointerleave@target", "29.mouseout@target", "30.mouseleave@target"]], + ["pointerleave", ["1.pointerover@target", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target", "14.mousemove@target", "15.pointerout@target", + "16.pointerleave@target_prevented_default", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target", "20.pointerenter@target", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target", "24.mousemove@target", + "25.pointerup@target", "26.mouseup@target", "27.pointerout@target", + "28.pointerleave@target_prevented_default", "29.mouseout@target", "30.mouseleave@target"]], + ["pointerover", ["1.pointerover@target_prevented_default", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target", "14.mousemove@target", "15.pointerout@target", + "16.pointerleave@target", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target_prevented_default", "20.pointerenter@target", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target", "24.mousemove@target", + "25.pointerup@target", "26.mouseup@target", "27.pointerout@target", + "28.pointerleave@target", "29.mouseout@target", "30.mouseleave@target"]], + ["pointerout", ["1.pointerover@target", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target", "14.mousemove@target", "15.pointerout@target_prevented_default", + "16.pointerleave@target", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target", "20.pointerenter@target", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target", "24.mousemove@target", + "25.pointerup@target", "26.mouseup@target", "27.pointerout@target_prevented_default", + "28.pointerleave@target", "29.mouseout@target", "30.mouseleave@target"]], + ["pointermove", ["1.pointerover@target", "2.pointerenter@target", "3.mouseover@target", + "4.mouseenter@target", "5.pointermove@target_prevented_default", "6.mousemove@target", + "7.pointerdown@target", "8.mousedown@target", "9.pointerup@target", + "10.mouseup@target", "11.pointerdown@target", "12.mousedown@target", + "13.pointermove@target_prevented_default", "14.mousemove@target", "15.pointerout@target", + "16.pointerleave@target", "17.mouseout@target", "18.mouseleave@target", + "19.pointerover@target", "20.pointerenter@target", "21.mouseover@target", + "22.mouseenter@target", "23.pointermove@target_prevented_default", "24.mousemove@target", + "25.pointerup@target", "26.mouseup@target", "27.pointerout@target", + "28.pointerleave@target", "29.mouseout@target", "30.mouseleave@target"]] +]); + +async function runTestForDefaultEvent(preventDefaultEvent) { + promise_test((test)=> + new Promise(async (resolve, reject)=>{ + test.add_cleanup(()=>{ + receivedEvents = []; + seqNo = 0; + eventToPreventDefault = ""; + }); + eventToPreventDefault = preventDefaultEvent; + + try{ + // if awaited Promise rejects then fail the test + await performActions(test, preventDefaultEvent); + } + catch(e){ + reject(e); + } + test.step(()=>{ + assert_array_equals(expectedEvents.get(preventDefaultEvent), receivedEvents);}); + resolve(); + }), `Verifies the effect of pointer event prevent-default on ${preventDefaultEvent}`); +} + +async function runTests(){ + // create one test for each event in preventDefaultList + // run all tests one by one + preventDefaultList.forEach(preventDefaultEvent=>runTestForDefaultEvent(preventDefaultEvent)); +} + +function performActions(test, preventDefaultEvent){ + let targetDiv = document.getElementById("target"); + let rect = targetDiv.getBoundingClientRect(); + let x1 = Math.ceil(rect.left - 3); + let y1 = Math.ceil(rect.top - 5); + let x2 = Math.ceil(rect.left + 3); + let y2 = Math.ceil(rect.top + 5); + + let eventWatcher = new EventWatcher(test, targetDiv, ["mouseleave"], ()=>waitForAnimationFrames(900)); + // wait for all expected events to happen. + // Because each test ends with mouseleave and there are 2 mouseleave events, + // we use EventWatcher to wait for the two mouseleave events to happen + let donePromise = eventWatcher.wait_for(["mouseleave", "mouseleave"], { record: 'all' }); + let actionsPromise = new test_driver.Actions() + .pointerMove(0, 0) + // start with mouse outside target + .pointerMove(x1, y1) + // move into target and click + .pointerMove(x2, y2) + .pointerDown() + .pointerUp() + // drag out of and into target & release within target + .pointerDown() + .pointerMove(x2+5, y2+5) + .pointerMove(x1, y1) + .pointerMove(x2, y2) + .pointerUp() + // move outside target again + .pointerMove(x1, y1) + .send(); + return Promise.all([donePromise, actionsPromise]); +} + +init(); +runTests(); +</script> diff --git a/tests/wpt/web-platform-tests/streams/readable-streams/tee.any.js b/tests/wpt/web-platform-tests/streams/readable-streams/tee.any.js index b6a95368aa5..55d4532ff83 100644 --- a/tests/wpt/web-platform-tests/streams/readable-streams/tee.any.js +++ b/tests/wpt/web-platform-tests/streams/readable-streams/tee.any.js @@ -317,6 +317,57 @@ promise_test(t => { }, 'ReadableStream teeing: erroring the original should immediately error the branches'); +promise_test(async t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const [reader1, reader2] = rs.tee().map(branch => branch.getReader()); + const cancelPromise = reader2.cancel(); + + controller.enqueue('a'); + + const read1 = await reader1.read(); + assert_object_equals(read1, { value: 'a', done: false }, 'first read() from branch1 should fulfill with the chunk'); + + controller.close(); + + const read2 = await reader1.read(); + assert_object_equals(read2, { value: undefined, done: true }, 'second read() from branch1 should be done'); + + await Promise.all([ + reader1.closed, + cancelPromise + ]); + +}, 'ReadableStream teeing: canceling branch1 should finish when branch2 reads until end of stream'); + +promise_test(async t => { + + let controller; + const theError = { name: 'boo!' }; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const [reader1, reader2] = rs.tee().map(branch => branch.getReader()); + const cancelPromise = reader2.cancel(); + + controller.error(theError); + + await Promise.all([ + promise_rejects_exactly(t, theError, reader1.read()), + cancelPromise + ]); + +}, 'ReadableStream teeing: canceling branch1 should finish when original stream errors'); + test(t => { // Copy original global. diff --git a/tests/wpt/web-platform-tests/svg/types/scripted/SVGGeometryElement.getPointAtLength-03.svg b/tests/wpt/web-platform-tests/svg/types/scripted/SVGGeometryElement.getPointAtLength-03.svg index 61737efb7da..8d3f7e36123 100644 --- a/tests/wpt/web-platform-tests/svg/types/scripted/SVGGeometryElement.getPointAtLength-03.svg +++ b/tests/wpt/web-platform-tests/svg/types/scripted/SVGGeometryElement.getPointAtLength-03.svg @@ -8,7 +8,6 @@ <script><![CDATA[ test(function() { var pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path"); - pathElement.setAttribute("d", 'M0,20 L400,20 L640,20'); assert_throws_dom("InvalidStateError", function() { pathElement.getPointAtLength(700) }); }, document.title + " with SVGPathElement"); diff --git a/tests/wpt/web-platform-tests/tools/ci/run_tc.py b/tests/wpt/web-platform-tests/tools/ci/run_tc.py index a5a8ca7db29..3a3b0d8090b 100755 --- a/tests/wpt/web-platform-tests/tools/ci/run_tc.py +++ b/tests/wpt/web-platform-tests/tools/ci/run_tc.py @@ -141,7 +141,7 @@ def install_certificates(): def install_chrome(channel): - if channel in ("experimental", "dev", "nightly"): + if channel in ("experimental", "dev"): deb_archive = "google-chrome-unstable_current_amd64.deb" elif channel == "beta": deb_archive = "google-chrome-beta_current_amd64.deb" @@ -323,7 +323,10 @@ def setup_environment(args): if "chrome" in args.browser: assert args.channel is not None - install_chrome(args.channel) + # Chrome Nightly will be installed via `wpt run --install-browser` + # later in taskcluster-run.py. + if args.channel != "nightly": + install_chrome(args.channel) elif "webkitgtk_minibrowser" in args.browser: assert args.channel is not None install_webkitgtk(args.channel) diff --git a/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py b/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py index 61d05689287..245ee7fa592 100755 --- a/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py +++ b/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py @@ -8,19 +8,19 @@ import shutil import subprocess import sys -browser_specific_args = { - "servo": ["--install-browser", "--processes=12"] -} - -def get_browser_args(product): +def get_browser_args(product, channel): if product == "firefox": local_binary = os.path.expanduser(os.path.join("~", "build", "firefox", "firefox")) if os.path.exists(local_binary): return ["--binary=%s" % local_binary] print("WARNING: Local firefox binary not found") return ["--install-browser", "--install-webdriver"] - return browser_specific_args.get(product, []) + if product == "servo": + return ["--install-browser", "--processes=12"] + if product == "chrome" and channel == "nightly": + return ["--install-browser", "--install-webdriver"] + return [] def find_wptreport(args): @@ -43,7 +43,7 @@ def gzip_file(filename, delete_original=True): os.unlink(filename) -def main(product, commit_range, wpt_args): +def main(product, channel, commit_range, wpt_args): """Invoke the `wpt run` command according to the needs of the Taskcluster continuous integration service.""" @@ -75,7 +75,7 @@ def main(product, commit_range, wpt_args): "--no-headless", "--verify-log-full" ] - wpt_args += get_browser_args(product) + wpt_args += get_browser_args(product, channel) # Hack to run servo with one process only for wdspec if product == "servo" and "--test-type=wdspec" in wpt_args: @@ -84,6 +84,9 @@ def main(product, commit_range, wpt_args): command = ["python", "./wpt", "run"] + wpt_args + [product] logger.info("Executing command: %s" % " ".join(command)) + with open("/home/test/artifacts/checkrun.md", "a") as f: + f.write("\n**WPT Command:** `%s`\n\n" % " ".join(command)) + retcode = subprocess.call(command, env=dict(os.environ, TERM="dumb")) if retcode != 0: sys.exit(retcode) @@ -104,6 +107,8 @@ if __name__ == "__main__": determine the list of test to execute""") parser.add_argument("product", action="store", help="Browser to run tests in") + parser.add_argument("channel", action="store", + help="Channel of the browser") parser.add_argument("wpt_args", nargs="*", help="Arguments to forward to `wpt run` command") main(**vars(parser.parse_args())) diff --git a/tests/wpt/web-platform-tests/tools/ci/tc/decision.py b/tests/wpt/web-platform-tests/tools/ci/tc/decision.py index 0820a6798b5..f52f5b093f0 100644 --- a/tests/wpt/web-platform-tests/tools/ci/tc/decision.py +++ b/tests/wpt/web-platform-tests/tools/ci/tc/decision.py @@ -241,6 +241,8 @@ def create_tc_task(event, task, taskgroup_id, depends_on_ids, env_extra=None): }, "routes": ["checks"] } + if "extra" in task: + task_data["extra"].update(task["extra"]) if env_extra: task_data["payload"]["env"].update(env_extra) if depends_on_ids: diff --git a/tests/wpt/web-platform-tests/tools/ci/tc/github_checks_output.py b/tests/wpt/web-platform-tests/tools/ci/tc/github_checks_output.py new file mode 100644 index 00000000000..16d39900298 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/ci/tc/github_checks_output.py @@ -0,0 +1,29 @@ +class GitHubChecksOutputter(object): + """Provides a method to output data to be shown in the GitHub Checks UI. + + This can be useful to provide a summary of a given check (e.g. the lint) + to enable developers to quickly understand what has gone wrong. The output + supports markdown format. + + See https://docs.taskcluster.net/docs/reference/integrations/github/checks#custom-text-output-in-checks + """ + def __init__(self, path): + self.path = path + + def output(self, line): + with open(self.path, 'a') as f: + f.write(line) + f.write('\n') + + +__outputter = None +def get_gh_checks_outputter(kwargs): + """Return the outputter for GitHub Checks output, if enabled. + + :param kwargs: The arguments passed to the program (to look for the + github_checks_text_file field) + """ + global __outputter + if kwargs['github_checks_text_file'] and __outputter is None: + __outputter = GitHubChecksOutputter(kwargs['github_checks_text_file']) + return __outputter diff --git a/tests/wpt/web-platform-tests/tools/ci/tc/tasks/test.yml b/tests/wpt/web-platform-tests/tools/ci/tc/tasks/test.yml index dd14fc71b91..b1534c9d230 100644 --- a/tests/wpt/web-platform-tests/tools/ci/tc/tasks/test.yml +++ b/tests/wpt/web-platform-tests/tools/ci/tc/tasks/test.yml @@ -10,6 +10,13 @@ components: public/results: path: /home/test/artifacts type: directory + extra: + github: + customCheckRun: + # We set both textArtifactName and annotationsArtifactName due + # to https://github.com/taskcluster/taskcluster/issues/3191 + textArtifactName: public/results/checkrun.md + annotationsArtifactName: public/results/checkrun.md wpt-testharness: chunks: 16 @@ -52,6 +59,7 @@ components: command: >- ./tools/ci/taskcluster-run.py ${vars.browser} + ${vars.channel} -- --channel=${vars.channel} --log-wptreport=../artifacts/wpt_report.json @@ -60,6 +68,7 @@ components: --this-chunk=${chunks.id} --total-chunks=${chunks.total} --test-type=${vars.suite} + trigger-master: trigger: branch: @@ -164,6 +173,13 @@ tasks: - trigger-daily - trigger-push - vars: + # Chromium ToT + browser: chrome + channel: nightly + use: + - trigger-daily + - trigger-push + - vars: browser: chrome channel: dev use: @@ -239,6 +255,13 @@ tasks: - trigger-daily - trigger-push - vars: + # Chromium ToT + browser: chrome + channel: nightly + use: + - trigger-daily + - trigger-push + - vars: browser: chrome channel: dev use: @@ -292,10 +315,12 @@ tasks: ./tools/ci/taskcluster-run.py --commit-range base_head ${vars.browser} + ${vars.channel} -- --channel=${vars.channel} --verify --verify-no-chaos-mode + --github-checks-text-file="/home/test/artifacts/checkrun.md" - wpt-${vars.browser}-${vars.channel}-results: use: @@ -311,6 +336,7 @@ tasks: ./tools/ci/taskcluster-run.py --commit-range base_head ${vars.browser} + ${vars.channel} -- --channel=${vars.channel} --no-fail-on-unexpected @@ -333,6 +359,7 @@ tasks: ./tools/ci/taskcluster-run.py --commit-range task_head ${vars.browser} + ${vars.channel} -- --channel=${vars.channel} --no-fail-on-unexpected diff --git a/tests/wpt/web-platform-tests/tools/ci/tc/tests/test_valid.py b/tests/wpt/web-platform-tests/tools/ci/tc/tests/test_valid.py index 88ffdb29511..472f83f86a0 100644 --- a/tests/wpt/web-platform-tests/tools/ci/tc/tests/test_valid.py +++ b/tests/wpt/web-platform-tests/tools/ci/tc/tests/test_valid.py @@ -128,7 +128,7 @@ def test_verify_payload(): 'wpt-firefox-nightly-print-reftest-1', 'wpt-chrome-dev-print-reftest-1', 'lint']), - ("pr_event.json", True, {".taskcluster.yml",".travis.yml","tools/ci/start.sh"}, + ("pr_event.json", True, {".taskcluster.yml", ".travis.yml", "tools/ci/start.sh"}, ['lint', 'tools/ unittests (Python 2)', 'tools/ unittests (Python 3.6)', @@ -171,6 +171,22 @@ def test_verify_payload(): 'wpt-firefox-stable-testharness-14', 'wpt-firefox-stable-testharness-15', 'wpt-firefox-stable-testharness-16', + 'wpt-chrome-nightly-testharness-1', + 'wpt-chrome-nightly-testharness-2', + 'wpt-chrome-nightly-testharness-3', + 'wpt-chrome-nightly-testharness-4', + 'wpt-chrome-nightly-testharness-5', + 'wpt-chrome-nightly-testharness-6', + 'wpt-chrome-nightly-testharness-7', + 'wpt-chrome-nightly-testharness-8', + 'wpt-chrome-nightly-testharness-9', + 'wpt-chrome-nightly-testharness-10', + 'wpt-chrome-nightly-testharness-11', + 'wpt-chrome-nightly-testharness-12', + 'wpt-chrome-nightly-testharness-13', + 'wpt-chrome-nightly-testharness-14', + 'wpt-chrome-nightly-testharness-15', + 'wpt-chrome-nightly-testharness-16', 'wpt-chrome-stable-testharness-1', 'wpt-chrome-stable-testharness-2', 'wpt-chrome-stable-testharness-3', @@ -224,6 +240,11 @@ def test_verify_payload(): 'wpt-firefox-stable-reftest-3', 'wpt-firefox-stable-reftest-4', 'wpt-firefox-stable-reftest-5', + 'wpt-chrome-nightly-reftest-1', + 'wpt-chrome-nightly-reftest-2', + 'wpt-chrome-nightly-reftest-3', + 'wpt-chrome-nightly-reftest-4', + 'wpt-chrome-nightly-reftest-5', 'wpt-chrome-stable-reftest-1', 'wpt-chrome-stable-reftest-2', 'wpt-chrome-stable-reftest-3', @@ -241,6 +262,8 @@ def test_verify_payload(): 'wpt-servo-nightly-reftest-5', 'wpt-firefox-stable-wdspec-1', 'wpt-firefox-stable-wdspec-2', + 'wpt-chrome-nightly-wdspec-1', + 'wpt-chrome-nightly-wdspec-2', 'wpt-chrome-stable-wdspec-1', 'wpt-chrome-stable-wdspec-2', 'wpt-webkitgtk_minibrowser-nightly-wdspec-1', @@ -248,11 +271,13 @@ def test_verify_payload(): 'wpt-servo-nightly-wdspec-1', 'wpt-servo-nightly-wdspec-2', 'wpt-firefox-stable-crashtest-1', + 'wpt-chrome-nightly-crashtest-1', 'wpt-chrome-stable-crashtest-1', 'wpt-webkitgtk_minibrowser-nightly-crashtest-1', 'wpt-servo-nightly-crashtest-1', 'wpt-firefox-stable-print-reftest-1', - 'wpt-chrome-stable-print-reftest-1',]) + 'wpt-chrome-nightly-print-reftest-1', + 'wpt-chrome-stable-print-reftest-1']) ]) def test_schedule_tasks(event_path, is_pr, files_changed, expected): with mock.patch("tools.ci.tc.decision.get_fetch_rev", return_value=(None, None, None)): diff --git a/tests/wpt/web-platform-tests/tools/serve/commands.json b/tests/wpt/web-platform-tests/tools/serve/commands.json index a5457b55a33..ed1d72e60ec 100644 --- a/tests/wpt/web-platform-tests/tools/serve/commands.json +++ b/tests/wpt/web-platform-tests/tools/serve/commands.json @@ -12,7 +12,6 @@ "parser": "get_parser", "help": "Run wptserve server for WAVE", "virtualenv": true, - "install": ["ua-parser"], "requirements": ["../wave/requirements.txt"] } } diff --git a/tests/wpt/web-platform-tests/tools/serve/test_functional.py b/tests/wpt/web-platform-tests/tools/serve/test_functional.py index 894479d111c..5a918c2ee47 100644 --- a/tests/wpt/web-platform-tests/tools/serve/test_functional.py +++ b/tests/wpt/web-platform-tests/tools/serve/test_functional.py @@ -9,6 +9,7 @@ try: import Queue as queue # noqa: N813 except ImportError: import queue +import sys import tempfile import threading @@ -45,6 +46,8 @@ def tempfile_name(): os.remove(name) +@pytest.mark.skipif(sys.version_info >= (3, 8) and sys.platform == 'darwin', + reason="multiprocessing test hangs in Python 3.8 on macOS (#24880)") def test_subprocess_exit(server_subprocesses, tempfile_name): timeout = 30 diff --git a/tests/wpt/web-platform-tests/tools/wpt/commands.json b/tests/wpt/web-platform-tests/tools/wpt/commands.json index a47ab40d728..51f66d0decb 100644 --- a/tests/wpt/web-platform-tests/tools/wpt/commands.json +++ b/tests/wpt/web-platform-tests/tools/wpt/commands.json @@ -6,7 +6,6 @@ "help": "Run tests in a browser", "virtualenv": true, "install": [ - "requests", "zstandard" ], "requirements": [ @@ -25,9 +24,6 @@ "parser": "create_parser_update", "help": "Update expectations files from raw logs.", "virtualenv": true, - "install": [ - "requests" - ], "requirements": [ "../wptrunner/requirements.txt" ] @@ -51,6 +47,7 @@ "script": "run", "parser": "get_parser", "help": "Install browser components", + "virtualenv": true, "install": [ "mozinstall" ] diff --git a/tests/wpt/web-platform-tests/tools/wpt/tests/test_wpt.py b/tests/wpt/web-platform-tests/tools/wpt/tests/test_wpt.py index b5ae79d1311..0b6aff42d57 100644 --- a/tests/wpt/web-platform-tests/tools/wpt/tests/test_wpt.py +++ b/tests/wpt/web-platform-tests/tools/wpt/tests/test_wpt.py @@ -188,6 +188,8 @@ def test_run_zero_tests(): @pytest.mark.slow @pytest.mark.remote_network +@pytest.mark.skipif(sys.version_info >= (3, 8) and sys.platform == 'darwin', + reason="multiprocessing test hangs in Python 3.8 on macOS (#24880)") def test_run_failing_test(): """Failing tests should be reported with a non-zero exit status unless the `--no-fail-on-unexpected` option has been specified.""" @@ -211,6 +213,8 @@ def test_run_failing_test(): @pytest.mark.slow @pytest.mark.remote_network +@pytest.mark.skipif(sys.version_info >= (3, 8) and sys.platform == 'darwin', + reason="multiprocessing test hangs in Python 3.8 on macOS (#24880)") def test_run_verify_unstable(temp_test): """Unstable tests should be reported with a non-zero exit status. Stable tests should be reported with a zero exit status.""" diff --git a/tests/wpt/web-platform-tests/tools/wpt/wpt.py b/tests/wpt/web-platform-tests/tools/wpt/wpt.py index 395db0394da..efa985c7cc1 100644 --- a/tests/wpt/web-platform-tests/tools/wpt/wpt.py +++ b/tests/wpt/web-platform-tests/tools/wpt/wpt.py @@ -38,6 +38,8 @@ def load_commands(): "requirements": [os.path.join(base_dir, item) for item in props.get("requirements", [])] } + if rv[command]["install"] or rv[command]["requirements"]: + assert rv[command]["virtualenv"] return rv diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py index 3f8989729fd..605b40281a1 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py @@ -13,6 +13,7 @@ from mozlog.handlers import BaseHandler, StreamHandler, LogLevelFilter here = os.path.dirname(__file__) localpaths = imp.load_source("localpaths", os.path.abspath(os.path.join(here, os.pardir, os.pardir, "localpaths.py"))) +from ci.tc.github_checks_output import get_gh_checks_outputter from wpt.markdown import markdown_adjust, table @@ -178,8 +179,32 @@ def err_string(results_dict, iterations): return rv +def write_github_checks_summary_inconsistent(log, inconsistent, iterations): + """Outputs a summary of inconsistent tests for GitHub Checks.""" + log("Some affected tests had inconsistent (flaky) results:\n") + write_inconsistent(log, inconsistent, iterations) + log("\n") + log("These may be pre-existing or new flakes. Please try to reproduce (see " + "the above WPT command, though some flags may not be needed when " + "running locally) and determine if your change introduced the flake. " + "If you are unable to reproduce the problem, please tag " + "`@web-platform-tests/wpt-core-team` in a comment for help.\n") + + +def write_github_checks_summary_slow_tests(log, slow): + """Outputs a summary of slow tests for GitHub Checks.""" + log("Some affected tests had slow results:\n") + write_slow_tests(log, slow) + log("\n") + log("These may be pre-existing or newly slow tests. Slow tests indicate " + "that a test ran very close to the test timeout limit and so may " + "become TIMEOUT-flaky in the future. Consider speeding up the test or " + "breaking it into multiple tests. For help, please tag " + "`@web-platform-tests/wpt-core-team` in a comment.\n") + + def write_inconsistent(log, inconsistent, iterations): - """Output inconsistent tests to logger.error.""" + """Output inconsistent tests to the passed in logging function.""" log("## Unstable results ##\n") strings = [( "`%s`" % markdown_adjust(test), @@ -191,6 +216,7 @@ def write_inconsistent(log, inconsistent, iterations): def write_slow_tests(log, slow): + """Output slow tests to the passed in logging function.""" log("## Slow tests ##\n") strings = [( "`%s`" % markdown_adjust(test), @@ -321,6 +347,8 @@ def check_stability(logger, repeat_loop=10, repeat_restart=5, chaos_mode=True, m start_time = datetime.now() step_results = [] + github_checks_outputter = get_gh_checks_outputter(kwargs) + for desc, step_func in steps: if max_time and datetime.now() - start_time > max_time: logger.info("::: Test verification is taking too long: Giving up!") @@ -337,12 +365,16 @@ def check_stability(logger, repeat_loop=10, repeat_restart=5, chaos_mode=True, m if inconsistent: step_results.append((desc, "FAIL")) + if github_checks_outputter: + write_github_checks_summary_inconsistent(github_checks_outputter.output, inconsistent, iterations) write_inconsistent(logger.info, inconsistent, iterations) write_summary(logger, step_results, "FAIL") return 1 if slow: step_results.append((desc, "FAIL")) + if github_checks_outputter: + write_github_checks_summary_slow_tests(github_checks_outputter.output, slow) write_slow_tests(logger.info, slow) write_summary(logger, step_results, "FAIL") return 1 diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py index f322d77fbdd..ea586825616 100644 --- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py +++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py @@ -348,6 +348,11 @@ scheme host and port.""") help="Command-line argument to forward to the " "Sauce Connect binary (repeatable)") + taskcluster_group = parser.add_argument_group("Taskcluster-specific") + taskcluster_group.add_argument("--github-checks-text-file", + dest="github_checks_text_file", + help="Path to GitHub checks output file") + webkit_group = parser.add_argument_group("WebKit-specific") webkit_group.add_argument("--webkit-port", dest="webkit_port", help="WebKit port") diff --git a/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_stash.py b/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_stash.py index 1c4c9ad933a..0b960c1e9f8 100644 --- a/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_stash.py +++ b/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_stash.py @@ -1,3 +1,4 @@ +import sys import unittest import uuid @@ -9,6 +10,8 @@ from wptserve.stash import StashServer from .base import TestUsingServer +@pytest.mark.skipif(sys.version_info >= (3, 8) and sys.platform == 'darwin', + reason="multiprocessing test hangs in Python 3.8 on macOS (#24880)") class TestResponseSetCookie(TestUsingServer): def run(self, result=None): with StashServer(None, authkey=str(uuid.uuid4())): diff --git a/tests/wpt/web-platform-tests/user-timing/case-sensitivity.any.js b/tests/wpt/web-platform-tests/user-timing/case-sensitivity.any.js new file mode 100644 index 00000000000..1c0b0dcac36 --- /dev/null +++ b/tests/wpt/web-platform-tests/user-timing/case-sensitivity.any.js @@ -0,0 +1,25 @@ + test(function () { + assert_equals(typeof self.performance, "object"); + assert_equals(typeof self.performance.getEntriesByType, "function"); + + self.performance.mark("mark1"); + self.performance.measure("measure1"); + + const type = [ + 'mark', + 'measure', + ]; + type.forEach(function(entryType) { + if (PerformanceObserver.supportedEntryTypes.includes(entryType)) { + const entryTypeUpperCased = entryType.toUpperCase(); + const entryTypeCapitalized = entryType[0].toUpperCase() + entryType.substring(1); + const lowerList = self.performance.getEntriesByType(entryType); + const upperList = self.performance.getEntriesByType(entryTypeUpperCased); + const mixedList = self.performance.getEntriesByType(entryTypeCapitalized); + + assert_greater_than(lowerList.length, 0, "Entries exist"); + assert_equals(upperList.length, 0, "getEntriesByType('" + entryTypeCapitalized + "').length"); + assert_equals(mixedList.length, 0, "getEntriesByType('" + entryTypeCapitalized + "').length"); + } + }); + }, "getEntriesByType values are case sensitive"); diff --git a/tests/wpt/web-platform-tests/webdriver/tests/new_session/response.py b/tests/wpt/web-platform-tests/webdriver/tests/new_session/response.py index dc132899881..cbe9e7d7fb1 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/new_session/response.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/new_session/response.py @@ -1,7 +1,7 @@ import uuid import pytest -from six import text_type +from six import string_types from tests.support.asserts import assert_success @@ -9,21 +9,21 @@ from tests.support.asserts import assert_success def test_sessionid(new_session, add_browser_capabilities): response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({})}}) value = assert_success(response) - assert isinstance(value["sessionId"], text_type) + assert isinstance(value["sessionId"], string_types) uuid.UUID(hex=value["sessionId"]) @pytest.mark.parametrize("capability, type", [ - ("browserName", text_type), - ("browserVersion", text_type), - ("platformName", text_type), + ("browserName", string_types), + ("browserVersion", string_types), + ("platformName", string_types), ("acceptInsecureCerts", bool), - ("pageLoadStrategy", text_type), + ("pageLoadStrategy", string_types), ("proxy", dict), ("setWindowRect", bool), ("timeouts", dict), ("strictFileInteractability", bool), - ("unhandledPromptBehavior", text_type), + ("unhandledPromptBehavior", string_types), ]) def test_capability_type(session, capability, type): assert isinstance(session.capabilities, dict) diff --git a/tests/wpt/web-platform-tests/webdriver/tests/perform_actions/support/keys.py b/tests/wpt/web-platform-tests/webdriver/tests/perform_actions/support/keys.py index a62318814f3..2d38e1dff87 100644 --- a/tests/wpt/web-platform-tests/webdriver/tests/perform_actions/support/keys.py +++ b/tests/wpt/web-platform-tests/webdriver/tests/perform_actions/support/keys.py @@ -19,6 +19,7 @@ import sys +from collections import OrderedDict from inspect import getmembers from six import text_type @@ -108,638 +109,780 @@ class Keys(object): ALL_KEYS = getmembers(Keys, lambda x: type(x) == text_type) -ALL_EVENTS = { - "ADD": { - "code": "NumpadAdd", - "ctrl": False, - "key": "+", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue025", - }, - "ALT": { - "code": "AltLeft", - "ctrl": False, - "key": "Alt", - "location": 1, - "meta": False, - "shift": False, - "value": u"\ue00a", - }, - "BACKSPACE": { - "code": "Backspace", - "ctrl": False, - "key": "Backspace", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue003", - }, - "CANCEL": { - "code": "", - "ctrl": False, - "key": "Cancel", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue001", - }, - "CLEAR": { - "code": "", - "ctrl": False, - "key": "Clear", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue005", - }, - "CONTROL": { - "code": "ControlLeft", - "ctrl": True, - "key": "Control", - "location": 1, - "meta": False, - "shift": False, - "value": u"\ue009", - }, - "DECIMAL": { - "code": "NumpadDecimal", - "ctrl": False, - "key": ".", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue028", - }, - "DELETE": { - "code": "Delete", - "ctrl": False, - "key": "Delete", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue017", - }, - "DIVIDE": { - "code": "NumpadDivide", - "ctrl": False, - "key": "/", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue029", - }, - "DOWN": { - "code": "ArrowDown", - "ctrl": False, - "key": "ArrowDown", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue015", - }, - "END": { - "code": "End", - "ctrl": False, - "key": "End", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue010", - }, - "ENTER": { - "code": "NumpadEnter", - "ctrl": False, - "key": "Enter", - "location": 1, - "meta": False, - "shift": False, - "value": u"\ue007", - }, - "EQUALS": { - "code": "", - "ctrl": False, - "key": "=", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue019", - }, - "ESCAPE": { - "code": "Escape", - "ctrl": False, - "key": "Escape", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue00c", - }, - "F1": { - "code": "F1", - "ctrl": False, - "key": "F1", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue031", - }, - "F10": { - "code": "F10", - "ctrl": False, - "key": "F10", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue03a", - }, - "F11": { - "code": "F11", - "ctrl": False, - "key": "F11", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue03b", - }, - "F12": { - "code": "F12", - "ctrl": False, - "key": "F12", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue03c", - }, - "F2": { - "code": "F2", - "ctrl": False, - "key": "F2", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue032", - }, - "F3": { - "code": "F3", - "ctrl": False, - "key": "F3", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue033", - }, - "F4": { - "code": "F4", - "ctrl": False, - "key": "F4", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue034", - }, - "F5": { - "code": "F5", - "ctrl": False, - "key": "F5", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue035", - }, - "F6": { - "code": "F6", - "ctrl": False, - "key": "F6", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue036", - }, - "F7": { - "code": "F7", - "ctrl": False, - "key": "F7", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue037", - }, - "F8": { - "code": "F8", - "ctrl": False, - "key": "F8", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue038", - }, - "F9": { - "code": "F9", - "ctrl": False, - "key": "F9", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue039", - }, - "HELP": { - "code": "Help", - "ctrl": False, - "key": "Help", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue002", - }, - "HOME": { - "code": "Home", - "ctrl": False, - "key": "Home", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue011", - }, - "INSERT": { - "code": "Insert", - "ctrl": False, - "key": "Insert", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue016", - }, - "LEFT": { - "code": "ArrowLeft", - "ctrl": False, - "key": "ArrowLeft", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue012", - }, - "META": { - "code": "MetaLeft", - "ctrl": False, - "key": "Meta", - "location": 1, - "meta": True, - "shift": False, - "value": u"\ue03d", - }, - "MULTIPLY": { - "code": "NumpadMultiply", - "ctrl": False, - "key": "*", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue024", - }, - "NULL": { - "code": "", - "ctrl": False, - "key": "Unidentified", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue000", - }, - "NUMPAD0": { - "code": "Numpad0", - "ctrl": False, - "key": "0", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue01a", - }, - "NUMPAD1": { - "code": "Numpad1", - "ctrl": False, - "key": "1", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue01b", - }, - "NUMPAD2": { - "code": "Numpad2", - "ctrl": False, - "key": "2", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue01c", - }, - "NUMPAD3": { - "code": "Numpad3", - "ctrl": False, - "key": "3", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue01d", - }, - "NUMPAD4": { - "code": "Numpad4", - "ctrl": False, - "key": "4", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue01e", - }, - "NUMPAD5": { - "code": "Numpad5", - "ctrl": False, - "key": "5", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue01f", - }, - "NUMPAD6": { - "code": "Numpad6", - "ctrl": False, - "key": "6", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue020", - }, - "NUMPAD7": { - "code": "Numpad7", - "ctrl": False, - "key": "7", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue021", - }, - "NUMPAD8": { - "code": "Numpad8", - "ctrl": False, - "key": "8", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue022", - }, - "NUMPAD9": { - "code": "Numpad9", - "ctrl": False, - "key": "9", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue023", - }, - "PAGE_DOWN": { - "code": "PageDown", - "ctrl": False, - "key": "PageDown", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue00f", - }, - "PAGE_UP": { - "code": "PageUp", - "ctrl": False, - "key": "PageUp", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue00e", - }, - "PAUSE": { - "code": "", - "ctrl": False, - "key": "Pause", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue00b", - }, - "RETURN": { - "code": "Enter", - "ctrl": False, - "key": "Enter", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue006", - }, - "RIGHT": { - "code": "ArrowRight", - "ctrl": False, - "key": "ArrowRight", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue014", - }, - "R_ALT": { - "code": "AltRight", - "ctrl": False, - "key": "Alt", - "location": 2, - "meta": False, - "shift": False, - "value": u"\ue052", - }, - "R_ARROWDOWN": { - "code": "Numpad2", - "ctrl": False, - "key": "ArrowDown", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue05b", - }, - "R_ARROWLEFT": { - "code": "Numpad4", - "ctrl": False, - "key": "ArrowLeft", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue058", - }, - "R_ARROWRIGHT": { - "code": "Numpad6", - "ctrl": False, - "key": "ArrowRight", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue05a", - }, - "R_ARROWUP": { - "code": "Numpad8", - "ctrl": False, - "key": "ArrowUp", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue059", - }, - "R_CONTROL": { - "code": "ControlRight", - "ctrl": True, - "key": "Control", - "location": 2, - "meta": False, - "shift": False, - "value": u"\ue051", - }, - "R_DELETE": { - "code": "NumpadDecimal", - "ctrl": False, - "key": "Delete", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue05d", - }, - "R_END": { - "code": "Numpad1", - "ctrl": False, - "key": "End", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue056", - }, - "R_HOME": { - "code": "Numpad7", - "ctrl": False, - "key": "Home", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue057", - }, - "R_INSERT": { - "code": "Numpad0", - "ctrl": False, - "key": "Insert", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue05c", - }, - "R_META": { - "code": "MetaRight", - "ctrl": False, - "key": "Meta", - "location": 2, - "meta": True, - "shift": False, - "value": u"\ue053", - }, - "R_PAGEDOWN": { - "code": "Numpad3", - "ctrl": False, - "key": "PageDown", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue055", - }, - "R_PAGEUP": { - "code": "Numpad9", - "ctrl": False, - "key": "PageUp", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue054", - }, - "R_SHIFT": { - "code": "ShiftRight", - "ctrl": False, - "key": "Shift", - "location": 2, - "meta": False, - "shift": True, - "value": u"\ue050", - }, - "SEMICOLON": { - "code": "", - "ctrl": False, - "key": ";", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue018", - }, - "SEPARATOR": { - "code": "NumpadComma", - "ctrl": False, - "key": ",", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue026", - }, - "SHIFT": { - "code": "ShiftLeft", - "ctrl": False, - "key": "Shift", - "location": 1, - "meta": False, - "shift": True, - "value": u"\ue008", - }, - "SPACE": { - "code": "Space", - "ctrl": False, - "key": " ", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue00d", - }, - "SUBTRACT": { - "code": "NumpadSubtract", - "ctrl": False, - "key": "-", - "location": 3, - "meta": False, - "shift": False, - "value": u"\ue027", - }, - "TAB": { - "code": "Tab", - "ctrl": False, - "key": "Tab", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue004", - }, - "UP": { - "code": "ArrowUp", - "ctrl": False, - "key": "ArrowUp", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue013", - }, - "ZENKAKUHANKAKU": { - "code": "", - "ctrl": False, - "key": "ZenkakuHankaku", - "location": 0, - "meta": False, - "shift": False, - "value": u"\ue040", - } -} +ALL_EVENTS = OrderedDict( + [ + ("ADD", OrderedDict( + [ + ("code", "NumpadAdd"), + ("ctrl", False), + ("key", "+"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue025") + ] + )), + ("ALT", OrderedDict( + [ + ("code", "AltLeft"), + ("ctrl", False), + ("key", "Alt"), + ("location", 1), + ("meta", False), + ("shift", False), + ("value", u"\ue00a") + ] + )), + ("BACKSPACE", OrderedDict( + [ + ("code", "Backspace"), + ("ctrl", False), + ("key", "Backspace"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue003") + ] + )), + ("CANCEL", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", "Cancel"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue001") + ] + )), + ("CLEAR", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", "Clear"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue005") + ] + )), + ("CONTROL", OrderedDict( + [ + ("code", "ControlLeft"), + ("ctrl", True), + ("key", "Control"), + ("location", 1), + ("meta", False), + ("shift", False), + ("value", u"\ue009") + ] + )), + ("DECIMAL", OrderedDict( + [ + ("code", "NumpadDecimal"), + ("ctrl", False), + ("key", "."), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue028") + ] + )), + ("DELETE", OrderedDict( + [ + ("code", "Delete"), + ("ctrl", False), + ("key", "Delete"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue017") + ] + )), + ("DIVIDE", OrderedDict( + [ + ("code", "NumpadDivide"), + ("ctrl", False), + ("key", "/"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue029") + ] + )), + ("DOWN", OrderedDict( + [ + ("code", "ArrowDown"), + ("ctrl", False), + ("key", "ArrowDown"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue015") + ] + )), + ("END", OrderedDict( + [ + ("code", "End"), + ("ctrl", False), + ("key", "End"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue010") + ] + )), + ("ENTER", OrderedDict( + [ + ("code", "NumpadEnter"), + ("ctrl", False), + ("key", "Enter"), + ("location", 1), + ("meta", False), + ("shift", False), + ("value", u"\ue007") + ] + )), + ("EQUALS", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", "="), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue019") + ] + )), + ("ESCAPE", OrderedDict( + [ + ("code", "Escape"), + ("ctrl", False), + ("key", "Escape"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue00c") + ] + )), + ("F1", OrderedDict( + [ + ("code", "F1"), + ("ctrl", False), + ("key", "F1"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue031") + ] + )), + ("F10", OrderedDict( + [ + ("code", "F10"), + ("ctrl", False), + ("key", "F10"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue03a") + ] + )), + ("F11", OrderedDict( + [ + ("code", "F11"), + ("ctrl", False), + ("key", "F11"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue03b") + ] + )), + ("F12", OrderedDict( + [ + ("code", "F12"), + ("ctrl", False), + ("key", "F12"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue03c") + ] + )), + ("F2", OrderedDict( + [ + ("code", "F2"), + ("ctrl", False), + ("key", "F2"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue032") + ] + )), + ("F3", OrderedDict( + [ + ("code", "F3"), + ("ctrl", False), + ("key", "F3"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue033") + ] + )), + ("F4", OrderedDict( + [ + ("code", "F4"), + ("ctrl", False), + ("key", "F4"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue034") + ] + )), + ("F5", OrderedDict( + [ + ("code", "F5"), + ("ctrl", False), + ("key", "F5"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue035") + ] + )), + ("F6", OrderedDict( + [ + ("code", "F6"), + ("ctrl", False), + ("key", "F6"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue036") + ] + )), + ("F7", OrderedDict( + [ + ("code", "F7"), + ("ctrl", False), + ("key", "F7"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue037") + ] + )), + ("F8", OrderedDict( + [ + ("code", "F8"), + ("ctrl", False), + ("key", "F8"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue038") + ] + )), + ("F9", OrderedDict( + [ + ("code", "F9"), + ("ctrl", False), + ("key", "F9"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue039") + ] + )), + ("HELP", OrderedDict( + [ + ("code", "Help"), + ("ctrl", False), + ("key", "Help"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue002") + ] + )), + ("HOME", OrderedDict( + [ + ("code", "Home"), + ("ctrl", False), + ("key", "Home"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue011") + ] + )), + ("INSERT", OrderedDict( + [ + ("code", "Insert"), + ("ctrl", False), + ("key", "Insert"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue016") + ] + )), + ("LEFT", OrderedDict( + [ + ("code", "ArrowLeft"), + ("ctrl", False), + ("key", "ArrowLeft"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue012") + ] + )), + ("META", OrderedDict( + [ + ("code", "MetaLeft"), + ("ctrl", False), + ("key", "Meta"), + ("location", 1), + ("meta", True), + ("shift", False), + ("value", u"\ue03d") + ] + )), + ("MULTIPLY", OrderedDict( + [ + ("code", "NumpadMultiply"), + ("ctrl", False), + ("key", "*"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue024") + ] + )), + ("NULL", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", "Unidentified"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue000") + ] + )), + ("NUMPAD0", OrderedDict( + [ + ("code", "Numpad0"), + ("ctrl", False), + ("key", "0"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue01a") + ] + )), + ("NUMPAD1", OrderedDict( + [ + ("code", "Numpad1"), + ("ctrl", False), + ("key", "1"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue01b") + ] + )), + ("NUMPAD2", OrderedDict( + [ + ("code", "Numpad2"), + ("ctrl", False), + ("key", "2"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue01c") + ] + )), + ("NUMPAD3", OrderedDict( + [ + ("code", "Numpad3"), + ("ctrl", False), + ("key", "3"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue01d") + ] + )), + ("NUMPAD4", OrderedDict( + [ + ("code", "Numpad4"), + ("ctrl", False), + ("key", "4"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue01e") + ] + )), + ("NUMPAD5", OrderedDict( + [ + ("code", "Numpad5"), + ("ctrl", False), + ("key", "5"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue01f") + ] + )), + ("NUMPAD6", OrderedDict( + [ + ("code", "Numpad6"), + ("ctrl", False), + ("key", "6"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue020") + ] + )), + ("NUMPAD7", OrderedDict( + [ + ("code", "Numpad7"), + ("ctrl", False), + ("key", "7"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue021") + ] + )), + ("NUMPAD8", OrderedDict( + [ + ("code", "Numpad8"), + ("ctrl", False), + ("key", "8"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue022") + ] + )), + ("NUMPAD9", OrderedDict( + [ + ("code", "Numpad9"), + ("ctrl", False), + ("key", "9"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue023") + ] + )), + ("PAGE_DOWN", OrderedDict( + [ + ("code", "PageDown"), + ("ctrl", False), + ("key", "PageDown"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue00f") + ] + )), + ("PAGE_UP", OrderedDict( + [ + ("code", "PageUp"), + ("ctrl", False), + ("key", "PageUp"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue00e") + ] + )), + ("PAUSE", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", "Pause"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue00b") + ] + )), + ("RETURN", OrderedDict( + [ + ("code", "Enter"), + ("ctrl", False), + ("key", "Enter"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue006") + ] + )), + ("RIGHT", OrderedDict( + [ + ("code", "ArrowRight"), + ("ctrl", False), + ("key", "ArrowRight"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue014") + ] + )), + ("R_ALT", OrderedDict( + [ + ("code", "AltRight"), + ("ctrl", False), + ("key", "Alt"), + ("location", 2), + ("meta", False), + ("shift", False), + ("value", u"\ue052") + ] + )), + ("R_ARROWDOWN", OrderedDict( + [ + ("code", "Numpad2"), + ("ctrl", False), + ("key", "ArrowDown"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue05b") + ] + )), + ("R_ARROWLEFT", OrderedDict( + [ + ("code", "Numpad4"), + ("ctrl", False), + ("key", "ArrowLeft"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue058") + ] + )), + ("R_ARROWRIGHT", OrderedDict( + [ + ("code", "Numpad6"), + ("ctrl", False), + ("key", "ArrowRight"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue05a") + ] + )), + ("R_ARROWUP", OrderedDict( + [ + ("code", "Numpad8"), + ("ctrl", False), + ("key", "ArrowUp"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue059") + ] + )), + ("R_CONTROL", OrderedDict( + [ + ("code", "ControlRight"), + ("ctrl", True), + ("key", "Control"), + ("location", 2), + ("meta", False), + ("shift", False), + ("value", u"\ue051") + ] + )), + ("R_DELETE", OrderedDict( + [ + ("code", "NumpadDecimal"), + ("ctrl", False), + ("key", "Delete"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue05d") + ] + )), + ("R_END", OrderedDict( + [ + ("code", "Numpad1"), + ("ctrl", False), + ("key", "End"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue056") + ] + )), + ("R_HOME", OrderedDict( + [ + ("code", "Numpad7"), + ("ctrl", False), + ("key", "Home"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue057") + ] + )), + ("R_INSERT", OrderedDict( + [ + ("code", "Numpad0"), + ("ctrl", False), + ("key", "Insert"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue05c") + ] + )), + ("R_META", OrderedDict( + [ + ("code", "MetaRight"), + ("ctrl", False), + ("key", "Meta"), + ("location", 2), + ("meta", True), + ("shift", False), + ("value", u"\ue053") + ] + )), + ("R_PAGEDOWN", OrderedDict( + [ + ("code", "Numpad3"), + ("ctrl", False), + ("key", "PageDown"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue055") + ] + )), + ("R_PAGEUP", OrderedDict( + [ + ("code", "Numpad9"), + ("ctrl", False), + ("key", "PageUp"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue054") + ] + )), + ("R_SHIFT", OrderedDict( + [ + ("code", "ShiftRight"), + ("ctrl", False), + ("key", "Shift"), + ("location", 2), + ("meta", False), + ("shift", True), + ("value", u"\ue050") + ] + )), + ("SEMICOLON", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", ";"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue018") + ] + )), + ("SEPARATOR", OrderedDict( + [ + ("code", "NumpadComma"), + ("ctrl", False), + ("key", ","), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue026") + ] + )), + ("SHIFT", OrderedDict( + [ + ("code", "ShiftLeft"), + ("ctrl", False), + ("key", "Shift"), + ("location", 1), + ("meta", False), + ("shift", True), + ("value", u"\ue008") + ] + )), + ("SPACE", OrderedDict( + [ + ("code", "Space"), + ("ctrl", False), + ("key", " "), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue00d") + ] + )), + ("SUBTRACT", OrderedDict( + [ + ("code", "NumpadSubtract"), + ("ctrl", False), + ("key", "-"), + ("location", 3), + ("meta", False), + ("shift", False), + ("value", u"\ue027") + ] + )), + ("TAB", OrderedDict( + [ + ("code", "Tab"), + ("ctrl", False), + ("key", "Tab"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue004") + ] + )), + ("UP", OrderedDict( + [ + ("code", "ArrowUp"), + ("ctrl", False), + ("key", "ArrowUp"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue013") + ] + )), + ("ZENKAKUHANKAKU", OrderedDict( + [ + ("code", ""), + ("ctrl", False), + ("key", "ZenkakuHankaku"), + ("location", 0), + ("meta", False), + ("shift", False), + ("value", u"\ue040") + ] + )) + ] +) ALTERNATIVE_KEY_NAMES = { "ADD": "Add", diff --git a/tests/wpt/web-platform-tests/webxr/render_state_update.https.html b/tests/wpt/web-platform-tests/webxr/render_state_update.https.html new file mode 100644 index 00000000000..2f28d442ec9 --- /dev/null +++ b/tests/wpt/web-platform-tests/webxr/render_state_update.https.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/webxr_util.js"></script> +<script src="resources/webxr_test_constants.js"></script> +<canvas /> + +<script> +let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; + +let testSessionEnded = function(session, fakeDeviceController, t) { + return new Promise((resolve, reject) => { + resolve(session.end().then(() => { + t.step(() => { + assert_throws_dom('InvalidStateError', () => session.updateRenderState({})); + }); + })); + }); +}; + + +let testBaseLayer = function(session, fakeDeviceController, t, sessionObjects) { + return new Promise((resolve, reject) => { + navigator.xr.test.simulateUserActivation(() => { + navigator.xr.requestSession('inline').then((tempSession) => { + t.step(() => { + assert_not_equals(session, tempSession); + assert_throws_dom('InvalidStateError', () => session.updateRenderState({ baseLayer : new XRWebGLLayer(tempSession, sessionObjects.gl), })); + }); + }); + resolve(); + }); + }); +}; + +let testFieldOfView = function(session, fakeDeviceController, t) { + return new Promise((resolve, reject) => { + t.step(() => { + assert_throws_dom('InvalidStateError', () => session.updateRenderState({ inlineVerticalFieldOfView : Math.PI, })); + }); + resolve(); + }); +}; + +let testNoParams = function(session, fakeDeviceController, t) { + return new Promise((resolve, reject) => { + try { + session.updateRenderState({}); + } catch (err) { + assert_unreached("updateRenderState should not fail (actually not do anything) with no params"); + } + resolve(); + }); +}; + +let testParams = function(session, fakeDeviceController, t, sessionObjects) { + return new Promise((resolve, reject) => { + let gl = sessionObjects.gl; + try { + gl.makeXRCompatible().then(() => { + t.step(() => { + let fov = Math.PI; + let near = 0.2; + let far = 0.8; + let layer = new XRWebGLLayer(session, gl); + session.updateRenderState({ inlineVerticalFieldOfView: fov, depthNear: near, depthFar: far, baseLayer: layer }); + // The update can only happen between frame boundaries, updateRenderState only queues changes. + assert_not_equals(session.renderState.inlineVerticalFieldOfView, fov); + assert_not_equals(session.renderState.depthNear, near); + assert_not_equals(session.renderState.depthFar, far); + assert_not_equals(session.renderState.baseLayer, layer); + }); + }); + } catch (err) { + assert_unreached("updateRenderState should not fail when all params are specified"); + } + resolve(); + }); +}; + +let testName = "updateRenderState handles appropriately ended sessions"; +xr_session_promise_test(testName, testSessionEnded, fakeDeviceInitParams, 'immersive-vr'); + +testName = "updateRenderState handles appropriately baseLayers created with different sessions"; +xr_session_promise_test(testName, testBaseLayer, fakeDeviceInitParams, 'immersive-vr'); + +testName = "updateRenderState handles appropriately immersive sessions with specified inlineVerticalFieldOfView"; +xr_session_promise_test(testName, testFieldOfView, fakeDeviceInitParams, 'immersive-vr'); + +testName = "updateRenderState handles appropriately XRRenderStateInit with no params"; +xr_session_promise_test(testName, testNoParams, fakeDeviceInitParams, 'immersive-vr'); + +testName = "updateRenderState handles appropriately XRRenderStateInit params"; +xr_session_promise_test(testName, testParams, fakeDeviceInitParams, 'inline'); + +</script> diff --git a/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html b/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html index 840433d3d89..d8aa02a01b8 100644 --- a/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html +++ b/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.html @@ -1,19 +1,91 @@ <!doctype html> -<title>onerror, "not handled" with an error in the onerror function</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<div id="log"></div> <script> -async_test(function() { - var worker = new Worker('exception-in-onerror.js'); - worker.onerror = this.step_func(function(e) { - assert_true(e instanceof ErrorEvent, 'e instanceof ErrorEvent'); - assert_equals(typeof e.message, 'string', 'typeof e.message'); - assert_equals(e.filename, document.URL.replace('.html', '.js'), 'e.filename'); - assert_equals(typeof e.lineno, 'number', 'typeof e.lineno'); - assert_equals(typeof e.colno, 'number', 'typeof e.column'); - e.preventDefault(); // "handled" - this.done(); - }); -}); -</script>
\ No newline at end of file +// 1. Exception is thrown somewhere in a Worker. +// Various contexts are tested: from worker initialization, +// from setTimeout, from event handlers, etc. +// 2. WorkerGlobalScope.onerror event handler is called. +// (i.e. `onerror` in the worker script) +// 3. From the event handler, another exception is thrown. +// 4. Each of Worker.onerror handler (on the parent Document) and +// Worker error event listener should be called twice: +// once for each of the exceptions thrown in Step 1 and 2, respectively. +// (We don't check the ordering of two Worker.onerror calls, because +// browsers fires them in different orders) + +function prepareHandler(t, messages) { + const fired = {}; + let fired_count = 0; + t.step_timeout(() => { + if (fired_count < messages.length) { + let error_description = 'Worker.onerror not fired for:'; + for (const message of messages) { + if (!fired[message]) { + error_description += ' '; + error_description += message; + } + } + assert_unreached(error_description); + } + }, 2000); + return t.step_func(e => { + e.preventDefault(); + for (const message of messages) { + if (!fired[message] && e.message.indexOf(message) >= 0) { + fired[message] = true; + ++fired_count; + if (fired_count === messages.length) { + // Worker.onerror is fired for all messages. + t.done(); + } + return; + } + } + assert_unreached("Unexpected worker.onerror message: " + e.message); + }); +} + +function expectErrors(worker, title, messages) { + async_test(t => { + worker.addEventListener('error', prepareHandler(t, messages)); + }, title+ ': listener'); + async_test(t => { + worker.onerror = prepareHandler(t, messages); + }, title + ': handler'); +} + +for (const type of ['classic', 'module']) { + const workerOptions = type === 'module' ? {type: 'module'}: {}; + + const worker1 = new Worker( + 'exception-in-onerror.js?throw-in-worker-initialization', + workerOptions); + expectErrors( + worker1, + 'Throw in worker initialization: ' + type, + ['Throw in worker initialization', 'Throw in error handler']); + + const worker2 = new Worker( + 'exception-in-onerror.js?throw-in-setTimeout-function', workerOptions); + expectErrors( + worker2, + 'Throw in setTimeout(function): ' + type, + ['Throw in setTimeout function', 'Throw in error handler']); + + const worker3 = new Worker( + 'exception-in-onerror.js?throw-in-setTimeout-string', workerOptions); + expectErrors( + worker3, + 'Throw in setTimeout(string): ' + type, + ['Throw in setTimeout string', 'Throw in error handler']); + + const worker4 = new Worker('exception-in-onerror.js', workerOptions); + worker4.postMessage('foo'); + expectErrors( + worker4, + 'Throw in message handler: ' + type, + ['Throw in message handler', 'Throw in error handler']); + +} +</script> diff --git a/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.js b/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.js index 2bf4124cb74..61a95dda51f 100644 --- a/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.js +++ b/tests/wpt/web-platform-tests/workers/interfaces/WorkerGlobalScope/onerror/exception-in-onerror.js @@ -1,7 +1,25 @@ -onerror = function(a, b, c, d) { - y(); // the error is "not handled" +onerror = function() { + throw new Error('Throw in error handler'); + return false; +}; +onmessage = function() { + throw new Error('Throw in message handler'); + return false; +}; + +if (self.location.href.indexOf( + 'throw-in-worker-initialization') >= 0) { + throw new Error('Throw in worker initialization'); } -function x() { - y(); + +if (self.location.href.indexOf( + 'throw-in-setTimeout-function') >= 0) { + // To test the behavior of setTimeout(), raw setTimeout() is used. + setTimeout(() => { throw new Error('Throw in setTimeout function') }, 0); +} + +if (self.location.href.indexOf( + 'throw-in-setTimeout-string') >= 0) { + // To test the behavior of setTimeout(), raw setTimeout() is used. + setTimeout("throw new Error('Throw in setTimeout string')", 0); } -x();
\ No newline at end of file diff --git a/tests/wpt/webgl/meta/conformance/renderbuffers/framebuffer-state-restoration.html.ini b/tests/wpt/webgl/meta/conformance/renderbuffers/framebuffer-state-restoration.html.ini index 196c07cc589..b17781ba017 100644 --- a/tests/wpt/webgl/meta/conformance/renderbuffers/framebuffer-state-restoration.html.ini +++ b/tests/wpt/webgl/meta/conformance/renderbuffers/framebuffer-state-restoration.html.ini @@ -6,3 +6,6 @@ [WebGL test #1: context does not exist] expected: FAIL + [WebGL test #14: should be red\nat (0, 0) expected: 255,0,0,255 was 0,255,0,255] + expected: FAIL + diff --git a/tests/wpt/webgl/meta/conformance/rendering/multisample-corruption.html.ini b/tests/wpt/webgl/meta/conformance/rendering/multisample-corruption.html.ini index a318722d23b..2d68d489d35 100644 --- a/tests/wpt/webgl/meta/conformance/rendering/multisample-corruption.html.ini +++ b/tests/wpt/webgl/meta/conformance/rendering/multisample-corruption.html.ini @@ -7,75 +7,3 @@ [WebGL test #1: context does not exist] expected: FAIL - [WebGL test #9: Canvas should be red] - expected: FAIL - - [WebGL test #17: Canvas should be red] - expected: FAIL - - [WebGL test #23: Canvas should be red] - expected: FAIL - - [WebGL test #8: Canvas should be red] - expected: FAIL - - [WebGL test #13: Canvas should be red] - expected: FAIL - - [WebGL test #18: Canvas should be red] - expected: FAIL - - [WebGL test #5: Canvas should be red] - expected: FAIL - - [WebGL test #14: Canvas should be red] - expected: FAIL - - [WebGL test #22: Canvas should be red] - expected: FAIL - - [WebGL test #20: Canvas should be red] - expected: FAIL - - [WebGL test #3: Canvas should be red] - expected: FAIL - - [WebGL test #24: Canvas should be red] - expected: FAIL - - [WebGL test #7: Canvas should be red] - expected: FAIL - - [WebGL test #11: Canvas should be red] - expected: FAIL - - [WebGL test #2: Canvas should be red] - expected: FAIL - - [WebGL test #4: Canvas should be red] - expected: FAIL - - [WebGL test #1: Canvas should be red] - expected: FAIL - - [WebGL test #21: Canvas should be red] - expected: FAIL - - [WebGL test #6: Canvas should be red] - expected: FAIL - - [WebGL test #19: Canvas should be red] - expected: FAIL - - [WebGL test #12: Canvas should be red] - expected: FAIL - - [WebGL test #16: Canvas should be red] - expected: FAIL - - [WebGL test #10: Canvas should be red] - expected: FAIL - - [WebGL test #15: Canvas should be red] - expected: FAIL - diff --git a/tests/wpt/webgl/meta/conformance2/rendering/draw-buffers.html.ini b/tests/wpt/webgl/meta/conformance2/rendering/draw-buffers.html.ini index abd1336b54a..fd7fbe111ff 100644 --- a/tests/wpt/webgl/meta/conformance2/rendering/draw-buffers.html.ini +++ b/tests/wpt/webgl/meta/conformance2/rendering/draw-buffers.html.ini @@ -209,3 +209,12 @@ [WebGL test #44: attachment 7 should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 0,0,255,0] expected: FAIL + [WebGL test #43: attachment 6 should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 0,0,0,255] + expected: FAIL + + [WebGL test #44: attachment 7 should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255] + expected: FAIL + + [WebGL test #52: attachment 7 should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255] + expected: FAIL + |