diff options
120 files changed, 1946 insertions, 1193 deletions
diff --git a/Cargo.lock b/Cargo.lock index 402e1bb8979..91d5ae31e4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1017,6 +1017,7 @@ dependencies = [ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1444,7 +1445,7 @@ dependencies = [ "fontsan 0.4.0 (git+https://github.com/servo/fontsan)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx_traits 0.0.1", - "harfbuzz-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1466,7 +1467,7 @@ dependencies = [ "truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", "ucd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-script 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.60.0 (git+https://github.com/servo/webrender)", "xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "xml5ever 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1866,10 +1867,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "harfbuzz-sys" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2311,7 +2315,7 @@ dependencies = [ "style 0.0.1", "style_traits 0.0.1", "unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-script 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.60.0 (git+https://github.com/servo/webrender)", "xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2580,7 +2584,6 @@ dependencies = [ "hyper 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", "hyper_serde 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "keyboard-types 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mozjs 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.21.0", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2807,13 +2810,13 @@ dependencies = [ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mozjs_sys 0.61.12 (registry+https://github.com/rust-lang/crates.io-index)", + "mozjs_sys 0.61.13 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mozjs_sys" -version = "0.61.12" +version = "0.61.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bindgen 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3459,7 +3462,7 @@ dependencies = [ [[package]] name = "rust-webvr" -version = "0.10.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bindgen 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3472,13 +3475,13 @@ dependencies = [ "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "ovr-mobile-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-webvr-api 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "winit 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rust-webvr-api" -version = "0.10.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3813,7 +3816,7 @@ dependencies = [ "mozangle 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "osmesa-src 0.1.0 (git+https://github.com/servo/osmesa-src)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-webvr 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3860,7 +3863,7 @@ dependencies = [ [[package]] name = "servo-media" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "servo-media-audio 0.1.0 (git+https://github.com/servo/media)", "servo-media-player 0.1.0 (git+https://github.com/servo/media)", @@ -3871,7 +3874,7 @@ dependencies = [ [[package]] name = "servo-media-audio" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-slice-cast 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3887,7 +3890,7 @@ dependencies = [ [[package]] name = "servo-media-dummy" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3901,7 +3904,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-slice-cast 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3935,7 +3938,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "gstreamer 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer-video 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3945,7 +3948,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render-unix" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "gstreamer 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3958,7 +3961,7 @@ dependencies = [ [[package]] name = "servo-media-player" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3969,7 +3972,7 @@ dependencies = [ [[package]] name = "servo-media-streams" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3978,7 +3981,7 @@ dependencies = [ [[package]] name = "servo-media-webrtc" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "boxfnonce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4077,7 +4080,7 @@ dependencies = [ [[package]] name = "servo_media_derive" version = "0.1.0" -source = "git+https://github.com/servo/media#01cd9be1b8e92e036b4ddff0294893f7e53ef373" +source = "git+https://github.com/servo/media#8691ca03a37c45bf7efa9430e434dd5fbf0abfa3" dependencies = [ "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4831,10 +4834,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-script" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "harfbuzz-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5140,7 +5143,7 @@ dependencies = [ "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "servo_config 0.0.1", "webvr_traits 0.0.1", @@ -5152,7 +5155,7 @@ version = "0.0.1" dependencies = [ "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr-api 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5518,7 +5521,7 @@ dependencies = [ "checksum gvr-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1334b94d8ce67319ddc44663daef53d8c1538629a11562530c981dbd9085b9a" "checksum h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a27e7ed946e8335bdf9a191bc1b9b14a03ba822d013d2f58437f4fabcbd7fc2c" "checksum half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f" -"checksum harfbuzz-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87a29ce223fee4727c0c4810a1419a3412f65b29146339fb6a47ee39456c34ea" +"checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" "checksum hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "64b7d419d0622ae02fe5da6b9a5e1964b610a65bb37923b976aeebb6dbb8f86e" "checksum headers-core 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7794c3bca3a5fb812a06d43f715cf857f7b037d52d6d8e054231d439dd839073" "checksum headers-derive 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93b8509be5e3893b8c9c37805a05aa57e4561cf1f1a2aab30cec4931127f36ca" @@ -5591,7 +5594,7 @@ dependencies = [ "checksum mitochondria 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9de3eca27871df31c33b807f834b94ef7d000956f57aa25c5aed9c5f0aae8f6f" "checksum mozangle 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ba1ce5212fd56a71cfbc463aedd4ece76090d98b96d5122f84dedffa0cc508" "checksum mozjs 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "622108d35f4fdd68b3aa39bfe9bedaee5fa9efd19711d046e1d56ff607c0a36f" -"checksum mozjs_sys 0.61.12 (registry+https://github.com/rust-lang/crates.io-index)" = "370887111c83436555cde840bb2639c5a088c77f01b152957d99e1f279397f48" +"checksum mozjs_sys 0.61.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d35502544cf3e70b305e028c6ca9e4c3d5a48264af220f8341598f54d189ba" "checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729" "checksum muldiv 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "451a9a05d2a32c566c897835e0ea95cf79ed2fdfe957924045a1721a36c9980f" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" @@ -5650,8 +5653,8 @@ dependencies = [ "checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399" -"checksum rust-webvr 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "dd268264db4808e78ad1738ce1ff10a8bd53fb5ba408f595763174ca4a6fb875" -"checksum rust-webvr-api 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1b2af5c6c86fb79e70b5a34daa3d488411225707c73dc5467c7da7c83d557daf" +"checksum rust-webvr 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9c827417035beccd02f5e1dde7866c80662efafb8bcfe659f0082d10def4faa" +"checksum rust-webvr-api 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5208a3b3f0b02abf17e66c0fe1e0cd3a4f5172c9bf6d1a3e1ac6338a3d218d3" "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b8eb11f5b0a98c8eca2fb1483f42646d8c340e83e46ab416f8a063a0fd0eeb20" @@ -5740,7 +5743,7 @@ dependencies = [ "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a" "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" -"checksum unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e8bd7bbf020b2885113e6304f68bcc33881c5552657c58d4e9699cd1b6606e81" +"checksum unicode-script 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09f03ad95feb4fde244d79985bfd79eb34ff2702fedb441d2ba3f4ff813efd19" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 7a6022cb64c..a42e998c958 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -108,6 +108,7 @@ text time timeupdate toggle +track transitionend unhandledrejection unload diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index 474ae4b603b..af88ca905aa 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -12,7 +12,7 @@ use std::fmt; use std::num::NonZeroU32; use std::ops::Deref; use webrender_api::{DocumentId, ImageKey, PipelineId}; -use webvr_traits::WebVRFutureFrameData; +use webvr_traits::WebVRPoseInformation; /// Helper function that creates a WebGL channel (WebGLSender, WebGLReceiver) to be used in WebGLCommands. pub use crate::webgl_channel::webgl_channel; @@ -508,9 +508,13 @@ pub enum WebVRCommand { /// Synchronize the pose information to be used in the frame. SyncPoses( WebVRDeviceId, + // near f64, + // far f64, - WebGLSender<Result<WebVRFutureFrameData, ()>>, + // sync gamepads too + bool, + WebGLSender<Result<WebVRPoseInformation, ()>>, ), /// Submit the frame to a VR device using the specified texture coordinates. SubmitFrame(WebVRDeviceId, [f32; 4], [f32; 4]), diff --git a/components/devtools/Cargo.toml b/components/devtools/Cargo.toml index 20c2ff5abc8..8ec3b58ae83 100644 --- a/components/devtools/Cargo.toml +++ b/components/devtools/Cargo.toml @@ -24,3 +24,4 @@ msg = {path = "../msg"} serde = "1.0" serde_json = "1.0" time = "0.1" +uuid = {version = "0.7", features = ["v4"]} diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 82d44266afe..a4d8f0b1591 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -19,6 +19,7 @@ use msg::constellation_msg::PipelineId; use serde_json::{self, Map, Number, Value}; use std::cell::RefCell; use std::net::TcpStream; +use uuid::Uuid; trait EncodableConsoleMessage { fn encode(&self) -> serde_json::Result<String>; @@ -72,11 +73,30 @@ struct EvaluateJSReply { result: Value, timestamp: u64, exception: Value, - exceptionMessage: String, + exceptionMessage: Value, helperResult: Value, } #[derive(Serialize)] +struct EvaluateJSEvent { + from: String, + r#type: String, + input: String, + result: Value, + timestamp: u64, + resultID: String, + exception: Value, + exceptionMessage: Value, + helperResult: Value, +} + +#[derive(Serialize)] +struct EvaluateJSAsyncReply { + from: String, + resultID: String, +} + +#[derive(Serialize)] struct SetPreferencesReply { from: String, updated: Vec<String>, @@ -89,6 +109,86 @@ pub struct ConsoleActor { pub streams: RefCell<Vec<TcpStream>>, } +impl ConsoleActor { + fn evaluateJS( + &self, + registry: &ActorRegistry, + msg: &Map<String, Value>, + ) -> Result<EvaluateJSReply, ()> { + let input = msg.get("text").unwrap().as_str().unwrap().to_owned(); + let (chan, port) = ipc::channel().unwrap(); + self.script_chan + .send(DevtoolScriptControlMsg::EvaluateJS( + self.pipeline, + input.clone(), + chan, + )) + .unwrap(); + + //TODO: extract conversion into protocol module or some other useful place + let result = match port.recv().map_err(|_| ())? { + VoidValue => { + let mut m = Map::new(); + m.insert("type".to_owned(), Value::String("undefined".to_owned())); + Value::Object(m) + }, + NullValue => { + let mut m = Map::new(); + m.insert("type".to_owned(), Value::String("null".to_owned())); + Value::Object(m) + }, + BooleanValue(val) => Value::Bool(val), + NumberValue(val) => { + if val.is_nan() { + let mut m = Map::new(); + m.insert("type".to_owned(), Value::String("NaN".to_owned())); + Value::Object(m) + } else if val.is_infinite() { + let mut m = Map::new(); + if val < 0. { + m.insert("type".to_owned(), Value::String("-Infinity".to_owned())); + } else { + m.insert("type".to_owned(), Value::String("Infinity".to_owned())); + } + Value::Object(m) + } else if val == 0. && val.is_sign_negative() { + let mut m = Map::new(); + m.insert("type".to_owned(), Value::String("-0".to_owned())); + Value::Object(m) + } else { + Value::Number(Number::from_f64(val).unwrap()) + } + }, + StringValue(s) => Value::String(s), + ActorValue { class, uuid } => { + //TODO: make initial ActorValue message include these properties? + let mut m = Map::new(); + let actor = ObjectActor::new(registry, uuid); + + m.insert("type".to_owned(), Value::String("object".to_owned())); + m.insert("class".to_owned(), Value::String(class)); + m.insert("actor".to_owned(), Value::String(actor)); + m.insert("extensible".to_owned(), Value::Bool(true)); + m.insert("frozen".to_owned(), Value::Bool(false)); + m.insert("sealed".to_owned(), Value::Bool(false)); + Value::Object(m) + }, + }; + + //TODO: catch and return exception values from JS evaluation + let reply = EvaluateJSReply { + from: self.name(), + input: input, + result: result, + timestamp: 0, + exception: Value::Null, + exceptionMessage: Value::Null, + helperResult: Value::Null, + }; + std::result::Result::Ok(reply) + } +} + impl Actor for ConsoleActor { fn name(&self) -> String { self.name.clone() @@ -191,76 +291,33 @@ impl Actor for ConsoleActor { }, "evaluateJS" => { - let input = msg.get("text").unwrap().as_str().unwrap().to_owned(); - let (chan, port) = ipc::channel().unwrap(); - self.script_chan - .send(DevtoolScriptControlMsg::EvaluateJS( - self.pipeline, - input.clone(), - chan, - )) - .unwrap(); - - //TODO: extract conversion into protocol module or some other useful place - let result = match port.recv().map_err(|_| ())? { - VoidValue => { - let mut m = Map::new(); - m.insert("type".to_owned(), Value::String("undefined".to_owned())); - Value::Object(m) - }, - NullValue => { - let mut m = Map::new(); - m.insert("type".to_owned(), Value::String("null".to_owned())); - Value::Object(m) - }, - BooleanValue(val) => Value::Bool(val), - NumberValue(val) => { - if val.is_nan() { - let mut m = Map::new(); - m.insert("type".to_owned(), Value::String("NaN".to_owned())); - Value::Object(m) - } else if val.is_infinite() { - let mut m = Map::new(); - if val < 0. { - m.insert("type".to_owned(), Value::String("-Infinity".to_owned())); - } else { - m.insert("type".to_owned(), Value::String("Infinity".to_owned())); - } - Value::Object(m) - } else if val == 0. && val.is_sign_negative() { - let mut m = Map::new(); - m.insert("type".to_owned(), Value::String("-0".to_owned())); - Value::Object(m) - } else { - Value::Number(Number::from_f64(val).unwrap()) - } - }, - StringValue(s) => Value::String(s), - ActorValue { class, uuid } => { - //TODO: make initial ActorValue message include these properties? - let mut m = Map::new(); - let actor = ObjectActor::new(registry, uuid); + let msg = self.evaluateJS(®istry, &msg); + stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, - m.insert("type".to_owned(), Value::String("object".to_owned())); - m.insert("class".to_owned(), Value::String(class)); - m.insert("actor".to_owned(), Value::String(actor)); - m.insert("extensible".to_owned(), Value::Bool(true)); - m.insert("frozen".to_owned(), Value::Bool(false)); - m.insert("sealed".to_owned(), Value::Bool(false)); - Value::Object(m) - }, + "evaluateJSAsync" => { + let resultID = Uuid::new_v4().to_string(); + let early_reply = EvaluateJSAsyncReply { + from: self.name(), + resultID: resultID.clone(), }; - - //TODO: catch and return exception values from JS evaluation - let msg = EvaluateJSReply { + // Emit an eager reply so that the client starts listening + // for an async event with the resultID + stream.write_json_packet(&early_reply); + let reply = self.evaluateJS(®istry, &msg).unwrap(); + let msg = EvaluateJSEvent { from: self.name(), - input: input, - result: result, - timestamp: 0, - exception: Value::Object(Map::new()), - exceptionMessage: "".to_owned(), - helperResult: Value::Object(Map::new()), + r#type: "evaluationResult".to_owned(), + input: reply.input, + result: reply.result, + timestamp: reply.timestamp, + resultID: resultID, + exception: reply.exception, + exceptionMessage: reply.exceptionMessage, + helperResult: reply.helperResult, }; + // Send the data from evaluateJS along with a resultID stream.write_json_packet(&msg); ActorMessageStatus::Processed }, diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index 28da033b807..b57c70ac0c9 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -23,7 +23,7 @@ euclid = "0.19" fnv = "1.0" fontsan = {git = "https://github.com/servo/fontsan"} gfx_traits = {path = "../gfx_traits"} -harfbuzz-sys = "0.2" +harfbuzz-sys = "0.3" ipc-channel = "0.11" lazy_static = "1" libc = "0.2" @@ -41,7 +41,7 @@ smallvec = { version = "0.6", features = ["std", "union"] } style = {path = "../style", features = ["servo"]} time = "0.1.12" unicode-bidi = {version = "0.3", features = ["with_serde"]} -unicode-script = {version = "0.2", features = ["harfbuzz"]} +unicode-script = {version = "0.3", features = ["harfbuzz"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} xi-unicode = "0.1.0" ucd = "0.1.1" diff --git a/components/gfx/text/shaping/harfbuzz.rs b/components/gfx/text/shaping/harfbuzz.rs index 88cd19d59ff..ce8ce628f08 100644 --- a/components/gfx/text/shaping/harfbuzz.rs +++ b/components/gfx/text/shaping/harfbuzz.rs @@ -24,7 +24,6 @@ use harfbuzz_sys::hb_feature_t; use harfbuzz_sys::hb_font_create; use harfbuzz_sys::hb_font_funcs_create; use harfbuzz_sys::hb_font_funcs_set_glyph_h_advance_func; -use harfbuzz_sys::hb_font_funcs_set_glyph_h_kerning_func; use harfbuzz_sys::hb_font_funcs_set_nominal_glyph_func; use harfbuzz_sys::hb_font_set_funcs; use harfbuzz_sys::hb_font_set_ppem; @@ -459,12 +458,6 @@ lazy_static! { ptr::null_mut(), None, ); - hb_font_funcs_set_glyph_h_kerning_func( - hb_funcs, - Some(glyph_h_kerning_func), - ptr::null_mut(), - None, - ); FontFuncs(hb_funcs) }; @@ -519,22 +512,6 @@ fn glyph_space_advance(font: *const Font) -> (hb_codepoint_t, f64) { (space_glyph, space_advance) } -extern "C" fn glyph_h_kerning_func( - _: *mut hb_font_t, - font_data: *mut c_void, - first_glyph: hb_codepoint_t, - second_glyph: hb_codepoint_t, - _: *mut c_void, -) -> hb_position_t { - let font: *mut Font = font_data as *mut Font; - assert!(!font.is_null()); - - unsafe { - let advance = (*font).glyph_h_kerning(first_glyph as GlyphId, second_glyph as GlyphId); - Shaper::float_to_fixed(advance) - } -} - // Callback to get a font table out of a font. extern "C" fn font_table_func( _: *mut hb_face_t, diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 423d5ac9326..247a4623a43 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -51,7 +51,7 @@ smallvec = { version = "0.6", features = ["std", "union"] } style = {path = "../style", features = ["servo"]} style_traits = {path = "../style_traits"} unicode-bidi = {version = "0.3", features = ["with_serde"]} -unicode-script = {version = "0.2", features = ["harfbuzz"]} +unicode-script = {version = "0.3", features = ["harfbuzz"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} xi-unicode = "0.1.0" diff --git a/components/layout/display_list/gradient.rs b/components/layout/display_list/gradient.rs index db5208eadac..9b19ee48299 100644 --- a/components/layout/display_list/gradient.rs +++ b/components/layout/display_list/gradient.rs @@ -12,7 +12,7 @@ use style::values::computed::image::{EndingShape, LineDirection}; use style::values::computed::{Angle, GradientItem, LengthPercentage, Percentage, Position}; use style::values::generics::image::EndingShape as GenericEndingShape; use style::values::generics::image::GradientItem as GenericGradientItem; -use style::values::generics::image::{Circle, Ellipse, ShapeExtent}; +use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent}; use style::values::specified::position::{X, Y}; use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient}; @@ -92,7 +92,14 @@ fn convert_gradient_stops( let mut stop_items = gradient_items .iter() .filter_map(|item| match *item { - GenericGradientItem::ColorStop(ref stop) => Some(*stop), + GenericGradientItem::SimpleColorStop(color) => Some(ColorStop { + color, + position: None, + }), + GenericGradientItem::ComplexColorStop { color, position } => Some(ColorStop { + color, + position: Some(position), + }), _ => None, }) .collect::<Vec<_>>(); diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index ff62cc79638..ccc3de5ec7e 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -61,8 +61,8 @@ use style::selector_parser::RestyleDamage; use style::servo::restyle_damage::ServoRestyleDamage; use style::str::char_is_whitespace; use style::values::computed::counters::ContentItem; -use style::values::computed::{LengthPercentage, LengthPercentageOrAuto, Size}; -use style::values::generics::box_::{Perspective, VerticalAlign}; +use style::values::computed::{LengthPercentage, LengthPercentageOrAuto, Size, VerticalAlign}; +use style::values::generics::box_::{Perspective, VerticalAlignKeyword}; use style::values::generics::transform; use style::Zero; use webrender_api::{self, LayoutTransform}; @@ -2397,47 +2397,49 @@ impl Fragment { // FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing // to do. match style.get_box().vertical_align { - VerticalAlign::Baseline => {}, - VerticalAlign::Middle => { - let font_metrics = - with_thread_local_font_context(layout_context, |font_context| { - text::font_metrics_for_style(font_context, self.style.clone_font()) - }); - offset += (content_inline_metrics.ascent - - content_inline_metrics.space_below_baseline - - font_metrics.x_height) - .scale_by(0.5) - }, - VerticalAlign::Sub => { - offset += minimum_line_metrics - .space_needed() - .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO) - }, - VerticalAlign::Super => { - offset -= minimum_line_metrics - .space_needed() - .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO) - }, - VerticalAlign::TextTop => { - offset = self.content_inline_metrics(layout_context).ascent - - minimum_line_metrics.space_above_baseline - }, - VerticalAlign::TextBottom => { - offset = minimum_line_metrics.space_below_baseline - - self.content_inline_metrics(layout_context) - .space_below_baseline - }, - VerticalAlign::Top => { - if let Some(actual_line_metrics) = actual_line_metrics { - offset = - content_inline_metrics.ascent - actual_line_metrics.space_above_baseline - } - }, - VerticalAlign::Bottom => { - if let Some(actual_line_metrics) = actual_line_metrics { - offset = actual_line_metrics.space_below_baseline - - content_inline_metrics.space_below_baseline - } + VerticalAlign::Keyword(kw) => match kw { + VerticalAlignKeyword::Baseline => {}, + VerticalAlignKeyword::Middle => { + let font_metrics = + with_thread_local_font_context(layout_context, |font_context| { + text::font_metrics_for_style(font_context, self.style.clone_font()) + }); + offset += (content_inline_metrics.ascent - + content_inline_metrics.space_below_baseline - + font_metrics.x_height) + .scale_by(0.5) + }, + VerticalAlignKeyword::Sub => { + offset += minimum_line_metrics + .space_needed() + .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO) + }, + VerticalAlignKeyword::Super => { + offset -= minimum_line_metrics + .space_needed() + .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO) + }, + VerticalAlignKeyword::TextTop => { + offset = self.content_inline_metrics(layout_context).ascent - + minimum_line_metrics.space_above_baseline + }, + VerticalAlignKeyword::TextBottom => { + offset = minimum_line_metrics.space_below_baseline - + self.content_inline_metrics(layout_context) + .space_below_baseline + }, + VerticalAlignKeyword::Top => { + if let Some(actual_line_metrics) = actual_line_metrics { + offset = content_inline_metrics.ascent - + actual_line_metrics.space_above_baseline + } + }, + VerticalAlignKeyword::Bottom => { + if let Some(actual_line_metrics) = actual_line_metrics { + offset = actual_line_metrics.space_below_baseline - + content_inline_metrics.space_below_baseline + } + }, }, VerticalAlign::Length(ref lp) => { offset -= lp.to_used_value(minimum_line_metrics.space_needed()); @@ -3087,15 +3089,22 @@ impl Fragment { /// Returns true if any of the inline styles associated with this fragment have /// `vertical-align` set to `top` or `bottom`. pub fn is_vertically_aligned_to_top_or_bottom(&self) -> bool { - match self.style.get_box().vertical_align { - VerticalAlign::Top | VerticalAlign::Bottom => return true, - _ => {}, + fn is_top_or_bottom(v: &VerticalAlign) -> bool { + match *v { + VerticalAlign::Keyword(VerticalAlignKeyword::Top) | + VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => true, + _ => false, + } + } + + if is_top_or_bottom(&self.style.get_box().vertical_align) { + return true; } + if let Some(ref inline_context) = self.inline_context { for node in &inline_context.nodes { - match node.style.get_box().vertical_align { - VerticalAlign::Top | VerticalAlign::Bottom => return true, - _ => {}, + if is_top_or_bottom(&node.style.get_box().vertical_align) { + return true; } } } diff --git a/components/layout/inline.rs b/components/layout/inline.rs index bef95a64f96..f4dacefc5ef 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -41,7 +41,7 @@ use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use style::properties::ComputedValues; use style::servo::restyle_damage::ServoRestyleDamage; use style::values::computed::box_::VerticalAlign; -use style::values::generics::box_::VerticalAlign as GenericVerticalAlign; +use style::values::generics::box_::VerticalAlignKeyword; use style::values::specified::text::TextOverflowSide; use unicode_bidi as bidi; @@ -1269,13 +1269,13 @@ impl InlineFlow { let mut largest_block_size_for_top_fragments = Au(0); let mut largest_block_size_for_bottom_fragments = Au(0); - // We use `VerticalAlign::Baseline` here because `vertical-align` must + // We use `VerticalAlign::baseline()` here because `vertical-align` must // not apply to the inside of inline blocks. update_line_metrics_for_fragment( &mut line_metrics, &inline_metrics, style.get_box().display, - GenericVerticalAlign::Baseline, + VerticalAlign::baseline(), &mut largest_block_size_for_top_fragments, &mut largest_block_size_for_bottom_fragments, ); @@ -1322,11 +1322,20 @@ impl InlineFlow { largest_block_size_for_top_fragments: &mut Au, largest_block_size_for_bottom_fragments: &mut Au, ) { + // FIXME(emilio): This should probably be handled. + let vertical_align_value = match vertical_align_value { + VerticalAlign::Keyword(kw) => kw, + VerticalAlign::Length(..) => { + *line_metrics = line_metrics.new_metrics_for_fragment(inline_metrics); + return; + }, + }; + match (display_value, vertical_align_value) { - (Display::Inline, GenericVerticalAlign::Top) | - (Display::Block, GenericVerticalAlign::Top) | - (Display::InlineFlex, GenericVerticalAlign::Top) | - (Display::InlineBlock, GenericVerticalAlign::Top) + (Display::Inline, VerticalAlignKeyword::Top) | + (Display::Block, VerticalAlignKeyword::Top) | + (Display::InlineFlex, VerticalAlignKeyword::Top) | + (Display::InlineBlock, VerticalAlignKeyword::Top) if inline_metrics.space_above_baseline >= Au(0) => { *largest_block_size_for_top_fragments = max( @@ -1334,10 +1343,10 @@ impl InlineFlow { inline_metrics.space_above_baseline + inline_metrics.space_below_baseline, ) }, - (Display::Inline, GenericVerticalAlign::Bottom) | - (Display::Block, GenericVerticalAlign::Bottom) | - (Display::InlineFlex, GenericVerticalAlign::Bottom) | - (Display::InlineBlock, GenericVerticalAlign::Bottom) + (Display::Inline, VerticalAlignKeyword::Bottom) | + (Display::Block, VerticalAlignKeyword::Bottom) | + (Display::InlineFlex, VerticalAlignKeyword::Bottom) | + (Display::InlineBlock, VerticalAlignKeyword::Bottom) if inline_metrics.space_below_baseline >= Au(0) => { *largest_block_size_for_bottom_fragments = max( diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 682a1c38ec4..ec79f9f4540 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -23,7 +23,7 @@ use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMo use style::properties::ComputedValues; use style::values::computed::length::Size; use style::values::computed::Color; -use style::values::generics::box_::VerticalAlign; +use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword}; use style::values::specified::BorderStyle; #[allow(unsafe_code)] @@ -138,11 +138,11 @@ impl TableCellFlow { self.block_flow.fragment.border_padding.block_start_end(); let kids_self_gap = self_size - kids_size; - // This offset should also account for VerticalAlign::Baseline. + // This offset should also account for VerticalAlign::baseline. // Need max cell ascent from the first row of this cell. let offset = match self.block_flow.fragment.style().get_box().vertical_align { - VerticalAlign::Middle => kids_self_gap / 2, - VerticalAlign::Bottom => kids_self_gap, + VerticalAlign::Keyword(VerticalAlignKeyword::Middle) => kids_self_gap / 2, + VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => kids_self_gap, _ => Au(0), }; if offset == Au(0) { diff --git a/components/layout/text.rs b/components/layout/text.rs index b26e531e670..199bd7e07f2 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -21,13 +21,13 @@ use std::collections::LinkedList; use std::mem; use std::sync::Arc; use style::computed_values::text_rendering::T as TextRendering; -use style::computed_values::text_transform::T as TextTransform; use style::computed_values::white_space::T as WhiteSpace; use style::computed_values::word_break::T as WordBreak; use style::logical_geometry::{LogicalSize, WritingMode}; use style::properties::style_structs::Font as FontStyleStruct; use style::properties::ComputedValues; use style::values::generics::text::LineHeight; +use style::values::specified::text::{TextTransform, TextTransformCase}; use unicode_bidi as bidi; use unicode_script::{get_script, Script}; use xi_unicode::LineBreakLeafIter; @@ -738,23 +738,23 @@ fn apply_style_transform_if_necessary( last_whitespace: bool, is_first_run: bool, ) { - match text_transform { - TextTransform::None => {}, - TextTransform::Uppercase => { + match text_transform.case_ { + TextTransformCase::None => {}, + TextTransformCase::Uppercase => { let original = string[first_character_position..].to_owned(); string.truncate(first_character_position); for ch in original.chars().flat_map(|ch| ch.to_uppercase()) { string.push(ch); } }, - TextTransform::Lowercase => { + TextTransformCase::Lowercase => { let original = string[first_character_position..].to_owned(); string.truncate(first_character_position); for ch in original.chars().flat_map(|ch| ch.to_lowercase()) { string.push(ch); } }, - TextTransform::Capitalize => { + TextTransformCase::Capitalize => { let original = string[first_character_position..].to_owned(); string.truncate(first_character_position); diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 88eedef7d17..301a7500713 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -973,6 +973,11 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } #[inline] + fn is_part(&self, _name: &Atom) -> bool { + false + } + + #[inline] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { unsafe { self.element.has_class_for_layout(name, case_sensitivity) } } @@ -1484,6 +1489,12 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { false } + #[inline] + fn is_part(&self, _name: &Atom) -> bool { + debug!("ServoThreadSafeLayoutElement::is_part called"); + false + } + fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool { debug!("ServoThreadSafeLayoutElement::has_class called"); false diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml index e7acb0de25e..609a977b1dd 100644 --- a/components/malloc_size_of/Cargo.toml +++ b/components/malloc_size_of/Cargo.toml @@ -14,7 +14,6 @@ servo = [ "hyper", "hyper_serde", "keyboard-types", - "mozjs", "serde", "serde_bytes", "string_cache", @@ -33,7 +32,6 @@ hashglobe = { path = "../hashglobe" } hyper = { version = "0.12", optional = true } hyper_serde = { version = "0.9", optional = true } keyboard-types = {version = "0.4.3", optional = true} -mozjs = { version = "0.10.0", optional = true} selectors = { path = "../selectors" } serde = { version = "1.0.27", optional = true } serde_bytes = { version = "0.10", optional = true } diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs index bb4fe1a6adc..d65f8e957e4 100644 --- a/components/malloc_size_of/lib.rs +++ b/components/malloc_size_of/lib.rs @@ -58,8 +58,6 @@ extern crate hyper; extern crate hyper_serde; #[cfg(feature = "servo")] extern crate keyboard_types; -#[cfg(feature = "servo")] -extern crate mozjs as js; extern crate selectors; #[cfg(feature = "servo")] extern crate serde; @@ -749,6 +747,7 @@ where Component::ExplicitUniversalType | Component::LocalName(..) | Component::ID(..) | + Component::Part(..) | Component::Class(..) | Component::AttributeInNoNamespaceExists { .. } | Component::AttributeInNoNamespace { .. } | @@ -778,12 +777,6 @@ impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf } } -impl MallocSizeOf for selectors::context::QuirksMode { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - impl MallocSizeOf for Void { #[inline] fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { @@ -798,15 +791,6 @@ impl<Static: string_cache::StaticAtomSet> MallocSizeOf for string_cache::Atom<St } } -// This is measured properly by the heap measurement implemented in -// SpiderMonkey. -#[cfg(feature = "servo")] -impl<T: Copy + js::rust::GCMethods> MallocSizeOf for js::jsapi::Heap<T> { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - /// For use on types where size_of() returns 0. #[macro_export] macro_rules! malloc_size_of_is_0( diff --git a/components/script/dom/audiobuffer.rs b/components/script/dom/audiobuffer.rs index a021b61ac8c..cabd281dc63 100644 --- a/components/script/dom/audiobuffer.rs +++ b/components/script/dom/audiobuffer.rs @@ -42,6 +42,7 @@ type JSAudioChannel = Heap<*mut JSObject>; pub struct AudioBuffer { reflector_: Reflector, /// Float32Arrays returned by calls to GetChannelData. + #[ignore_malloc_size_of = "mozjs"] js_channels: DomRefCell<Vec<JSAudioChannel>>, /// Aggregates the data from js_channels. /// This is Some<T> iff the buffers in js_channels are detached. diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 56805e4d9fe..023917c172c 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -105,6 +105,7 @@ use servo_media::audio::panner_node::{DistanceModel, PanningModel}; use servo_media::audio::param::ParamType; use servo_media::player::Player; use servo_media::streams::registry::MediaStreamId; +use servo_media::streams::MediaStreamType; use servo_media::webrtc::WebRtcController; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use smallvec::SmallVec; @@ -138,7 +139,7 @@ use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec}; use uuid::Uuid; use webrender_api::{DocumentId, ImageKey, RenderApiSender}; -use webvr_traits::WebVRGamepadHand; +use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; /// A trait to allow tracing (only) DOM objects. pub unsafe trait JSTraceable { @@ -478,7 +479,7 @@ unsafe_no_jsmanaged_fields!(WebGLVertexArrayId); unsafe_no_jsmanaged_fields!(WebGLVersion); unsafe_no_jsmanaged_fields!(WebGLSLVersion); unsafe_no_jsmanaged_fields!(MediaList); -unsafe_no_jsmanaged_fields!(WebVRGamepadHand); +unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand); unsafe_no_jsmanaged_fields!(ScriptToConstellationChan); unsafe_no_jsmanaged_fields!(InteractiveMetrics); unsafe_no_jsmanaged_fields!(InteractiveWindow); @@ -490,7 +491,7 @@ unsafe_no_jsmanaged_fields!(NodeId); unsafe_no_jsmanaged_fields!(AnalysisEngine, DistanceModel, PanningModel, ParamType); unsafe_no_jsmanaged_fields!(dyn Player); unsafe_no_jsmanaged_fields!(WebRtcController); -unsafe_no_jsmanaged_fields!(MediaStreamId); +unsafe_no_jsmanaged_fields!(MediaStreamId, MediaStreamType); unsafe_no_jsmanaged_fields!(Mutex<MediaFrameRenderer>); unsafe_no_jsmanaged_fields!(RenderApiSender); unsafe_no_jsmanaged_fields!(ResourceFetchTiming); diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs index 3d329119f4e..39824e334a2 100644 --- a/components/script/dom/customelementregistry.rs +++ b/components/script/dom/customelementregistry.rs @@ -719,7 +719,7 @@ pub enum CustomElementReaction { Upgrade(#[ignore_malloc_size_of = "Rc"] Rc<CustomElementDefinition>), Callback( #[ignore_malloc_size_of = "Rc"] Rc<Function>, - Box<[Heap<JSVal>]>, + #[ignore_malloc_size_of = "mozjs"] Box<[Heap<JSVal>]>, ), } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 2d12e93774d..1b918c0537b 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -3075,6 +3075,10 @@ impl<'a> SelectorsElement for DomRoot<Element> { .map_or(false, |atom| case_sensitivity.eq_atom(id, atom)) } + fn is_part(&self, _name: &Atom) -> bool { + false + } + fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { Element::has_class(&**self, name, case_sensitivity) } diff --git a/components/script/dom/extendablemessageevent.rs b/components/script/dom/extendablemessageevent.rs index f9c2de13a4b..7fd16aafec4 100644 --- a/components/script/dom/extendablemessageevent.rs +++ b/components/script/dom/extendablemessageevent.rs @@ -24,6 +24,7 @@ use servo_atoms::Atom; #[dom_struct] pub struct ExtendableMessageEvent { event: ExtendableEvent, + #[ignore_malloc_size_of = "mozjs"] data: Heap<JSVal>, origin: DOMString, lastEventId: DOMString, diff --git a/components/script/dom/filereader.rs b/components/script/dom/filereader.rs index 6092dcc9935..55989078d4f 100644 --- a/components/script/dom/filereader.rs +++ b/components/script/dom/filereader.rs @@ -83,7 +83,7 @@ pub enum FileReaderReadyState { #[derive(JSTraceable, MallocSizeOf)] pub enum FileReaderResult { - ArrayBuffer(Heap<JSVal>), + ArrayBuffer(#[ignore_malloc_size_of = "mozjs"] Heap<JSVal>), String(DOMString), } diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index 3e65b0a11f5..fe86a3b2329 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -32,6 +32,7 @@ pub struct Gamepad { connected: Cell<bool>, timestamp: Cell<f64>, mapping_type: String, + #[ignore_malloc_size_of = "mozjs"] axes: Heap<*mut JSObject>, buttons: Dom<GamepadButtonList>, pose: Option<Dom<VRPose>>, diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 91112299034..33458bf2ece 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -145,6 +145,7 @@ pub struct GlobalScope { /// they're consumed before it'd be reported. /// /// <https://html.spec.whatwg.org/multipage/#about-to-be-notified-rejected-promises-list> + #[ignore_malloc_size_of = "mozjs"] uncaught_rejections: DomRefCell<Vec<Box<Heap<*mut JSObject>>>>, /// Promises in this list have previously been reported as rejected @@ -152,6 +153,7 @@ pub struct GlobalScope { /// in the last turn of the event loop. /// /// <https://html.spec.whatwg.org/multipage/#outstanding-rejected-promises-weak-set> + #[ignore_malloc_size_of = "mozjs"] consumed_rejections: DomRefCell<Vec<Box<Heap<*mut JSObject>>>>, } diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index e468b79440c..787591af8a3 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -40,6 +40,7 @@ enum PushOrReplace { pub struct History { reflector_: Reflector, window: Dom<Window>, + #[ignore_malloc_size_of = "mozjs"] state: Heap<JSVal>, state_id: Cell<Option<HistoryStateId>>, } diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 8d41a685b7b..9bbfb30d46c 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -62,7 +62,6 @@ use html5ever::{LocalName, Prefix}; use http::header::{self, HeaderMap, HeaderValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; -use mime::{self, Mime}; use net_traits::image::base::Image; use net_traits::image_cache::ImageResponse; use net_traits::request::{CredentialsMode, Destination, Referrer, RequestBuilder}; @@ -73,7 +72,7 @@ use servo_config::pref; use servo_media::player::context::{GlContext, NativeDisplay, PlayerGLContext}; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, StreamType}; -use servo_media::ServoMedia; +use servo_media::{ServoMedia, SupportsMediaType}; use servo_url::ServoUrl; use std::cell::Cell; use std::collections::VecDeque; @@ -840,9 +839,13 @@ impl HTMLMediaElement { self.fetch_request(None); }, SrcObject::MediaStream(ref stream) => { - for stream in stream.get_tracks() { - if let Err(_) = - self.player.borrow().as_ref().unwrap().set_stream(&stream) + for stream in &*stream.get_tracks() { + if let Err(_) = self + .player + .borrow() + .as_ref() + .unwrap() + .set_stream(&stream.id()) { self.queue_dedicated_media_source_failure_steps(); } @@ -1674,20 +1677,10 @@ impl HTMLMediaElementMethods for HTMLMediaElement { // https://html.spec.whatwg.org/multipage/#dom-navigator-canplaytype fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult { - match type_.parse::<Mime>() { - // XXX GStreamer is currently not very reliable playing OGG and most of - // the media related WPTs uses OGG if we report that we are able to - // play this type. So we report that we are unable to play it to force - // the usage of other types. - // https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/issues/520 - Ok(ref mime) - if (mime.type_() == mime::APPLICATION && mime.subtype() == mime::OCTET_STREAM) || - (mime.subtype() == mime::OGG) => - { - CanPlayTypeResult::_empty - }, - Err(_) => CanPlayTypeResult::_empty, - _ => CanPlayTypeResult::Maybe, + match ServoMedia::get().unwrap().can_play_type(&type_) { + SupportsMediaType::No => CanPlayTypeResult::_empty, + SupportsMediaType::Maybe => CanPlayTypeResult::Maybe, + SupportsMediaType::Probably => CanPlayTypeResult::Probably, } } diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index 9566c7b0f39..29c15f665a4 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -25,6 +25,7 @@ pub struct ImageData { reflector_: Reflector, width: u32, height: u32, + #[ignore_malloc_size_of = "mozjs"] data: Heap<*mut JSObject>, } diff --git a/components/script/dom/mediadevices.rs b/components/script/dom/mediadevices.rs index 0a4f00e2472..bcc575a2ef6 100644 --- a/components/script/dom/mediadevices.rs +++ b/components/script/dom/mediadevices.rs @@ -14,9 +14,11 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::mediastream::MediaStream; +use crate::dom::mediastreamtrack::MediaStreamTrack; use crate::dom::promise::Promise; use dom_struct::dom_struct; use servo_media::streams::capture::{Constrain, ConstrainRange, MediaTrackConstraintSet}; +use servo_media::streams::MediaStreamType; use servo_media::ServoMedia; use std::rc::Rc; @@ -51,18 +53,20 @@ impl MediaDevicesMethods for MediaDevices { InCompartment::Already(&in_compartment_proof), ); let media = ServoMedia::get().unwrap(); - let mut tracks = vec![]; + let stream = MediaStream::new(&self.global()); if let Some(constraints) = convert_constraints(&constraints.audio) { if let Some(audio) = media.create_audioinput_stream(constraints) { - tracks.push(audio) + let track = MediaStreamTrack::new(&self.global(), audio, MediaStreamType::Audio); + stream.add_track(&track); } } if let Some(constraints) = convert_constraints(&constraints.video) { if let Some(video) = media.create_videoinput_stream(constraints) { - tracks.push(video) + let track = MediaStreamTrack::new(&self.global(), video, MediaStreamType::Video); + stream.add_track(&track); } } - let stream = MediaStream::new(&self.global(), tracks); + p.resolve_native(&stream); p } diff --git a/components/script/dom/mediastream.rs b/components/script/dom/mediastream.rs index 42588afc476..2ae573b290e 100644 --- a/components/script/dom/mediastream.rs +++ b/components/script/dom/mediastream.rs @@ -3,38 +3,131 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::MediaStreamBinding; -use crate::dom::bindings::reflector::reflect_dom_object; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::{self, MediaStreamMethods}; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; +use crate::dom::mediastreamtrack::MediaStreamTrack; +use crate::dom::window::Window; use dom_struct::dom_struct; -use servo_media::streams::registry::MediaStreamId; +use servo_media::streams::MediaStreamType; +use std::cell::Ref; #[dom_struct] pub struct MediaStream { eventtarget: EventTarget, - #[ignore_malloc_size_of = "defined in servo-media"] - tracks: DomRefCell<Vec<MediaStreamId>>, + tracks: DomRefCell<Vec<Dom<MediaStreamTrack>>>, } impl MediaStream { - pub fn new_inherited(tracks: Vec<MediaStreamId>) -> MediaStream { + pub fn new_inherited() -> MediaStream { MediaStream { eventtarget: EventTarget::new_inherited(), - tracks: DomRefCell::new(tracks), + tracks: DomRefCell::new(vec![]), } } - pub fn new(global: &GlobalScope, tracks: Vec<MediaStreamId>) -> DomRoot<MediaStream> { + pub fn new(global: &GlobalScope) -> DomRoot<MediaStream> { reflect_dom_object( - Box::new(MediaStream::new_inherited(tracks)), + Box::new(MediaStream::new_inherited()), global, MediaStreamBinding::Wrap, ) } - pub fn get_tracks(&self) -> Vec<MediaStreamId> { - self.tracks.borrow_mut().clone() + pub fn Constructor(global: &Window) -> Fallible<DomRoot<MediaStream>> { + Ok(MediaStream::new(&global.global())) + } + + pub fn Constructor_(_: &Window, stream: &MediaStream) -> Fallible<DomRoot<MediaStream>> { + Ok(stream.Clone()) + } + + pub fn Constructor__( + global: &Window, + tracks: Vec<DomRoot<MediaStreamTrack>>, + ) -> Fallible<DomRoot<MediaStream>> { + let new = MediaStream::new(&global.global()); + for track in tracks { + // this is quadratic, but shouldn't matter much + // if this becomes a problem we can use a hash map + new.AddTrack(&track) + } + Ok(new) + } + + pub fn get_tracks(&self) -> Ref<[Dom<MediaStreamTrack>]> { + Ref::map(self.tracks.borrow(), |tracks| &**tracks) + } + + pub fn add_track(&self, track: &MediaStreamTrack) { + self.tracks.borrow_mut().push(Dom::from_ref(track)) + } +} + +impl MediaStreamMethods for MediaStream { + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-gettracks + fn GetTracks(&self) -> Vec<DomRoot<MediaStreamTrack>> { + self.tracks + .borrow() + .iter() + .map(|x| DomRoot::from_ref(&**x)) + .collect() + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-getaudiotracks + fn GetAudioTracks(&self) -> Vec<DomRoot<MediaStreamTrack>> { + self.tracks + .borrow() + .iter() + .filter(|x| x.ty() == MediaStreamType::Audio) + .map(|x| DomRoot::from_ref(&**x)) + .collect() + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-getvideotracks + fn GetVideoTracks(&self) -> Vec<DomRoot<MediaStreamTrack>> { + self.tracks + .borrow() + .iter() + .filter(|x| x.ty() == MediaStreamType::Video) + .map(|x| DomRoot::from_ref(&**x)) + .collect() + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-gettrackbyid + fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<MediaStreamTrack>> { + self.tracks + .borrow() + .iter() + .find(|x| x.id().id().to_string() == &*id) + .map(|x| DomRoot::from_ref(&**x)) + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-addtrack + fn AddTrack(&self, track: &MediaStreamTrack) { + let existing = self.tracks.borrow().iter().find(|x| *x == &track).is_some(); + + if existing { + return; + } + self.add_track(track) + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-removetrack + fn RemoveTrack(&self, track: &MediaStreamTrack) { + self.tracks.borrow_mut().retain(|x| *x != track); + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastream-clone + fn Clone(&self) -> DomRoot<MediaStream> { + let new = MediaStream::new(&self.global()); + for track in &*self.tracks.borrow() { + new.add_track(&track) + } + new } } diff --git a/components/script/dom/mediastreamtrack.rs b/components/script/dom/mediastreamtrack.rs new file mode 100644 index 00000000000..2cc0bde7442 --- /dev/null +++ b/components/script/dom/mediastreamtrack.rs @@ -0,0 +1,74 @@ +/* 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::MediaStreamTrackBinding::{ + self, MediaStreamTrackMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use servo_media::streams::registry::MediaStreamId; +use servo_media::streams::MediaStreamType; + +#[dom_struct] +pub struct MediaStreamTrack { + eventtarget: EventTarget, + #[ignore_malloc_size_of = "defined in servo-media"] + id: MediaStreamId, + #[ignore_malloc_size_of = "defined in servo-media"] + ty: MediaStreamType, +} + +impl MediaStreamTrack { + pub fn new_inherited(id: MediaStreamId, ty: MediaStreamType) -> MediaStreamTrack { + MediaStreamTrack { + eventtarget: EventTarget::new_inherited(), + id, + ty, + } + } + + pub fn new( + global: &GlobalScope, + id: MediaStreamId, + ty: MediaStreamType, + ) -> DomRoot<MediaStreamTrack> { + reflect_dom_object( + Box::new(MediaStreamTrack::new_inherited(id, ty)), + global, + MediaStreamTrackBinding::Wrap, + ) + } + + pub fn id(&self) -> MediaStreamId { + self.id + } + + pub fn ty(&self) -> MediaStreamType { + self.ty + } +} + +impl MediaStreamTrackMethods for MediaStreamTrack { + /// https://w3c.github.io/mediacapture-main/#dom-mediastreamtrack-kind + fn Kind(&self) -> DOMString { + match self.ty { + MediaStreamType::Video => "video".into(), + MediaStreamType::Audio => "audio".into(), + } + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastreamtrack-id + fn Id(&self) -> DOMString { + self.id.id().to_string().into() + } + + /// https://w3c.github.io/mediacapture-main/#dom-mediastreamtrack-clone + fn Clone(&self) -> DomRoot<MediaStreamTrack> { + MediaStreamTrack::new(&self.global(), self.id, self.ty) + } +} diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs index edde48f95e6..c02df1e8e61 100644 --- a/components/script/dom/messageevent.rs +++ b/components/script/dom/messageevent.rs @@ -25,6 +25,7 @@ use std::ptr::NonNull; #[dom_struct] pub struct MessageEvent { event: Event, + #[ignore_malloc_size_of = "mozjs"] data: Heap<JSVal>, origin: DOMString, source: Option<Dom<WindowProxy>>, diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index ff1688fb6c3..1dbc1383101 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -399,6 +399,7 @@ pub mod medialist; pub mod mediaquerylist; pub mod mediaquerylistevent; pub mod mediastream; +pub mod mediastreamtrack; pub mod messageevent; pub mod mimetype; pub mod mimetypearray; @@ -450,6 +451,7 @@ pub mod rtcicecandidate; pub mod rtcpeerconnection; pub mod rtcpeerconnectioniceevent; pub mod rtcsessiondescription; +pub mod rtctrackevent; pub mod screen; pub mod serviceworker; pub mod serviceworkercontainer; @@ -540,6 +542,7 @@ pub mod xmlhttprequestupload; pub mod xmlserializer; pub mod xr; pub mod xrframe; +pub mod xrinputsource; pub mod xrlayer; pub mod xrpose; pub mod xrreferencespace; diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index fe119eefbf3..89b9907c95c 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -76,6 +76,7 @@ pub struct PaintWorkletGlobalScope { /// <https://drafts.css-houdini.org/css-paint-api/#paint-definitions> paint_definitions: DomRefCell<HashMap<Atom, Box<PaintDefinition>>>, /// <https://drafts.css-houdini.org/css-paint-api/#paint-class-instances> + #[ignore_malloc_size_of = "mozjs"] paint_class_instances: DomRefCell<HashMap<Atom, Box<Heap<JSVal>>>>, /// The most recent name the worklet was called with cached_name: DomRefCell<Atom>, @@ -473,7 +474,9 @@ pub enum PaintWorkletTask { #[derive(JSTraceable, MallocSizeOf)] #[must_root] struct PaintDefinition { + #[ignore_malloc_size_of = "mozjs"] class_constructor: Heap<JSVal>, + #[ignore_malloc_size_of = "mozjs"] paint_function: Heap<JSVal>, constructor_valid_flag: Cell<bool>, context_alpha_flag: bool, diff --git a/components/script/dom/rtcpeerconnection.rs b/components/script/dom/rtcpeerconnection.rs index 962eb0fb02c..9ec1940bbb3 100644 --- a/components/script/dom/rtcpeerconnection.rs +++ b/components/script/dom/rtcpeerconnection.rs @@ -26,10 +26,12 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::mediastream::MediaStream; +use crate::dom::mediastreamtrack::MediaStreamTrack; use crate::dom::promise::Promise; use crate::dom::rtcicecandidate::RTCIceCandidate; use crate::dom::rtcpeerconnectioniceevent::RTCPeerConnectionIceEvent; use crate::dom::rtcsessiondescription::RTCSessionDescription; +use crate::dom::rtctrackevent::RTCTrackEvent; use crate::dom::window::Window; use crate::task::TaskCanceller; use crate::task_source::networking::NetworkingTaskSource; @@ -37,6 +39,7 @@ use crate::task_source::TaskSource; use dom_struct::dom_struct; use servo_media::streams::registry::MediaStreamId; +use servo_media::streams::MediaStreamType; use servo_media::webrtc::{ BundlePolicy, GatheringState, IceCandidate, IceConnectionState, SdpType, SessionDescription, SignalingState, WebRtcController, WebRtcSignaller, @@ -128,7 +131,17 @@ impl WebRtcSignaller for RTCSignaller { ); } - fn on_add_stream(&self, _: &MediaStreamId) {} + fn on_add_stream(&self, id: &MediaStreamId, ty: MediaStreamType) { + let this = self.trusted.clone(); + let id = *id; + let _ = self.task_source.queue_with_canceller( + task!(on_add_stream: move || { + let this = this.root(); + this.on_add_stream(id, ty); + }), + &self.canceller, + ); + } fn close(&self) { // do nothing @@ -238,6 +251,15 @@ impl RTCPeerConnection { event.upcast::<Event>().fire(self.upcast()); } + fn on_add_stream(&self, id: MediaStreamId, ty: MediaStreamType) { + if self.closed.get() { + return; + } + let track = MediaStreamTrack::new(&self.global(), id, ty); + let event = RTCTrackEvent::new(&self.global(), atom!("track"), false, false, &track); + event.upcast::<Event>().fire(self.upcast()); + } + /// https://www.w3.org/TR/webrtc/#update-ice-gathering-state fn update_gathering_state(&self, state: GatheringState) { // step 1 @@ -399,6 +421,9 @@ impl RTCPeerConnectionMethods for RTCPeerConnection { /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icecandidate event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate); + /// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ontrack + event_handler!(track, GetOntrack, SetOntrack); + /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-iceconnectionstatechange event_handler!( iceconnectionstatechange, @@ -584,10 +609,12 @@ impl RTCPeerConnectionMethods for RTCPeerConnection { // https://w3c.github.io/webrtc-pc/#legacy-interface-extensions fn AddStream(&self, stream: &MediaStream) { - let mut tracks = stream.get_tracks(); - - for ref track in tracks.drain(..) { - self.controller.borrow().as_ref().unwrap().add_stream(track); + for track in &*stream.get_tracks() { + self.controller + .borrow() + .as_ref() + .unwrap() + .add_stream(&track.id()); } } diff --git a/components/script/dom/rtctrackevent.rs b/components/script/dom/rtctrackevent.rs new file mode 100644 index 00000000000..9c5d7bec362 --- /dev/null +++ b/components/script/dom/rtctrackevent.rs @@ -0,0 +1,78 @@ +/* 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::RTCTrackEventBinding::{self, RTCTrackEventMethods}; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::event::Event; +use crate::dom::globalscope::GlobalScope; +use crate::dom::mediastreamtrack::MediaStreamTrack; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use servo_atoms::Atom; + +#[dom_struct] +pub struct RTCTrackEvent { + event: Event, + track: Dom<MediaStreamTrack>, +} + +impl RTCTrackEvent { + #[allow(unrooted_must_root)] + fn new_inherited(track: &MediaStreamTrack) -> RTCTrackEvent { + RTCTrackEvent { + event: Event::new_inherited(), + track: Dom::from_ref(track), + } + } + + pub fn new( + global: &GlobalScope, + type_: Atom, + bubbles: bool, + cancelable: bool, + track: &MediaStreamTrack, + ) -> DomRoot<RTCTrackEvent> { + let trackevent = reflect_dom_object( + Box::new(RTCTrackEvent::new_inherited(&track)), + global, + RTCTrackEventBinding::Wrap, + ); + { + let event = trackevent.upcast::<Event>(); + event.init_event(type_, bubbles, cancelable); + } + trackevent + } + + pub fn Constructor( + window: &Window, + type_: DOMString, + init: &RTCTrackEventBinding::RTCTrackEventInit, + ) -> Fallible<DomRoot<RTCTrackEvent>> { + Ok(RTCTrackEvent::new( + &window.global(), + Atom::from(type_), + init.parent.bubbles, + init.parent.cancelable, + &init.track, + )) + } +} + +impl RTCTrackEventMethods for RTCTrackEvent { + // https://w3c.github.io/webrtc-pc/#dom-rtctrackevent-track + fn Track(&self) -> DomRoot<MediaStreamTrack> { + DomRoot::from_ref(&*self.track) + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } +} diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index a8cb594a2b1..77817bae2fc 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -22,7 +22,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; -use crate::dom::bindings::root::{DomRoot, MutDom, MutNullableDom}; +use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; @@ -36,6 +36,7 @@ use crate::dom::vrpose::VRPose; use crate::dom::vrstageparameters::VRStageParameters; use crate::dom::webglrenderingcontext::WebGLRenderingContext; use crate::dom::xrframe::XRFrame; +use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrsession::XRSession; use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::script_runtime::CommonScriptMsg; @@ -47,11 +48,12 @@ use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; use profile_traits::ipc; use std::cell::Cell; +use std::collections::HashMap; use std::mem; use std::ops::Deref; use std::rc::Rc; use std::thread; -use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRFutureFrameData}; +use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRPoseInformation}; use webvr_traits::{WebVRLayer, WebVRMsg}; #[dom_struct] @@ -86,12 +88,16 @@ pub struct VRDisplay { // Compositor VRFrameData synchonization frame_data_status: Cell<VRFrameDataStatus>, #[ignore_malloc_size_of = "closures are hard"] - frame_data_receiver: DomRefCell<Option<WebGLReceiver<Result<WebVRFutureFrameData, ()>>>>, + frame_data_receiver: DomRefCell<Option<WebGLReceiver<Result<WebVRPoseInformation, ()>>>>, running_display_raf: Cell<bool>, paused: Cell<bool>, stopped_on_pause: Cell<bool>, /// Whether or not this is XR mode, and the session xr_session: MutNullableDom<XRSession>, + /// Have inputs been initialized? (i.e, has getInputSources() been called?) + /// XR only + initialized_inputs: Cell<bool>, + input_sources: DomRefCell<HashMap<u32, Dom<XRInputSource>>>, } unsafe_no_jsmanaged_fields!(WebVRDisplayData); @@ -115,6 +121,8 @@ struct VRRAFUpdate { /// Number uniquely identifying the WebGL context /// so that we may setup/tear down VR compositors as things change context_id: usize, + /// Do we need input data? + needs_inputs: bool, } type VRRAFUpdateSender = Sender<Result<VRRAFUpdate, ()>>; @@ -164,6 +172,8 @@ impl VRDisplay { // When the VR Resume event is received and the flag is set, VR presentation automatically restarts. stopped_on_pause: Cell::new(false), xr_session: MutNullableDom::default(), + initialized_inputs: Cell::new(false), + input_sources: DomRefCell::new(HashMap::new()), } } @@ -627,6 +637,7 @@ impl VRDisplay { depth_far: self.depth_far.get(), api_sender: self.api_sender(), context_id: self.context_id(), + needs_inputs: self.initialized_inputs.get(), } } @@ -690,6 +701,7 @@ impl VRDisplay { let (raf_sender, raf_receiver) = unbounded(); let (wakeup_sender, wakeup_receiver) = unbounded(); *self.raf_wakeup_sender.borrow_mut() = Some(wakeup_sender); + let mut needs_inputs = false; // The render loop at native headset frame rate is implemented using a dedicated thread. // Every loop iteration syncs pose data with the HMD, submits the pixels to the display and waits for Vsync. @@ -726,8 +738,13 @@ impl VRDisplay { .unwrap(); // Run Sync Poses in parallell on Render thread - let msg = - WebVRCommand::SyncPoses(display_id, near, far, sync_sender.clone()); + let msg = WebVRCommand::SyncPoses( + display_id, + near, + far, + needs_inputs, + sync_sender.clone(), + ); api_sender.send_vr(msg).unwrap(); } else { let _ = wakeup_receiver.recv(); @@ -752,6 +769,7 @@ impl VRDisplay { if let Ok(update) = raf_receiver.recv().unwrap() { near = update.depth_near; far = update.depth_far; + needs_inputs = update.needs_inputs; if update.context_id != context_id { if let Some(ref api_sender) = update.api_sender { api_sender @@ -808,8 +826,16 @@ impl VRDisplay { fn sync_frame_data(&self) { let status = if let Some(receiver) = self.frame_data_receiver.borrow().as_ref() { match receiver.recv().unwrap() { - Ok(future_data) => { - *self.frame_data.borrow_mut() = future_data.block(); + Ok(pose) => { + *self.frame_data.borrow_mut() = pose.frame.block(); + if self.initialized_inputs.get() { + let inputs = self.input_sources.borrow(); + for (id, state) in pose.gamepads { + if let Some(input) = inputs.get(&id) { + input.update_state(state); + } + } + } VRFrameDataStatus::Synced }, Err(()) => VRFrameDataStatus::Exit, @@ -909,6 +935,53 @@ impl VRDisplay { pair.1 = None; } } + + /// Initialize XRInputSources + fn initialize_inputs(&self) { + if self.initialized_inputs.get() { + return; + } + self.initialized_inputs.set(true); + + let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); + let display = self.display.borrow().display_id; + self.webvr_thread() + .send(WebVRMsg::GetGamepadsForDisplay(display, sender)) + .unwrap(); + match receiver.recv().unwrap() { + Ok(gamepads) => { + let global = self.global(); + let session = self + .xr_session + .get() + .expect("initialize_inputs called on a VR session"); + let roots: Vec<_> = gamepads + .into_iter() + .map(|g| { + ( + g.1.gamepad_id, + XRInputSource::new(&global, &session, g.0, g.1), + ) + }) + .collect(); + + let mut inputs = self.input_sources.borrow_mut(); + for (id, root) in &roots { + inputs.insert(*id, Dom::from_ref(&root)); + } + }, + Err(_) => {}, + } + } + + pub fn get_input_sources(&self) -> Vec<DomRoot<XRInputSource>> { + self.initialize_inputs(); + self.input_sources + .borrow() + .iter() + .map(|(_, x)| DomRoot::from_ref(&**x)) + .collect() + } } // WebVR Spec: If the number of values in the leftBounds/rightBounds arrays diff --git a/components/script/dom/vreyeparameters.rs b/components/script/dom/vreyeparameters.rs index 2a212ab48bc..b7ea63bbd3e 100644 --- a/components/script/dom/vreyeparameters.rs +++ b/components/script/dom/vreyeparameters.rs @@ -22,6 +22,7 @@ pub struct VREyeParameters { reflector_: Reflector, #[ignore_malloc_size_of = "Defined in rust-webvr"] parameters: DomRefCell<WebVREyeParameters>, + #[ignore_malloc_size_of = "mozjs"] offset: Heap<*mut JSObject>, fov: Dom<VRFieldOfView>, } diff --git a/components/script/dom/vrframedata.rs b/components/script/dom/vrframedata.rs index 08acd74bb42..25f6af821a7 100644 --- a/components/script/dom/vrframedata.rs +++ b/components/script/dom/vrframedata.rs @@ -22,9 +22,13 @@ use webvr_traits::WebVRFrameData; #[dom_struct] pub struct VRFrameData { reflector_: Reflector, + #[ignore_malloc_size_of = "mozjs"] left_proj: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] left_view: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] right_proj: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] right_view: Heap<*mut JSObject>, pose: Dom<VRPose>, timestamp: Cell<f64>, diff --git a/components/script/dom/vrpose.rs b/components/script/dom/vrpose.rs index c807ab72c91..90bca0956ee 100644 --- a/components/script/dom/vrpose.rs +++ b/components/script/dom/vrpose.rs @@ -17,11 +17,17 @@ use webvr_traits::webvr; #[dom_struct] pub struct VRPose { reflector_: Reflector, + #[ignore_malloc_size_of = "mozjs"] position: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] orientation: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] linear_vel: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] angular_vel: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] linear_acc: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] angular_acc: Heap<*mut JSObject>, } diff --git a/components/script/dom/vrstageparameters.rs b/components/script/dom/vrstageparameters.rs index ffbe6ce0f3f..41184091232 100644 --- a/components/script/dom/vrstageparameters.rs +++ b/components/script/dom/vrstageparameters.rs @@ -21,6 +21,7 @@ pub struct VRStageParameters { reflector_: Reflector, #[ignore_malloc_size_of = "Defined in rust-webvr"] parameters: DomRefCell<WebVRStageParameters>, + #[ignore_malloc_size_of = "mozjs"] transform: Heap<*mut JSObject>, } diff --git a/components/script/dom/webidls/MediaStream.webidl b/components/script/dom/webidls/MediaStream.webidl index 0257e3c6061..b1d5664a7f7 100644 --- a/components/script/dom/webidls/MediaStream.webidl +++ b/components/script/dom/webidls/MediaStream.webidl @@ -4,20 +4,20 @@ // https://w3c.github.io/mediacapture-main/#dom-mediastream -// [Exposed=Window, -// Constructor, -// Constructor(MediaStream stream), -// Constructor(sequence<MediaStreamTrack> tracks)] -[Exposed=Window, Pref="dom.webrtc.enabled"] +[Exposed=Window, + Constructor, + Constructor(MediaStream stream), + Constructor(sequence<MediaStreamTrack> tracks), +Pref="dom.webrtc.enabled"] interface MediaStream : EventTarget { // readonly attribute DOMString id; - // sequence<MediaStreamTrack> getAudioTracks(); - // sequence<MediaStreamTrack> getVideoTracks(); - // sequence<MediaStreamTrack> getTracks(); - // MediaStreamTrack? getTrackById(DOMString trackId); - // void addTrack(MediaStreamTrack track); - // void removeTrack(MediaStreamTrack track); - // MediaStream clone(); + sequence<MediaStreamTrack> getAudioTracks(); + sequence<MediaStreamTrack> getVideoTracks(); + sequence<MediaStreamTrack> getTracks(); + MediaStreamTrack? getTrackById(DOMString trackId); + void addTrack(MediaStreamTrack track); + void removeTrack(MediaStreamTrack track); + MediaStream clone(); // readonly attribute boolean active; // attribute EventHandler onaddtrack; // attribute EventHandler onremovetrack; diff --git a/components/script/dom/webidls/MediaStreamTrack.webidl b/components/script/dom/webidls/MediaStreamTrack.webidl new file mode 100644 index 00000000000..2f8bfb0bbec --- /dev/null +++ b/components/script/dom/webidls/MediaStreamTrack.webidl @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/mediacapture-main/#dom-mediastreamtrack + +[Exposed=Window, Pref="dom.webrtc.enabled"] +interface MediaStreamTrack : EventTarget { + readonly attribute DOMString kind; + readonly attribute DOMString id; + // readonly attribute DOMString label; + // attribute boolean enabled; + // readonly attribute boolean muted; + // attribute EventHandler onmute; + // attribute EventHandler onunmute; + // readonly attribute MediaStreamTrackState readyState; + // attribute EventHandler onended; + MediaStreamTrack clone(); + // void stop(); + // MediaTrackCapabilities getCapabilities(); + // MediaTrackConstraints getConstraints(); + // MediaTrackSettings getSettings(); + // Promise<void> applyConstraints(optional MediaTrackConstraints constraints); +}; diff --git a/components/script/dom/webidls/RTCPeerConnection.webidl b/components/script/dom/webidls/RTCPeerConnection.webidl index 0f00f3b5b95..58cb7301ea3 100644 --- a/components/script/dom/webidls/RTCPeerConnection.webidl +++ b/components/script/dom/webidls/RTCPeerConnection.webidl @@ -114,3 +114,15 @@ enum RTCSignalingState { "have-remote-pranswer", "closed" }; + +partial interface RTCPeerConnection { + // sequence<RTCRtpSender> getSenders(); + // sequence<RTCRtpReceiver> getReceivers(); + // sequence<RTCRtpTransceiver> getTransceivers(); + // RTCRtpSender addTrack(MediaStreamTrack track, + // MediaStream... streams); + // void removeTrack(RTCRtpSender sender); + // RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind, + // optional RTCRtpTransceiverInit init); + attribute EventHandler ontrack; +}; diff --git a/components/script/dom/webidls/RTCTrackEvent.webidl b/components/script/dom/webidls/RTCTrackEvent.webidl new file mode 100644 index 00000000000..fa07f36b9f5 --- /dev/null +++ b/components/script/dom/webidls/RTCTrackEvent.webidl @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/webrtc-pc/#dom-rtctrackevent + +[Constructor(DOMString type, RTCTrackEventInit eventInitDict), + Exposed=Window, Pref="dom.webrtc.enabled"] +interface RTCTrackEvent : Event { + // readonly attribute RTCRtpReceiver receiver; + readonly attribute MediaStreamTrack track; + // [SameObject] + // readonly attribute FrozenArray<MediaStream> streams; + // readonly attribute RTCRtpTransceiver transceiver; +}; + +// https://www.w3.org/TR/webrtc/#dom-rtctrackeventinit +dictionary RTCTrackEventInit : EventInit { + // required RTCRtpReceiver receiver; + required MediaStreamTrack track; + // sequence<MediaStream> streams = []; + // required RTCRtpTransceiver transceiver; +}; diff --git a/components/script/dom/webidls/XRInputSource.webidl b/components/script/dom/webidls/XRInputSource.webidl new file mode 100644 index 00000000000..5ad1e48628f --- /dev/null +++ b/components/script/dom/webidls/XRInputSource.webidl @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://immersive-web.github.io/webxr/#xrinputsource-interface + +enum XRHandedness { + "none", + "left", + "right" +}; + +enum XRTargetRayMode { + "gaze", + "tracked-pointer", + "screen" +}; + +[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"] +interface XRInputSource { + readonly attribute XRHandedness handedness; + // [SameObject] readonly attribute XRTargetRayMode targetRayMode; + [SameObject] readonly attribute XRSpace targetRaySpace; + // [SameObject] readonly attribute XRSpace? gripSpace; + // [SameObject] readonly attribute Gamepad? gamepad; +}; diff --git a/components/script/dom/webidls/XRSession.webidl b/components/script/dom/webidls/XRSession.webidl index f22918d469d..0399f2d0bc1 100644 --- a/components/script/dom/webidls/XRSession.webidl +++ b/components/script/dom/webidls/XRSession.webidl @@ -20,12 +20,15 @@ interface XRSession : EventTarget { readonly attribute XREnvironmentBlendMode environmentBlendMode; readonly attribute XRRenderState renderState; - readonly attribute XRSpace viewerSpace; + [SameObject] readonly attribute XRSpace viewerSpace; // // Methods Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceOptions options); + // workaround until we have FrozenArray + // see https://github.com/servo/servo/issues/10427#issuecomment-449593626 // FrozenArray<XRInputSource> getInputSources(); + sequence<XRInputSource> getInputSources(); Promise<void> updateRenderState(optional XRRenderStateInit state); long requestAnimationFrame(XRFrameRequestCallback callback); diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 0b86f0eeb1c..3b9622b3cfb 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -137,6 +137,7 @@ pub struct XMLHttpRequest { response_type: Cell<XMLHttpRequestResponseType>, response_xml: MutNullableDom<Document>, response_blob: MutNullableDom<Blob>, + #[ignore_malloc_size_of = "mozjs"] response_arraybuffer: Heap<*mut JSObject>, #[ignore_malloc_size_of = "Defined in rust-mozjs"] response_json: Heap<JSVal>, diff --git a/components/script/dom/xrinputsource.rs b/components/script/dom/xrinputsource.rs new file mode 100644 index 00000000000..2b623864d56 --- /dev/null +++ b/components/script/dom/xrinputsource.rs @@ -0,0 +1,83 @@ +/* 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::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding; +use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{ + XRHandedness, XRInputSourceMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::xrsession::XRSession; +use crate::dom::xrspace::XRSpace; +use dom_struct::dom_struct; +use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState, WebVRPose}; + +#[dom_struct] +pub struct XRInputSource { + reflector: Reflector, + session: Dom<XRSession>, + #[ignore_malloc_size_of = "Defined in rust-webvr"] + data: WebVRGamepadData, + #[ignore_malloc_size_of = "Defined in rust-webvr"] + state: DomRefCell<WebVRGamepadState>, + target_ray_space: MutNullableDom<XRSpace>, +} + +impl XRInputSource { + pub fn new_inherited( + session: &XRSession, + data: WebVRGamepadData, + state: WebVRGamepadState, + ) -> XRInputSource { + XRInputSource { + reflector: Reflector::new(), + session: Dom::from_ref(session), + data, + state: DomRefCell::new(state), + target_ray_space: Default::default(), + } + } + + pub fn new( + global: &GlobalScope, + session: &XRSession, + data: WebVRGamepadData, + state: WebVRGamepadState, + ) -> DomRoot<XRInputSource> { + reflect_dom_object( + Box::new(XRInputSource::new_inherited(session, data, state)), + global, + XRInputSourceBinding::Wrap, + ) + } + + pub fn update_state(&self, state: WebVRGamepadState) { + *self.state.borrow_mut() = state; + } + + pub fn pose(&self) -> WebVRPose { + self.state.borrow().pose + } +} + +impl XRInputSourceMethods for XRInputSource { + /// https://immersive-web.github.io/webxr/#dom-xrinputsource-handedness + fn Handedness(&self) -> XRHandedness { + match self.data.hand { + WebVRGamepadHand::Unknown => XRHandedness::None, + WebVRGamepadHand::Left => XRHandedness::Left, + WebVRGamepadHand::Right => XRHandedness::Right, + } + } + + /// https://immersive-web.github.io/webxr/#dom-xrinputsource-targetrayspace + fn TargetRaySpace(&self) -> DomRoot<XRSpace> { + self.target_ray_space.or_init(|| { + let global = self.global(); + XRSpace::new_inputspace(&global, &self.session, &self) + }) + } +} diff --git a/components/script/dom/xrreferencespace.rs b/components/script/dom/xrreferencespace.rs index 664dc4f0e23..932f917b5a5 100644 --- a/components/script/dom/xrreferencespace.rs +++ b/components/script/dom/xrreferencespace.rs @@ -110,7 +110,7 @@ impl XRReferenceSpace { // non-subclassed XRReferenceSpaces exist, obtained via the "identity" // type. These are equivalent to the viewer pose and follow the headset // around - XRSpace::viewer_pose_from_frame_data(base_pose) + XRSpace::pose_to_transform(&base_pose.pose) } } } diff --git a/components/script/dom/xrrigidtransform.rs b/components/script/dom/xrrigidtransform.rs index 969ec839292..f75d3f085fb 100644 --- a/components/script/dom/xrrigidtransform.rs +++ b/components/script/dom/xrrigidtransform.rs @@ -27,6 +27,7 @@ pub struct XRRigidTransform { #[ignore_malloc_size_of = "defined in euclid"] transform: RigidTransform3D<f64>, inverse: MutNullableDom<XRRigidTransform>, + #[ignore_malloc_size_of = "defined in mozjs"] matrix: Heap<*mut JSObject>, } @@ -93,7 +94,7 @@ impl XRRigidTransformMethods for XRRigidTransform { } // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation fn Orientation(&self) -> DomRoot<DOMPointReadOnly> { - self.position.or_init(|| { + self.orientation.or_init(|| { let r = &self.transform.rotation; DOMPointReadOnly::new(&self.global(), r.i, r.j, r.k, r.r) }) diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 4b855cba4c8..5aab6341ca4 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -19,6 +19,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::vrdisplay::VRDisplay; +use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrlayer::XRLayer; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrrenderstate::XRRenderState; @@ -33,6 +34,7 @@ pub struct XRSession { display: Dom<VRDisplay>, base_layer: MutNullableDom<XRLayer>, blend_mode: XREnvironmentBlendMode, + viewer_space: MutNullableDom<XRSpace>, } impl XRSession { @@ -43,6 +45,7 @@ impl XRSession { base_layer: Default::default(), // we don't yet support any AR devices blend_mode: XREnvironmentBlendMode::Opaque, + viewer_space: Default::default(), } } @@ -86,7 +89,8 @@ impl XRSessionMethods for XRSession { // https://immersive-web.github.io/webxr/#dom-xrsession-viewerspace fn ViewerSpace(&self) -> DomRoot<XRSpace> { - XRSpace::new_viewerspace(&self.global(), &self) + self.viewer_space + .or_init(|| XRSpace::new_viewerspace(&self.global(), &self)) } /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe @@ -153,4 +157,9 @@ impl XRSessionMethods for XRSession { p } + + /// https://immersive-web.github.io/webxr/#dom-xrsession-getinputsources + fn GetInputSources(&self) -> Vec<DomRoot<XRInputSource>> { + self.display.get_input_sources() + } } diff --git a/components/script/dom/xrspace.rs b/components/script/dom/xrspace.rs index 8948ac03c8e..30f9246c354 100644 --- a/components/script/dom/xrspace.rs +++ b/components/script/dom/xrspace.rs @@ -5,20 +5,22 @@ use crate::dom::bindings::codegen::Bindings::XRSpaceBinding; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object; -use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; +use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrsession::XRSession; use dom_struct::dom_struct; use euclid::{RigidTransform3D, Rotation3D, Vector3D}; -use webvr_traits::WebVRFrameData; +use webvr_traits::{WebVRFrameData, WebVRPose}; #[dom_struct] pub struct XRSpace { eventtarget: EventTarget, session: Dom<XRSession>, is_viewerspace: bool, + input_source: MutNullableDom<XRInputSource>, } impl XRSpace { @@ -27,6 +29,7 @@ impl XRSpace { eventtarget: EventTarget::new_inherited(), session: Dom::from_ref(session), is_viewerspace: false, + input_source: Default::default(), } } @@ -35,6 +38,7 @@ impl XRSpace { eventtarget: EventTarget::new_inherited(), session: Dom::from_ref(session), is_viewerspace: true, + input_source: Default::default(), } } @@ -45,6 +49,27 @@ impl XRSpace { XRSpaceBinding::Wrap, ) } + + fn new_inputspace_inner(session: &XRSession, input: &XRInputSource) -> XRSpace { + XRSpace { + eventtarget: EventTarget::new_inherited(), + session: Dom::from_ref(session), + is_viewerspace: false, + input_source: MutNullableDom::new(Some(input)), + } + } + + pub fn new_inputspace( + global: &GlobalScope, + session: &XRSession, + input: &XRInputSource, + ) -> DomRoot<XRSpace> { + reflect_dom_object( + Box::new(XRSpace::new_inputspace_inner(session, input)), + global, + XRSpaceBinding::Wrap, + ) + } } impl XRSpace { @@ -57,16 +82,18 @@ impl XRSpace { if let Some(reference) = self.downcast::<XRReferenceSpace>() { reference.get_pose(base_pose) } else if self.is_viewerspace { - XRSpace::viewer_pose_from_frame_data(base_pose) + XRSpace::pose_to_transform(&base_pose.pose) + } else if let Some(source) = self.input_source.get() { + XRSpace::pose_to_transform(&source.pose()) } else { unreachable!() } } - pub fn viewer_pose_from_frame_data(data: &WebVRFrameData) -> RigidTransform3D<f64> { - let pos = data.pose.position.unwrap_or([0., 0., 0.]); + pub fn pose_to_transform(pose: &WebVRPose) -> RigidTransform3D<f64> { + let pos = pose.position.unwrap_or([0., 0., 0.]); let translation = Vector3D::new(pos[0] as f64, pos[1] as f64, pos[2] as f64); - let orient = data.pose.orientation.unwrap_or([0., 0., 0., 0.]); + let orient = pose.orientation.unwrap_or([0., 0., 0., 0.]); let rotation = Rotation3D::quaternion( orient[0] as f64, orient[1] as f64, diff --git a/components/script/dom/xrstationaryreferencespace.rs b/components/script/dom/xrstationaryreferencespace.rs index a867aa0d976..65a0aebb790 100644 --- a/components/script/dom/xrstationaryreferencespace.rs +++ b/components/script/dom/xrstationaryreferencespace.rs @@ -55,7 +55,7 @@ impl XRStationaryReferenceSpace { /// /// Does not apply originOffset, use get_viewer_pose on XRReferenceSpace instead pub fn get_unoffset_viewer_pose(&self, viewer_pose: &WebVRFrameData) -> RigidTransform3D<f64> { - let viewer_pose = XRSpace::viewer_pose_from_frame_data(viewer_pose); + let viewer_pose = XRSpace::pose_to_transform(&viewer_pose.pose); // all math is in column-vector notation // we use the following equation to verify correctness here: // get_viewer_pose(space) = get_pose(space).inverse() * get_pose(viewer_space) @@ -113,7 +113,7 @@ impl XRStationaryReferenceSpace { }, XRStationaryReferenceSpaceSubtype::Position_disabled => { // This space follows the user around, but does not mirror the user's orientation - let viewer_pose = XRSpace::viewer_pose_from_frame_data(viewer_pose); + let viewer_pose = XRSpace::pose_to_transform(&viewer_pose.pose); viewer_pose.translation.into() }, } diff --git a/components/script/dom/xrview.rs b/components/script/dom/xrview.rs index a2eee03581d..c53eb8cb947 100644 --- a/components/script/dom/xrview.rs +++ b/components/script/dom/xrview.rs @@ -21,7 +21,9 @@ pub struct XRView { reflector_: Reflector, session: Dom<XRSession>, eye: XREye, + #[ignore_malloc_size_of = "mozjs"] proj: Heap<*mut JSObject>, + #[ignore_malloc_size_of = "mozjs"] view: Heap<*mut JSObject>, transform: Dom<XRRigidTransform>, } diff --git a/components/script/dom/xrviewerpose.rs b/components/script/dom/xrviewerpose.rs index 717fdcea16e..92caa7f037b 100644 --- a/components/script/dom/xrviewerpose.rs +++ b/components/script/dom/xrviewerpose.rs @@ -22,6 +22,7 @@ use webvr_traits::WebVRFrameData; #[dom_struct] pub struct XRViewerPose { pose: XRPose, + #[ignore_malloc_size_of = "mozjs"] views: Heap<JSVal>, } diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 6cad5d4238e..80fe9cf31e5 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -685,11 +685,11 @@ unsafe fn set_gc_zeal_options(cx: *mut JSContext) { use js::jsapi::{JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ}; let level = match pref!(js.mem.gc.zeal.level) { - Some(level @ 0...14) => level as u8, + level @ 0...14 => level as u8, _ => return, }; let frequency = match pref!(js.mem.gc.zeal.frequency) { - Some(frequency) if frequency >= 0 => frequency as u32, + frequency if frequency >= 0 => frequency as u32, _ => JS_DEFAULT_ZEAL_FREQ, }; JS_SetGCZeal(cx, level, frequency); diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index 72404de1084..b548ceb83ce 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -270,7 +270,7 @@ where Component::Combinator(..) => { unreachable!("Found combinator in simple selectors vector?"); }, - Component::PseudoElement(..) | Component::LocalName(..) => { + Component::Part(..) | Component::PseudoElement(..) | Component::LocalName(..) => { specificity.element_selectors += 1 }, Component::Slotted(ref selector) => { diff --git a/components/selectors/context.rs b/components/selectors/context.rs index 3ecd4dd82d5..95caa4055d7 100644 --- a/components/selectors/context.rs +++ b/components/selectors/context.rs @@ -279,13 +279,13 @@ where /// Runs F with a given shadow host which is the root of the tree whose /// rules we're matching. #[inline] - pub fn with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R + pub fn with_shadow_host<F, E, R>(&mut self, host: E, f: F) -> R where E: Element, F: FnOnce(&mut Self) -> R, { let original_host = self.current_host.take(); - self.current_host = host.map(|h| h.opaque()); + self.current_host = Some(host.opaque()); let result = f(self); self.current_host = original_host; result diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index f50dc8ddecf..aa200cc4b26 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -450,6 +450,7 @@ where element.containing_shadow_host() }, + Combinator::Part => element.containing_shadow_host(), Combinator::SlotAssignment => { debug_assert!( context.current_host.is_some(), @@ -517,6 +518,7 @@ where Combinator::Child | Combinator::Descendant | Combinator::SlotAssignment | + Combinator::Part | Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally, }; @@ -671,6 +673,7 @@ where match *selector { Component::Combinator(_) => unreachable!(), + Component::Part(ref part) => element.is_part(part), Component::Slotted(ref selector) => { // <slots> are never flattened tree slottables. !element.is_html_slot_element() && diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 0515eaca193..a924bbc17d0 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -30,10 +30,7 @@ pub trait PseudoElement: Sized + ToCss { /// Whether the pseudo-element supports a given state selector to the right /// of it. - fn supports_pseudo_class( - &self, - _pseudo_class: &<Self::Impl as SelectorImpl>::NonTSPseudoClass, - ) -> bool { + fn accepts_state_pseudo_classes(&self) -> bool { false } @@ -50,6 +47,11 @@ pub trait NonTSPseudoClass: Sized + ToCss { /// Whether this pseudo-class is :active or :hover. fn is_active_or_hover(&self) -> bool; + + /// Whether this pseudo-class belongs to: + /// + /// https://drafts.csswg.org/selectors-4/#useraction-pseudos + fn is_user_action_state(&self) -> bool; } /// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a @@ -64,6 +66,72 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> { } } +bitflags! { + /// Flags that indicate at which point of parsing a selector are we. + struct SelectorParsingState: u8 { + /// Whether we're inside a negation. If we're inside a negation, we're + /// not allowed to add another negation or such, for example. + const INSIDE_NEGATION = 1 << 0; + /// Whether we've parsed a ::slotted() pseudo-element already. + /// + /// If so, then we can only parse a subset of pseudo-elements, and + /// whatever comes after them if so. + const AFTER_SLOTTED = 1 << 1; + /// Whether we've parsed a ::part() pseudo-element already. + /// + /// If so, then we can only parse a subset of pseudo-elements, and + /// whatever comes after them if so. + const AFTER_PART = 1 << 2; + /// Whether we've parsed a pseudo-element (as in, an + /// `Impl::PseudoElement` thus not accounting for `::slotted` or + /// `::part`) already. + /// + /// If so, then other pseudo-elements and most other selectors are + /// disallowed. + const AFTER_PSEUDO_ELEMENT = 1 << 3; + /// Whether we've parsed a non-stateful pseudo-element (again, as-in + /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are + /// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set + /// as well. + const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4; + /// Whether we are after any of the pseudo-like things. + const AFTER_PSEUDO = Self::AFTER_PART.bits | Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits; + } +} + +impl SelectorParsingState { + #[inline] + fn allows_functional_pseudo_classes(self) -> bool { + !self.intersects(SelectorParsingState::AFTER_PSEUDO) + } + + #[inline] + fn allows_slotted(self) -> bool { + !self.intersects(SelectorParsingState::AFTER_PSEUDO) + } + + // TODO(emilio): Should we allow other ::part()s after ::part()? + // + // See https://github.com/w3c/csswg-drafts/issues/3841 + #[inline] + fn allows_part(self) -> bool { + !self.intersects(SelectorParsingState::AFTER_PSEUDO) + } + + #[inline] + fn allows_non_functional_pseudo_classes(self) -> bool { + !self.intersects( + SelectorParsingState::AFTER_SLOTTED | + SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT, + ) + } + + #[inline] + fn allows_tree_structural_pseudo_classes(self) -> bool { + !self.intersects(SelectorParsingState::AFTER_PSEUDO) + } +} + pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>; #[derive(Clone, Debug, PartialEq)] @@ -76,6 +144,7 @@ pub enum SelectorParseErrorKind<'i> { NonCompoundSelector, NonPseudoElementAfterSlotted, InvalidPseudoElementAfterSlotted, + InvalidState, UnexpectedTokenInAttributeSelector(Token<'i>), PseudoElementExpectedColon(Token<'i>), PseudoElementExpectedIdent(Token<'i>), @@ -108,6 +177,7 @@ macro_rules! with_all_bounds { type AttrValue: $($InSelector)*; type Identifier: $($InSelector)*; type ClassName: $($InSelector)*; + type PartName: $($InSelector)*; type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>; type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>; type NamespacePrefix: $($InSelector)* + Default; @@ -143,17 +213,16 @@ pub trait Parser<'i> { type Impl: SelectorImpl; type Error: 'i + From<SelectorParseErrorKind<'i>>; - /// Whether the name is a pseudo-element that can be specified with - /// the single colon syntax in addition to the double-colon syntax. - fn pseudo_element_allows_single_colon(name: &str) -> bool { - is_css2_pseudo_element(name) - } - /// Whether to parse the `::slotted()` pseudo-element. fn parse_slotted(&self) -> bool { false } + /// Whether to parse the `::part()` pseudo-element. + fn parse_part(&self) -> bool { + false + } + /// Whether to parse the `:host` pseudo-class. fn parse_host(&self) -> bool { false @@ -694,7 +763,8 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { /// combinators to the left. #[inline] pub(crate) fn is_featureless_host_selector(&mut self) -> bool { - self.all(|component| matches!(*component, Component::Host(..))) && + self.selector_length() > 0 && + self.all(|component| matches!(*component, Component::Host(..))) && self.next_sequence().is_none() } @@ -798,6 +868,9 @@ pub enum Combinator { /// Another combinator used for ::slotted(), which represent the jump from /// a node to its assigned slot. SlotAssignment, + /// Another combinator used for `::part()`, which represents the jump from + /// the part to the containing shadow host. + Part, } impl Combinator { @@ -891,8 +964,7 @@ pub enum Component<Impl: SelectorImpl> { LastOfType, OnlyOfType, NonTSPseudoClass(#[shmem(field_bound)] Impl::NonTSPseudoClass), - /// The ::slotted() pseudo-element (which isn't actually a pseudo-element, - /// and probably should be a pseudo-class): + /// The ::slotted() pseudo-element: /// /// https://drafts.csswg.org/css-scoping/#slotted-pseudo /// @@ -904,6 +976,9 @@ pub enum Component<Impl: SelectorImpl> { /// /// See https://github.com/w3c/csswg-drafts/issues/2158 Slotted(Selector<Impl>), + /// The `::part` pseudo-element. + /// https://drafts.csswg.org/css-shadow-parts/#part + Part(#[shmem(field_bound)] Impl::PartName), /// The `:host` pseudo-class: /// /// https://drafts.csswg.org/css-scoping/#host-selector @@ -1153,8 +1228,7 @@ impl ToCss for Combinator { Combinator::Descendant => dest.write_str(" "), Combinator::NextSibling => dest.write_str(" + "), Combinator::LaterSibling => dest.write_str(" ~ "), - Combinator::PseudoElement => Ok(()), - Combinator::SlotAssignment => Ok(()), + Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => Ok(()), } } } @@ -1193,6 +1267,11 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> { selector.to_css(dest)?; dest.write_char(')') }, + Part(ref part_name) => { + dest.write_str("::part(")?; + display_to_css_identifier(part_name, dest)?; + dest.write_char(')') + }, PseudoElement(ref p) => p.to_css(dest), ID(ref s) => { dest.write_char('#')?; @@ -1364,15 +1443,12 @@ where { let mut builder = SelectorBuilder::default(); - let mut has_pseudo_element; - let mut slotted; + let mut has_pseudo_element = false; + let mut slotted = false; 'outer_loop: loop { // Parse a sequence of simple selectors. - match parse_compound_selector(parser, input, &mut builder)? { - Some((has_pseudo, slot)) => { - has_pseudo_element = has_pseudo; - slotted = slot; - }, + let state = match parse_compound_selector(parser, input, &mut builder)? { + Some(state) => state, None => { return Err(input.new_custom_error(if builder.has_combinators() { SelectorParseErrorKind::DanglingCombinator @@ -1382,7 +1458,11 @@ where }, }; - if has_pseudo_element || slotted { + if state.intersects(SelectorParsingState::AFTER_PSEUDO) { + has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT); + slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED); + let part = state.intersects(SelectorParsingState::AFTER_PART); + debug_assert!(has_pseudo_element || slotted || part); break; } @@ -1420,6 +1500,8 @@ where builder.push_combinator(combinator); } + // TODO(emilio): We'll have to flag part() somehow as well, but we need more + // bits! Ok(Selector(builder.build(has_pseudo_element, slotted))) } @@ -1510,6 +1592,7 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> { SimpleSelector(Component<Impl>), PseudoElement(Impl::PseudoElement), SlottedPseudo(Selector<Impl>), + PartPseudo(Impl::PartName), } #[derive(Debug)] @@ -1848,7 +1931,7 @@ where Err(e) => return Err(e.into()), }; if !is_type_sel { - match parse_one_simple_selector(parser, input, /* inside_negation = */ true)? { + match parse_one_simple_selector(parser, input, SelectorParsingState::INSIDE_NEGATION)? { Some(SimpleSelectorParseResult::SimpleSelector(s)) => { sequence.push(s); }, @@ -1856,6 +1939,7 @@ where return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation)); }, Some(SimpleSelectorParseResult::PseudoElement(_)) | + Some(SimpleSelectorParseResult::PartPseudo(_)) | Some(SimpleSelectorParseResult::SlottedPseudo(_)) => { let e = SelectorParseErrorKind::NonSimpleSelectorInNegation; return Err(input.new_custom_error(e)); @@ -1875,14 +1959,11 @@ where /// /// `Err(())` means invalid selector. /// `Ok(None)` is an empty selector -/// -/// The booleans represent whether a pseudo-element has been parsed, and whether -/// ::slotted() has been parsed, respectively. fn parse_compound_selector<'i, 't, P, Impl>( parser: &P, input: &mut CssParser<'i, 't>, builder: &mut SelectorBuilder<Impl>, -) -> Result<Option<(bool, bool)>, ParseError<'i, P::Error>> +) -> Result<Option<SelectorParsingState>, ParseError<'i, P::Error>> where P: Parser<'i, Impl = Impl>, Impl: SelectorImpl, @@ -1901,122 +1982,48 @@ where empty = false; } - let mut pseudo = false; - let mut slot = false; + let mut state = SelectorParsingState::empty(); loop { - let parse_result = - match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? { - None => break, - Some(result) => result, - }; + let parse_result = match parse_one_simple_selector(parser, input, state)? { + None => break, + Some(result) => result, + }; empty = false; - let slotted_selector; - let pseudo_element; - match parse_result { SimpleSelectorParseResult::SimpleSelector(s) => { builder.push_simple_selector(s); - continue; }, - SimpleSelectorParseResult::PseudoElement(p) => { - slotted_selector = None; - pseudo_element = Some(p); + SimpleSelectorParseResult::PartPseudo(part_name) => { + state.insert(SelectorParsingState::AFTER_PART); + builder.push_combinator(Combinator::Part); + builder.push_simple_selector(Component::Part(part_name)); }, SimpleSelectorParseResult::SlottedPseudo(selector) => { - slotted_selector = Some(selector); - let maybe_pseudo = - parse_one_simple_selector(parser, input, /* inside_negation = */ false)?; - - pseudo_element = match maybe_pseudo { - None => None, - Some(SimpleSelectorParseResult::PseudoElement(pseudo)) => { - if !pseudo.valid_after_slotted() { - return Err(input.new_custom_error( - SelectorParseErrorKind::InvalidPseudoElementAfterSlotted, - )); - } - Some(pseudo) - }, - Some(SimpleSelectorParseResult::SimpleSelector(..)) | - Some(SimpleSelectorParseResult::SlottedPseudo(..)) => { - return Err(input.new_custom_error( - SelectorParseErrorKind::NonPseudoElementAfterSlotted, - )); - }, - }; + state.insert(SelectorParsingState::AFTER_SLOTTED); + if !builder.is_empty() { + builder.push_combinator(Combinator::SlotAssignment); + } + builder.push_simple_selector(Component::Slotted(selector)); }, - } - - debug_assert!(slotted_selector.is_some() || pseudo_element.is_some()); - // Try to parse state to the right of the pseudo-element. - // - // There are only 3 allowable state selectors that can go on - // pseudo-elements as of right now. - let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new(); - if let Some(ref p) = pseudo_element { - loop { - let location = input.current_source_location(); - match input.next_including_whitespace() { - Ok(&Token::Colon) => {}, - Ok(&Token::WhiteSpace(_)) | Err(_) => break, - Ok(t) => { - let e = SelectorParseErrorKind::PseudoElementExpectedColon(t.clone()); - return Err(location.new_custom_error(e)); - }, + SimpleSelectorParseResult::PseudoElement(p) => { + state.insert(SelectorParsingState::AFTER_PSEUDO_ELEMENT); + if !p.accepts_state_pseudo_classes() { + state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT); } - - let location = input.current_source_location(); - // TODO(emilio): Functional pseudo-classes too? - // We don't need it for now. - let name = match input.next_including_whitespace()? { - &Token::Ident(ref name) => name.clone(), - t => { - return Err(location.new_custom_error( - SelectorParseErrorKind::NoIdentForPseudo(t.clone()), - )); - }, - }; - - let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name.clone())?; - if !p.supports_pseudo_class(&pseudo_class) { - return Err(input.new_custom_error( - SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name), - )); + if !builder.is_empty() { + builder.push_combinator(Combinator::PseudoElement); } - state_selectors.push(Component::NonTSPseudoClass(pseudo_class)); - } - } - - if let Some(slotted) = slotted_selector { - slot = true; - if !builder.is_empty() { - builder.push_combinator(Combinator::SlotAssignment); - } - builder.push_simple_selector(Component::Slotted(slotted)); - } - - if let Some(p) = pseudo_element { - pseudo = true; - if !builder.is_empty() { - builder.push_combinator(Combinator::PseudoElement); - } - - builder.push_simple_selector(Component::PseudoElement(p)); - - for state_selector in state_selectors.drain() { - builder.push_simple_selector(state_selector); - } + builder.push_simple_selector(Component::PseudoElement(p)); + }, } - - break; } if empty { // An empty selector is invalid. Ok(None) } else { - Ok(Some((pseudo, slot))) + Ok(Some(state)) } } @@ -2024,12 +2031,16 @@ fn parse_functional_pseudo_class<'i, 't, P, Impl>( parser: &P, input: &mut CssParser<'i, 't>, name: CowRcStr<'i>, - inside_negation: bool, + state: SelectorParsingState, ) -> Result<Component<Impl>, ParseError<'i, P::Error>> where P: Parser<'i, Impl = Impl>, Impl: SelectorImpl, { + if !state.allows_functional_pseudo_classes() { + return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + } + debug_assert!(state.allows_tree_structural_pseudo_classes()); match_ignore_ascii_case! { &name, "nth-child" => return Ok(parse_nth_pseudo_class(input, Component::NthChild)?), "nth-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthOfType)?), @@ -2037,11 +2048,12 @@ where "nth-last-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthLastOfType)?), "host" => return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input)?))), "not" => { - if inside_negation { + if state.intersects(SelectorParsingState::INSIDE_NEGATION) { return Err(input.new_custom_error( SelectorParseErrorKind::UnexpectedIdent("not".into()) )); } + debug_assert!(state.is_empty()); return parse_negation(parser, input) }, _ => {} @@ -2064,7 +2076,7 @@ where /// Returns whether the name corresponds to a CSS2 pseudo-element that /// can be specified with the single colon syntax (in addition to the /// double-colon syntax, which can be used for all pseudo-elements). -pub fn is_css2_pseudo_element(name: &str) -> bool { +fn is_css2_pseudo_element(name: &str) -> bool { // ** Do not add to this list! ** match_ignore_ascii_case! { name, "before" | "after" | "first-line" | "first-letter" => true, @@ -2080,37 +2092,52 @@ pub fn is_css2_pseudo_element(name: &str) -> bool { fn parse_one_simple_selector<'i, 't, P, Impl>( parser: &P, input: &mut CssParser<'i, 't>, - inside_negation: bool, + state: SelectorParsingState, ) -> Result<Option<SimpleSelectorParseResult<Impl>>, ParseError<'i, P::Error>> where P: Parser<'i, Impl = Impl>, Impl: SelectorImpl, { let start = input.state(); - // FIXME: remove clone() when lifetimes are non-lexical - match input.next_including_whitespace().map(|t| t.clone()) { - Ok(Token::IDHash(id)) => { + let token = match input.next_including_whitespace().map(|t| t.clone()) { + Ok(t) => t, + Err(..) => { + input.reset(&start); + return Ok(None); + }, + }; + + Ok(Some(match token { + Token::IDHash(id) => { + if state.intersects(SelectorParsingState::AFTER_PSEUDO) { + return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + } let id = Component::ID(id.as_ref().into()); - Ok(Some(SimpleSelectorParseResult::SimpleSelector(id))) + SimpleSelectorParseResult::SimpleSelector(id) }, - Ok(Token::Delim('.')) => { + Token::Delim('.') => { + if state.intersects(SelectorParsingState::AFTER_PSEUDO) { + return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + } let location = input.current_source_location(); - match *input.next_including_whitespace()? { - Token::Ident(ref class) => { - let class = Component::Class(class.as_ref().into()); - Ok(Some(SimpleSelectorParseResult::SimpleSelector(class))) - }, + let class = match *input.next_including_whitespace()? { + Token::Ident(ref class) => class, ref t => { let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone()); - Err(location.new_custom_error(e)) + return Err(location.new_custom_error(e)); }, - } + }; + let class = Component::Class(class.as_ref().into()); + SimpleSelectorParseResult::SimpleSelector(class) }, - Ok(Token::SquareBracketBlock) => { + Token::SquareBracketBlock => { + if state.intersects(SelectorParsingState::AFTER_PSEUDO) { + return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + } let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?; - Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr))) + SimpleSelectorParseResult::SimpleSelector(attr) }, - Ok(Token::Colon) => { + Token::Colon => { let location = input.current_source_location(); let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() { Token::Colon => (false, input.next_including_whitespace()?.clone()), @@ -2124,72 +2151,102 @@ where return Err(input.new_custom_error(e)); }, }; - let is_pseudo_element = - !is_single_colon || P::pseudo_element_allows_single_colon(&name); + let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name); if is_pseudo_element { - let parse_result = if is_functional { + if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) { + return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + } + let pseudo_element = if is_functional { + if P::parse_part(parser) && name.eq_ignore_ascii_case("part") { + if !state.allows_part() { + return Err( + input.new_custom_error(SelectorParseErrorKind::InvalidState) + ); + } + let name = input.parse_nested_block(|input| { + Ok(input.expect_ident()?.as_ref().into()) + })?; + return Ok(Some(SimpleSelectorParseResult::PartPseudo(name))); + } if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") { + if !state.allows_slotted() { + return Err( + input.new_custom_error(SelectorParseErrorKind::InvalidState) + ); + } let selector = input.parse_nested_block(|input| { parse_inner_compound_selector(parser, input) })?; - SimpleSelectorParseResult::SlottedPseudo(selector) - } else { - let selector = input.parse_nested_block(|input| { - P::parse_functional_pseudo_element(parser, name, input) - })?; - SimpleSelectorParseResult::PseudoElement(selector) + return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector))); } + input.parse_nested_block(|input| { + P::parse_functional_pseudo_element(parser, name, input) + })? } else { - SimpleSelectorParseResult::PseudoElement(P::parse_pseudo_element( - parser, location, name, - )?) + P::parse_pseudo_element(parser, location, name)? }; - Ok(Some(parse_result)) + + if state.intersects(SelectorParsingState::AFTER_SLOTTED) && + !pseudo_element.valid_after_slotted() + { + return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + } + SimpleSelectorParseResult::PseudoElement(pseudo_element) } else { let pseudo_class = if is_functional { input.parse_nested_block(|input| { - parse_functional_pseudo_class(parser, input, name, inside_negation) + parse_functional_pseudo_class(parser, input, name, state) })? } else { - parse_simple_pseudo_class(parser, location, name)? + parse_simple_pseudo_class(parser, location, name, state)? }; - Ok(Some(SimpleSelectorParseResult::SimpleSelector( - pseudo_class, - ))) + SimpleSelectorParseResult::SimpleSelector(pseudo_class) } }, _ => { input.reset(&start); - Ok(None) + return Ok(None); }, - } + })) } fn parse_simple_pseudo_class<'i, P, Impl>( parser: &P, location: SourceLocation, name: CowRcStr<'i>, + state: SelectorParsingState, ) -> Result<Component<Impl>, ParseError<'i, P::Error>> where P: Parser<'i, Impl = Impl>, Impl: SelectorImpl, { - (match_ignore_ascii_case! { &name, - "first-child" => Ok(Component::FirstChild), - "last-child" => Ok(Component::LastChild), - "only-child" => Ok(Component::OnlyChild), - "root" => Ok(Component::Root), - "empty" => Ok(Component::Empty), - "scope" => Ok(Component::Scope), - "host" if P::parse_host(parser) => Ok(Component::Host(None)), - "first-of-type" => Ok(Component::FirstOfType), - "last-of-type" => Ok(Component::LastOfType), - "only-of-type" => Ok(Component::OnlyOfType), - _ => Err(()) - }) - .or_else(|()| { - P::parse_non_ts_pseudo_class(parser, location, name).map(Component::NonTSPseudoClass) - }) + if !state.allows_non_functional_pseudo_classes() { + return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState)); + } + + if state.allows_tree_structural_pseudo_classes() { + match_ignore_ascii_case! { &name, + "first-child" => return Ok(Component::FirstChild), + "last-child" => return Ok(Component::LastChild), + "only-child" => return Ok(Component::OnlyChild), + "root" => return Ok(Component::Root), + "empty" => return Ok(Component::Empty), + "scope" => return Ok(Component::Scope), + "host" if P::parse_host(parser) => return Ok(Component::Host(None)), + "first-of-type" => return Ok(Component::FirstOfType), + "last-of-type" => return Ok(Component::LastOfType), + "only-of-type" => return Ok(Component::OnlyOfType), + _ => {}, + } + } + + let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name)?; + if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) && + !pseudo_class.is_user_action_state() + { + return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState)); + } + Ok(Component::NonTSPseudoClass(pseudo_class)) } // NB: pub module in order to access the DummyParser @@ -2218,11 +2275,8 @@ pub mod tests { impl parser::PseudoElement for PseudoElement { type Impl = DummySelectorImpl; - fn supports_pseudo_class(&self, pc: &PseudoClass) -> bool { - match *pc { - PseudoClass::Hover => true, - PseudoClass::Active | PseudoClass::Lang(..) => false, - } + fn accepts_state_pseudo_classes(&self) -> bool { + true } fn valid_after_slotted(&self) -> bool { @@ -2237,6 +2291,11 @@ pub mod tests { fn is_active_or_hover(&self) -> bool { matches!(*self, PseudoClass::Active | PseudoClass::Hover) } + + #[inline] + fn is_user_action_state(&self) -> bool { + self.is_active_or_hover() + } } impl ToCss for PseudoClass { @@ -2302,6 +2361,7 @@ pub mod tests { type AttrValue = DummyAtom; type Identifier = DummyAtom; type ClassName = DummyAtom; + type PartName = DummyAtom; type LocalName = DummyAtom; type NamespaceUrl = DummyAtom; type NamespacePrefix = DummyAtom; @@ -2340,6 +2400,10 @@ pub mod tests { true } + fn parse_part(&self) -> bool { + true + } + fn parse_non_ts_pseudo_class( &self, location: SourceLocation, @@ -2789,11 +2853,11 @@ pub mod tests { specificity(0, 2, 1) | HAS_PSEUDO_BIT, )])) ); - assert!(parse("::before:hover:active").is_err()); + assert!(parse("::before:hover:lang(foo)").is_err()); assert!(parse("::before:hover .foo").is_err()); assert!(parse("::before .foo").is_err()); assert!(parse("::before ~ bar").is_err()); - assert!(parse("::before:active").is_err()); + assert!(parse("::before:active").is_ok()); // https://github.com/servo/servo/issues/15335 assert!(parse(":: before").is_err()); @@ -2914,6 +2978,14 @@ pub mod tests { assert!(parse("::slotted(div).foo").is_err()); assert!(parse("::slotted(div + bar)").is_err()); assert!(parse("::slotted(div) + foo").is_err()); + + assert!(parse("::part()").is_err()); + assert!(parse("::part(42)").is_err()); + // Though note https://github.com/w3c/csswg-drafts/issues/3502 + assert!(parse("::part(foo bar)").is_err()); + assert!(parse("::part(foo):hover").is_ok()); + assert!(parse("::part(foo) + bar").is_err()); + assert!(parse("div ::slotted(div)").is_ok()); assert!(parse("div + slot::slotted(div)").is_ok()); assert!(parse("div + slot::slotted(div.foo)").is_ok()); diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index badfca86ed6..52599893d2f 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -110,6 +110,8 @@ pub trait Element: Sized + Clone + Debug { case_sensitivity: CaseSensitivity, ) -> bool; + fn is_part(&self, name: &<Self::Impl as SelectorImpl>::PartName) -> bool; + /// Returns whether this element matches `:empty`. /// /// That is, whether it does not contain any child element or any non-zero-length text node. diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs index eff64ed6c1e..58d9bda423a 100644 --- a/components/style/author_styles.rs +++ b/components/style/author_styles.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! A set of author stylesheets and their computed representation, such as the -//! ones used for ShadowRoot and XBL. +//! ones used for ShadowRoot. use crate::context::QuirksMode; use crate::dom::TElement; @@ -17,7 +17,7 @@ use crate::stylesheets::StylesheetInDocument; use crate::stylist::CascadeData; /// A set of author stylesheets and their computed representation, such as the -/// ones used for ShadowRoot and XBL. +/// ones used for ShadowRoot. #[derive(MallocSizeOf)] pub struct AuthorStyles<S> where @@ -28,9 +28,6 @@ where pub stylesheets: AuthorStylesheetSet<S>, /// The actual cascade data computed from the stylesheets. pub data: CascadeData, - /// The quirks mode of the last stylesheet flush, used because XBL sucks and - /// we should really fix it, see bug 1406875. - pub quirks_mode: QuirksMode, } impl<S> AuthorStyles<S> @@ -43,7 +40,6 @@ where Self { stylesheets: AuthorStylesheetSet::new(), data: CascadeData::new(), - quirks_mode: QuirksMode::NoQuirks, } } @@ -65,10 +61,6 @@ where .stylesheets .flush::<E>(/* host = */ None, /* snapshot_map = */ None); - if flusher.sheets.dirty() { - self.quirks_mode = quirks_mode; - } - // Ignore OOM. let _ = self .data diff --git a/components/style/dom.rs b/components/style/dom.rs index 967b978bad4..9a81e2ec774 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -761,7 +761,7 @@ pub trait TElement: /// Returns the anonymous content for the current element's XBL binding, /// given if any. /// - /// This is used in Gecko for XBL and shadow DOM. + /// This is used in Gecko for XBL. fn xbl_binding_anonymous_content(&self) -> Option<Self::ConcreteNode> { None } @@ -772,11 +772,6 @@ pub trait TElement: /// The shadow root which roots the subtree this element is contained in. fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>; - /// XBL hack for style sharing. :( - fn has_same_xbl_proto_binding_as(&self, _other: Self) -> bool { - true - } - /// Return the element which we can use to look up rules in the selector /// maps. /// @@ -792,56 +787,34 @@ pub trait TElement: } } - /// Implements Gecko's `nsBindingManager::WalkRules`. - /// - /// Returns whether to cut off the binding inheritance, that is, whether - /// document rules should _not_ apply. - fn each_xbl_cascade_data<'a, F>(&self, _: F) -> bool - where - Self: 'a, - F: FnMut(&'a CascadeData, QuirksMode), - { - false - } - /// Executes the callback for each applicable style rule data which isn't /// the main document's data (which stores UA / author rules). /// /// The element passed to the callback is the containing shadow host for the - /// data if it comes from Shadow DOM, None if it comes from XBL. + /// data if it comes from Shadow DOM. /// /// Returns whether normal document author rules should apply. fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool where Self: 'a, - F: FnMut(&'a CascadeData, QuirksMode, Option<Self>), + F: FnMut(&'a CascadeData, Self), { use rule_collector::containing_shadow_ignoring_svg_use; - let mut doc_rules_apply = !self.each_xbl_cascade_data(|data, quirks_mode| { - f(data, quirks_mode, None); - }); + let mut doc_rules_apply = self.matches_user_and_author_rules(); // Use the same rules to look for the containing host as we do for rule // collection. if let Some(shadow) = containing_shadow_ignoring_svg_use(*self) { doc_rules_apply = false; if let Some(data) = shadow.style_data() { - f( - data, - self.as_node().owner_doc().quirks_mode(), - Some(shadow.host()), - ); + f(data, shadow.host()); } } if let Some(shadow) = self.shadow_root() { if let Some(data) = shadow.style_data() { - f( - data, - self.as_node().owner_doc().quirks_mode(), - Some(shadow.host()), - ); + f(data, shadow.host()); } } @@ -850,11 +823,7 @@ pub trait TElement: // Slots can only have assigned nodes when in a shadow tree. let shadow = slot.containing_shadow().unwrap(); if let Some(data) = shadow.style_data() { - f( - data, - self.as_node().owner_doc().quirks_mode(), - Some(shadow.host()), - ); + f(data, shadow.host()); } current = slot.assigned_slot(); } diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 43438438a40..4cd5722bdb0 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -13,18 +13,17 @@ use crate::gecko::values::GeckoStyleCoordConvertible; use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue, Matrix4x4Components}; -use crate::gecko_bindings::structs::{nsStyleImage, nsresult, SheetType}; +use crate::gecko_bindings::structs::{nsStyleImage, nsresult}; use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue}; -use crate::stylesheets::{Origin, RulesMutateError}; +use crate::stylesheets::RulesMutateError; use crate::values::computed::image::LineDirection; use crate::values::computed::transform::Matrix3D; use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::{Angle, Gradient, Image}; use crate::values::computed::{Integer, LengthPercentage}; use crate::values::computed::{Length, Percentage, TextAlign}; -use crate::values::generics::box_::VerticalAlign; use crate::values::generics::grid::{TrackListValue, TrackSize}; -use crate::values::generics::image::{CompatMode, GradientItem, Image as GenericImage}; +use crate::values::generics::image::{CompatMode, Image as GenericImage}; use crate::values::generics::rect::Rect; use crate::Zero; use app_units::Au; @@ -154,7 +153,6 @@ impl nsStyleImage { // FIXME(emilio): This is really complex, we should use cbindgen for this. fn set_gradient(&mut self, gradient: Gradient) { - use self::structs::nsStyleCoord; use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER; use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE; use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER; @@ -329,26 +327,9 @@ impl nsStyleImage { }, }; - for (index, item) in gradient.items.iter().enumerate() { - // NB: stops are guaranteed to be none in the gecko side by - // default. - + for (index, item) in gradient.items.into_iter().enumerate() { let gecko_stop = unsafe { &mut (*gecko_gradient).mStops[index] }; - let mut coord = nsStyleCoord::null(); - - match *item { - GradientItem::ColorStop(ref stop) => { - gecko_stop.mColor = stop.color.into(); - gecko_stop.mIsInterpolationHint = false; - coord.set(stop.position); - }, - GradientItem::InterpolationHint(hint) => { - gecko_stop.mIsInterpolationHint = true; - coord.set(Some(hint)); - }, - } - - gecko_stop.mLocation.move_from(coord); + *gecko_stop = item; } unsafe { @@ -419,7 +400,7 @@ impl nsStyleImage { use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER; use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE; use crate::values::computed::position::Position; - use crate::values::generics::image::{Circle, ColorStop, Ellipse}; + use crate::values::generics::image::{Circle, Ellipse}; use crate::values::generics::image::{EndingShape, GradientKind, ShapeExtent}; let gecko_gradient = bindings::Gecko_GetGradientImageValue(self) @@ -531,24 +512,7 @@ impl nsStyleImage { }, }; - let items = gecko_gradient - .mStops - .iter() - .map(|ref stop| { - if stop.mIsInterpolationHint { - GradientItem::InterpolationHint( - LengthPercentage::from_gecko_style_coord(&stop.mLocation) - .expect("mLocation could not convert to LengthPercentage"), - ) - } else { - GradientItem::ColorStop(ColorStop { - color: stop.mColor.into(), - position: LengthPercentage::from_gecko_style_coord(&stop.mLocation), - }) - } - }) - .collect(); - + let items = gecko_gradient.mStops.iter().cloned().collect(); let compat_mode = if gecko_gradient.mMozLegacySyntax { CompatMode::Moz } else if gecko_gradient.mLegacySyntax { @@ -818,16 +782,6 @@ impl From<RulesMutateError> for nsresult { } } -impl From<Origin> for SheetType { - fn from(other: Origin) -> Self { - match other { - Origin::UserAgent => SheetType::Agent, - Origin::Author => SheetType::Doc, - Origin::User => SheetType::User, - } - } -} - impl TrackSize<LengthPercentage> { /// Return TrackSize from given two nsStyleCoord pub fn from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self { @@ -920,26 +874,6 @@ where } } -impl<L> VerticalAlign<L> { - /// Converts an enumerated value coming from Gecko to a `VerticalAlign<L>`. - pub fn from_gecko_keyword(value: u32) -> Self { - match value { - structs::NS_STYLE_VERTICAL_ALIGN_BASELINE => VerticalAlign::Baseline, - structs::NS_STYLE_VERTICAL_ALIGN_SUB => VerticalAlign::Sub, - structs::NS_STYLE_VERTICAL_ALIGN_SUPER => VerticalAlign::Super, - structs::NS_STYLE_VERTICAL_ALIGN_TOP => VerticalAlign::Top, - structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP => VerticalAlign::TextTop, - structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE => VerticalAlign::Middle, - structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM => VerticalAlign::Bottom, - structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM => VerticalAlign::TextBottom, - structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE => { - VerticalAlign::MozMiddleWithBaseline - }, - _ => panic!("unexpected enumerated value for vertical-align"), - } - } -} - impl TextAlign { /// Obtain a specified value from a Gecko keyword value /// diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index ead7d88502b..0ce43bca46d 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -145,11 +145,6 @@ impl PerDocumentStyleData { /// Create a `PerDocumentStyleData`. pub fn new(document: *const structs::Document) -> Self { let device = Device::new(document); - - // FIXME(emilio, tlin): How is this supposed to work with XBL? This is - // right now not always honored, see bug 1405543... - // - // Should we just force XBL Stylists to be NoQuirks? let quirks_mode = device.document().mCompatMode; PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl { diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index b39e748ce2b..4c04d9573e5 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -169,7 +169,6 @@ impl Device { self.document() .mPresShell .as_ref()? - ._base .mPresContext .mRawPtr .as_ref() diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index 479c12b9cde..7538b785858 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -11,7 +11,7 @@ use crate::gecko_bindings::structs::{self, PseudoStyleType}; use crate::properties::longhands::display::computed_value::T as Display; use crate::properties::{ComputedValues, PropertyFlags}; -use crate::selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl}; +use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl}; use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; use crate::string_cache::Atom; use crate::values::serialize_atom_identifier; @@ -30,6 +30,7 @@ impl ::selectors::parser::PseudoElement for PseudoElement { // ::slotted() should support all tree-abiding pseudo-elements, see // https://drafts.csswg.org/css-scoping/#slotted-pseudo // https://drafts.csswg.org/css-pseudo-4/#treelike + #[inline] fn valid_after_slotted(&self) -> bool { matches!( *self, @@ -40,12 +41,9 @@ impl ::selectors::parser::PseudoElement for PseudoElement { ) } - fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool { - if !self.supports_user_action_state() { - return false; - } - - return pseudo_class.is_safe_user_action_state(); + #[inline] + fn accepts_state_pseudo_classes(&self) -> bool { + self.supports_user_action_state() } } diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 41de05c33b9..18718446c1a 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -184,16 +184,6 @@ impl NonTSPseudoClass { } } - /// <https://drafts.csswg.org/selectors-4/#useraction-pseudos> - /// - /// We intentionally skip the link-related ones. - pub fn is_safe_user_action_state(&self) -> bool { - matches!( - *self, - NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus - ) - } - /// Get the state flag associated with a pseudo-class, if any. pub fn state_flag(&self) -> ElementState { macro_rules! flag { @@ -279,6 +269,15 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass { fn is_active_or_hover(&self) -> bool { matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover) } + + /// We intentionally skip the link-related ones. + #[inline] + fn is_user_action_state(&self) -> bool { + matches!( + *self, + NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus + ) + } } /// The dummy struct we use to implement our selector parsing. @@ -290,6 +289,7 @@ impl ::selectors::SelectorImpl for SelectorImpl { type AttrValue = Atom; type Identifier = Atom; type ClassName = Atom; + type PartName = Atom; type LocalName = Atom; type NamespacePrefix = Atom; type NamespaceUrl = Namespace; @@ -352,11 +352,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { self.parse_slotted() } - fn pseudo_element_allows_single_colon(name: &str) -> bool { - // FIXME: -moz-tree check should probably be ascii-case-insensitive. - ::selectors::parser::is_css2_pseudo_element(name) || name.starts_with("-moz-tree-") - } - fn parse_non_ts_pseudo_class( &self, location: SourceLocation, diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index 6ac4003fa25..d294939baeb 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -184,12 +184,22 @@ impl ElementSnapshot for GeckoElementSnapshot { } #[inline] + fn is_part(&self, name: &Atom) -> bool { + let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) { + Some(attr) => attr, + None => return false, + }; + + snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) + } + + #[inline] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { if !self.has_any(Flags::MaybeClass) { return false; } - snapshot_helpers::has_class(name, case_sensitivity, &self.mClass) + snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass) } #[inline] diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs index 0eb84830546..aaa2254dbd7 100644 --- a/components/style/gecko/snapshot_helpers.rs +++ b/components/style/gecko/snapshot_helpers.rs @@ -29,7 +29,7 @@ unsafe fn ptr<T>(attr: &structs::nsAttrValue) -> *const T { } #[inline(always)] -unsafe fn get_class_from_attr(attr: &structs::nsAttrValue) -> Class { +unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class { debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr)); let base_type = base_type(attr); if base_type == structs::nsAttrValue_ValueBaseType_eStringBase { @@ -82,15 +82,15 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> { Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) }) } -/// Given a class name, a case sensitivity, and an array of attributes, returns -/// whether the class has the attribute that name represents. +/// Given a class or part name, a case sensitivity, and an array of attributes, +/// returns whether the attribute has that name. #[inline(always)] -pub fn has_class( +pub fn has_class_or_part( name: &Atom, case_sensitivity: CaseSensitivity, attr: &structs::nsAttrValue, ) -> bool { - match unsafe { get_class_from_attr(attr) } { + match unsafe { get_class_or_part_from_attr(attr) } { Class::None => false, Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) }, Class::More(atoms) => match case_sensitivity { @@ -114,7 +114,7 @@ where F: FnMut(&Atom), { unsafe { - match get_class_from_attr(attr) { + match get_class_or_part_from_attr(attr) { Class::None => {}, Class::One(atom) => Atom::with(atom, callback), Class::More(atoms) => { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 0fa354fb139..9d987c56308 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -172,15 +172,7 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { Self: 'a, { let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? }; - let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles); - - debug_assert!( - author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() || - author_styles.stylesheets.is_empty() || - author_styles.stylesheets.dirty() - ); - Some(&author_styles.data) } @@ -536,11 +528,6 @@ impl<'lb> GeckoXBLBinding<'lb> { self.0.mContent.raw::<nsIContent>() } - #[inline] - fn inherits_style(&self) -> bool { - unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) } - } - // This duplicates the logic in Gecko's // nsBindingManager::GetBindingWithContent. fn binding_with_content(&self) -> Option<Self> { @@ -552,22 +539,6 @@ impl<'lb> GeckoXBLBinding<'lb> { binding = binding.base_binding()?; } } - - fn each_xbl_cascade_data<F>(&self, f: &mut F) - where - F: FnMut(&'lb CascadeData, QuirksMode), - { - if let Some(base) = self.base_binding() { - base.each_xbl_cascade_data(f); - } - - let data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyles(self.0).as_ref() }; - - if let Some(data) = data { - let data: &'lb _ = AuthorStyles::<GeckoStyleSheet>::from_ffi(data); - f(&data.data, data.quirks_mode) - } - } } /// A simple wrapper over a non-null Gecko `Element` pointer. @@ -603,6 +574,11 @@ impl<'le> GeckoElement<'le> { } #[inline(always)] + fn get_part_attr(&self) -> Option<&structs::nsAttrValue> { + snapshot_helpers::find_attr(self.attrs(), &atom!("part")) + } + + #[inline(always)] fn get_class_attr(&self) -> Option<&structs::nsAttrValue> { if !self.may_have_class() { return None; @@ -894,10 +870,11 @@ impl<'le> GeckoElement<'le> { if !self.as_node().is_in_shadow_tree() { return false; } - match self.containing_shadow_host() { - Some(e) => e.is_svg_element() && e.local_name() == &*local_name!("use"), - None => false, + if !self.parent_node_is_shadow_root() { + return false; } + let host = self.containing_shadow_host().unwrap(); + host.is_svg_element() && host.local_name() == &*local_name!("use") } fn css_transitions_info(&self) -> FxHashMap<LonghandId, Arc<AnimationValue>> { @@ -1250,14 +1227,6 @@ impl<'le> TElement for GeckoElement<'le> { } } - fn has_same_xbl_proto_binding_as(&self, other: Self) -> bool { - match (self.xbl_binding(), other.xbl_binding()) { - (None, None) => true, - (Some(a), Some(b)) => a.0.mPrototypeBinding == b.0.mPrototypeBinding, - _ => false, - } - } - fn each_anonymous_content_child<F>(&self, mut f: F) where F: FnMut(Self), @@ -1436,7 +1405,7 @@ impl<'le> TElement for GeckoElement<'le> { #[inline] fn matches_user_and_author_rules(&self) -> bool { - !self.is_in_native_anonymous_subtree() + !self.rule_hash_target().is_in_native_anonymous_subtree() } #[inline] @@ -1599,43 +1568,6 @@ impl<'le> TElement for GeckoElement<'le> { self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) } } - fn each_xbl_cascade_data<'a, F>(&self, mut f: F) -> bool - where - 'le: 'a, - F: FnMut(&'a CascadeData, QuirksMode), - { - // Walk the binding scope chain, starting with the binding attached to - // our content, up till we run out of scopes or we get cut off. - // - // If we are a NAC pseudo-element, we want to get rules from our - // rule_hash_target, that is, our originating element. - let mut current = Some(self.rule_hash_target()); - while let Some(element) = current { - if let Some(binding) = element.xbl_binding() { - binding.each_xbl_cascade_data(&mut f); - - // If we're not looking at our original element, allow the - // binding to cut off style inheritance. - if element != *self && !binding.inherits_style() { - // Go no further; we're not inheriting style from - // anything above here. - break; - } - } - - if element.is_root_of_native_anonymous_subtree() { - // Deliberately cut off style inheritance here. - break; - } - - current = element.xbl_binding_parent(); - } - - // If current has something, this means we cut off inheritance at some - // point in the loop. - current.is_some() - } - fn xbl_binding_anonymous_content(&self) -> Option<GeckoNode<'le>> { self.xbl_binding_with_content() .map(|b| unsafe { GeckoNode::from_content(&*b.anon_content()) }) @@ -2332,6 +2264,16 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { case_sensitivity.eq_atom(element_id, id) } + #[inline] + fn is_part(&self, name: &Atom) -> bool { + let attr = match self.get_part_attr() { + Some(c) => c, + None => return false, + }; + + snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) + } + #[inline(always)] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { let attr = match self.get_class_attr() { @@ -2339,7 +2281,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { None => return false, }; - snapshot_helpers::has_class(name, case_sensitivity, attr) + snapshot_helpers::has_class_or_part(name, case_sensitivity, attr) } #[inline] diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index c794eb15c2c..6e8e504c226 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -58,6 +58,10 @@ pub trait ElementSnapshot: Sized { /// if `has_attrs()` returns true. fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool; + /// Whether this snapshot represents the part named `name`. Should only be + /// called if `has_attrs()` returns true. + fn is_part(&self, name: &Atom) -> bool; + /// A callback that should be called for each class of the snapshot. Should /// only be called if `has_attrs()` returns true. fn each_class<F>(&self, _: F) @@ -340,6 +344,13 @@ where } } + fn is_part(&self, name: &Atom) -> bool { + match self.snapshot() { + Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name), + _ => self.element.is_part(name), + } + } + fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { match self.snapshot() { Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity), diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs index e4cc8115760..63d6eb6acc4 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -98,6 +98,7 @@ impl Dependency { // an eager pseudo, and return only Descendants here if not. Some(Combinator::PseudoElement) => DependencyInvalidationKind::ElementAndDescendants, Some(Combinator::SlotAssignment) => DependencyInvalidationKind::SlottedElements, + Some(Combinator::Part) => unimplemented!("Need to add invalidation for shadow parts"), } } } diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index 371c0771183..ec1548f4aa3 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -158,7 +158,10 @@ impl<'a> Invalidation<'a> { // We should be able to do better here! match self.selector.combinator_at_parse_order(self.offset - 1) { Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true, - Combinator::SlotAssignment | Combinator::NextSibling | Combinator::Child => false, + Combinator::Part | + Combinator::SlotAssignment | + Combinator::NextSibling | + Combinator::Child => false, } } @@ -171,6 +174,9 @@ impl<'a> Invalidation<'a> { Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => { InvalidationKind::Descendant(DescendantInvalidationKind::Dom) }, + Combinator::Part => { + unimplemented!("Need to add invalidation for shadow parts"); + }, Combinator::SlotAssignment => { InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) }, diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index e4555bb9a70..a49bb6306c5 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -224,8 +224,8 @@ where let mut shadow_rule_datas = SmallVec::<[_; 3]>::new(); let matches_document_author_rules = - element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| { - shadow_rule_datas.push((data, quirks_mode, host.map(|h| h.opaque()))) + element.each_applicable_non_document_style_rule_data(|data, host| { + shadow_rule_datas.push((data, host.opaque())) }); let invalidated_self = { @@ -258,12 +258,8 @@ where } } - for &(ref data, quirks_mode, ref host) in &shadow_rule_datas { - // FIXME(emilio): Replace with assert / remove when we figure - // out what to do with the quirks mode mismatches - // (that is, when bug 1406875 is properly fixed). - collector.matching_context.set_quirks_mode(quirks_mode); - collector.matching_context.current_host = host.clone(); + for &(ref data, ref host) in &shadow_rule_datas { + collector.matching_context.current_host = Some(host.clone()); collector.collect_dependencies_in_invalidation_map(data.invalidation_map()); } diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 407cc5fcdb0..7a96402c4a1 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -347,6 +347,7 @@ class Longhand(object): "TextAlign", "TextDecorationLine", "TextEmphasisPosition", + "TextTransform", "TouchAction", "TransformStyle", "UserSelect", @@ -399,8 +400,6 @@ class Shorthand(object): and allowed_in_keyframe_block != "False" def get_animatable(self): - if self.ident == "all": - return False for sub in self.sub_properties: if sub.animatable: return True diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 81e1ee4307d..b73bb073fa6 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -38,7 +38,7 @@ use crate::gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom} use crate::gecko_bindings::structs; use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::gecko_bindings::structs::mozilla::PseudoStyleType; -use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut}; +use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut}; use crate::gecko_bindings::sugar::refptr::RefPtr; use crate::gecko::values::GeckoStyleCoordConvertible; use crate::gecko::values::round_border_to_device_pixels; @@ -1104,9 +1104,6 @@ impl ${style_struct.gecko_struct_name} { } result } - pub fn get_gecko(&self) -> &${style_struct.gecko_ffi_name} { - &self.gecko - } } impl Drop for ${style_struct.gecko_struct_name} { fn drop(&mut self) { @@ -2508,7 +2505,7 @@ fn static_assert() { } </%def> -<% skip_box_longhands= """display vertical-align +<% skip_box_longhands= """display animation-name animation-delay animation-duration animation-direction animation-fill-mode animation-play-state animation-iteration-count animation-timing-function @@ -2564,47 +2561,6 @@ fn static_assert() { ) %> ${impl_keyword('clear', 'mBreakType', clear_keyword)} - pub fn set_vertical_align(&mut self, v: longhands::vertical_align::computed_value::T) { - use crate::values::generics::box_::VerticalAlign; - let value = match v { - VerticalAlign::Baseline => structs::NS_STYLE_VERTICAL_ALIGN_BASELINE, - VerticalAlign::Sub => structs::NS_STYLE_VERTICAL_ALIGN_SUB, - VerticalAlign::Super => structs::NS_STYLE_VERTICAL_ALIGN_SUPER, - VerticalAlign::Top => structs::NS_STYLE_VERTICAL_ALIGN_TOP, - VerticalAlign::TextTop => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP, - VerticalAlign::Middle => structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE, - VerticalAlign::Bottom => structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM, - VerticalAlign::TextBottom => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM, - VerticalAlign::MozMiddleWithBaseline => { - structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE - }, - VerticalAlign::Length(length) => { - self.gecko.mVerticalAlign.set(length); - return; - }, - }; - self.gecko.mVerticalAlign.set_value(CoordDataValue::Enumerated(value)); - } - - pub fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T { - use crate::values::computed::LengthPercentage; - use crate::values::generics::box_::VerticalAlign; - - let gecko = &self.gecko.mVerticalAlign; - match gecko.as_value() { - CoordDataValue::Enumerated(value) => VerticalAlign::from_gecko_keyword(value), - _ => { - VerticalAlign::Length( - LengthPercentage::from_gecko_style_coord(gecko).expect( - "expected <length-percentage> for vertical-align", - ), - ) - }, - } - } - - <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call> - ${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")} ${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")} @@ -4455,7 +4411,7 @@ clip-path fn set_counter_function( data: &mut nsStyleContentData, content_type: StyleContentType, - name: &CustomIdent, + name: CustomIdent, sep: &str, style: CounterStyleOrNone, ) { @@ -4464,7 +4420,9 @@ clip-path let counter_func = unsafe { bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap() }; - counter_func.mIdent.assign(name.0.as_slice()); + counter_func.mIdent.set_move(unsafe { + RefPtr::from_addrefed(name.0.into_addrefed()) + }); if content_type == StyleContentType::Counters { counter_func.mSeparator.assign_str(sep); } @@ -4493,14 +4451,14 @@ clip-path Gecko_ClearAndResizeStyleContents(&mut self.gecko, items.len() as u32); } - for (i, item) in items.into_iter().enumerate() { + for (i, item) in items.into_vec().into_iter().enumerate() { // NB: Gecko compares the mString value if type is not image // or URI independently of whatever gets there. In the quote // cases, they set it to null, so do the same here. unsafe { *self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut(); } - match *item { + match item { ContentItem::String(ref value) => { self.gecko.mContents[i].mType = StyleContentType::String; unsafe { @@ -4536,22 +4494,22 @@ clip-path => self.gecko.mContents[i].mType = StyleContentType::NoOpenQuote, ContentItem::NoCloseQuote => self.gecko.mContents[i].mType = StyleContentType::NoCloseQuote, - ContentItem::Counter(ref name, ref style) => { + ContentItem::Counter(name, style) => { set_counter_function( &mut self.gecko.mContents[i], StyleContentType::Counter, - &name, + name, "", - style.clone(), + style, ); } - ContentItem::Counters(ref name, ref sep, ref style) => { + ContentItem::Counters(name, sep, style) => { set_counter_function( &mut self.gecko.mContents[i], StyleContentType::Counters, - &name, + name, &sep, - style.clone(), + style, ); } ContentItem::Url(ref url) => { @@ -4627,7 +4585,9 @@ clip-path StyleContentType::Counter | StyleContentType::Counters => { let gecko_function = unsafe { &**gecko_content.mContent.mCounters.as_ref() }; - let ident = CustomIdent(Atom::from(&*gecko_function.mIdent)); + let ident = CustomIdent(unsafe { + Atom::from_raw(gecko_function.mIdent.mRawPtr) + }); let style = CounterStyleOrNone::from_gecko_value(&gecko_function.mCounterStyle); let style = match style { @@ -4664,8 +4624,10 @@ clip-path ) { unsafe { bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko, v.len() as u32); - for (i, ref pair) in v.iter().enumerate() { - self.gecko.m${counter_property}s[i].mCounter.assign(pair.name.0.as_slice()); + for (i, pair) in v.0.into_vec().into_iter().enumerate() { + self.gecko.m${counter_property}s[i].mCounter.set_move( + RefPtr::from_addrefed(pair.name.0.into_addrefed()) + ); self.gecko.m${counter_property}s[i].mValue = pair.value; } } @@ -4690,7 +4652,9 @@ clip-path longhands::counter_${counter_property.lower()}::computed_value::T::new( self.gecko.m${counter_property}s.iter().map(|ref gecko_counter| { CounterPair { - name: CustomIdent(Atom::from(gecko_counter.mCounter.to_string())), + name: CustomIdent(unsafe { + Atom::from_raw(gecko_counter.mCounter.mRawPtr) + }), value: gecko_counter.mValue, } }).collect() diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 397393ed3d8..a7a2cb9e348 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -337,11 +337,11 @@ ${helpers.predefined_type( "Position", "computed::Position::zero()", vector=True, + allow_empty=True, products="gecko", gecko_pref="layout.css.scroll-snap.enabled", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)", animation_value_type="discrete", - allow_empty="NotInitial", )} <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %> diff --git a/components/style/properties/longhands/inherited_table.mako.rs b/components/style/properties/longhands/inherited_table.mako.rs index 7dc4d73fe62..246a073d2a8 100644 --- a/components/style/properties/longhands/inherited_table.mako.rs +++ b/components/style/properties/longhands/inherited_table.mako.rs @@ -9,7 +9,7 @@ ${helpers.single_keyword( "border-collapse", "separate collapse", - gecko_constant_prefix="NS_STYLE_BORDER", + gecko_enum_prefix="StyleBorderCollapse", animation_value_type="discrete", spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse", servo_restyle_damage = "reflow", diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index f61c8754b5e..cdd7df93256 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -19,11 +19,10 @@ ${helpers.predefined_type( // CSS Text Module Level 3 -// TODO(pcwalton): `full-width` -${helpers.single_keyword( +${helpers.predefined_type( "text-transform", - "none capitalize uppercase lowercase", - extra_gecko_values="full-width full-size-kana", + "TextTransform", + "computed::TextTransform::none()", animation_value_type="discrete", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://drafts.csswg.org/css-text/#propdef-text-transform", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 5f44a259641..e7f9c72413e 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -27,7 +27,7 @@ use crate::context::QuirksMode; #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin; #[cfg(feature = "servo")] use crate::computed_values; use crate::logical_geometry::WritingMode; -#[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use crate::media_queries::Device; use crate::parser::ParserContext; use crate::properties::longhands::system_font::SystemFont; @@ -376,7 +376,6 @@ impl PartialEq for PropertyDeclaration { } } -#[cfg(feature = "gecko")] impl MallocSizeOf for PropertyDeclaration { #[inline] fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { @@ -1086,6 +1085,8 @@ impl LonghandId { } } + // TODO(emilio): Should we use a function table like CASCADE_PROPERTY does + // to avoid blowing up code-size here? fn parse_value<'i, 't>( &self, context: &ParserContext, @@ -1533,64 +1534,7 @@ impl UnparsedValue { quirks_mode: QuirksMode, environment: &::custom_properties::CssEnvironment, ) -> PropertyDeclaration { - crate::custom_properties::substitute( - &self.css, - self.first_token_type, - custom_properties, - environment, - ).ok().and_then(|css| { - // As of this writing, only the base URL is used for property - // values. - // - // NOTE(emilio): we intentionally pase `None` as the rule type here. - // If something starts depending on it, it's probably a bug, since - // it'd change how values are parsed depending on whether we're in a - // @keyframes rule or not, for example... So think twice about - // whether you want to do this! - // - // FIXME(emilio): ParsingMode is slightly fishy... - let context = ParserContext::new( - Origin::Author, - &self.url_data, - None, - ParsingMode::DEFAULT, - quirks_mode, - None, - None, - ); - - let mut input = ParserInput::new(&css); - Parser::new(&mut input).parse_entirely(|input| { - match self.from_shorthand { - None => longhand_id.parse_value(&context, input), - Some(ShorthandId::All) => { - // No need to parse the 'all' shorthand as anything other than a CSS-wide - // keyword, after variable substitution. - Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into()))) - } - % for shorthand in data.shorthands_except_all(): - Some(ShorthandId::${shorthand.camel_case}) => { - shorthands::${shorthand.ident}::parse_value(&context, input) - .map(|longhands| { - match longhand_id { - % for property in shorthand.sub_properties: - LonghandId::${property.camel_case} => { - PropertyDeclaration::${property.camel_case}( - longhands.${property.ident} - ) - } - % endfor - _ => unreachable!() - } - }) - } - % endfor - } - }) - .ok() - }) - .unwrap_or_else(|| { - // Invalid at computed-value time. + let invalid_at_computed_value_time = || { let keyword = if longhand_id.inherited() { CSSWideKeyword::Inherit } else { @@ -1600,7 +1544,80 @@ impl UnparsedValue { id: longhand_id, keyword, }) - }) + }; + + let css = match crate::custom_properties::substitute( + &self.css, + self.first_token_type, + custom_properties, + environment, + ) { + Ok(css) => css, + Err(..) => return invalid_at_computed_value_time(), + }; + + // As of this writing, only the base URL is used for property + // values. + // + // NOTE(emilio): we intentionally pase `None` as the rule type here. + // If something starts depending on it, it's probably a bug, since + // it'd change how values are parsed depending on whether we're in a + // @keyframes rule or not, for example... So think twice about + // whether you want to do this! + // + // FIXME(emilio): ParsingMode is slightly fishy... + let context = ParserContext::new( + Origin::Author, + &self.url_data, + None, + ParsingMode::DEFAULT, + quirks_mode, + None, + None, + ); + + let mut input = ParserInput::new(&css); + let mut input = Parser::new(&mut input); + input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. + if let Ok(keyword) = input.try(CSSWideKeyword::parse) { + return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { + id: longhand_id, + keyword, + }); + } + + let declaration = input.parse_entirely(|input| { + match self.from_shorthand { + None => longhand_id.parse_value(&context, input), + Some(ShorthandId::All) => { + // No need to parse the 'all' shorthand as anything other + // than a CSS-wide keyword, after variable substitution. + Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into()))) + } + % for shorthand in data.shorthands_except_all(): + Some(ShorthandId::${shorthand.camel_case}) => { + shorthands::${shorthand.ident}::parse_value(&context, input) + .map(|longhands| { + match longhand_id { + % for property in shorthand.sub_properties: + LonghandId::${property.camel_case} => { + PropertyDeclaration::${property.camel_case}( + longhands.${property.ident} + ) + } + % endfor + _ => unreachable!() + } + }) + } + % endfor + } + }); + + match declaration { + Ok(decl) => decl, + Err(..) => invalid_at_computed_value_time(), + } } } @@ -3745,8 +3762,7 @@ pub fn adjust_border_width(style: &mut StyleBuilder) { } /// An identifier for a given alias property. -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] +#[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)] #[repr(u16)] pub enum AliasId { % for i, property in enumerate(data.all_aliases()): diff --git a/components/style/properties/shorthands/position.mako.rs b/components/style/properties/shorthands/position.mako.rs index aaf97a0674c..6fdbe1235cf 100644 --- a/components/style/properties/shorthands/position.mako.rs +++ b/components/style/properties/shorthands/position.mako.rs @@ -254,7 +254,7 @@ use crate::parser::Parse; use servo_arc::Arc; use crate::values::{Either, None_}; - use crate::values::generics::grid::{LineNameList, TrackSize, TrackList, TrackListType}; + use crate::values::generics::grid::{TrackSize, TrackList, TrackListType}; use crate::values::generics::grid::{TrackListValue, concat_serialize_idents}; use crate::values::specified::{GridTemplateComponent, GenericGridTemplateComponent}; use crate::values::specified::grid::parse_line_names; @@ -265,12 +265,11 @@ context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(GridTemplateComponent, GridTemplateComponent, Either<TemplateAreasArc, None_>), ParseError<'i>> { - // Other shorthand sub properties also parse `none` and `subgrid` keywords and this - // shorthand should know after these keywords there is nothing to parse. Otherwise it - // gets confused and rejects the sub properties that contains `none` or `subgrid`. + // Other shorthand sub properties also parse the `none` keyword and this shorthand + // should know after this keyword there is nothing to parse. Otherwise it gets + // confused and rejects the sub properties that contains `none`. <% keywords = { "none": "GenericGridTemplateComponent::None", - "subgrid": "GenericGridTemplateComponent::Subgrid(LineNameList::default())" } %> % for keyword, rust_type in keywords.items(): diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index ba602ddca83..e87a86c5e47 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -98,7 +98,7 @@ where flags_setter: &'a mut F, ) -> Self { let rule_hash_target = element.rule_hash_target(); - let matches_user_and_author_rules = rule_hash_target.matches_user_and_author_rules(); + let matches_user_and_author_rules = element.matches_user_and_author_rules(); // Gecko definitely has pseudo-elements with style attributes, like // ::-moz-color-swatch. @@ -198,7 +198,7 @@ where let rules = &mut self.rules; let flags_setter = &mut self.flags_setter; let shadow_cascade_order = self.shadow_cascade_order; - self.context.with_shadow_host(Some(shadow_host), |context| { + self.context.with_shadow_host(shadow_host, |context| { map.get_all_matching_rules( element, rule_hash_target, @@ -303,42 +303,6 @@ where self.collect_stylist_rules(Origin::Author); } - fn collect_xbl_rules(&mut self) { - let element = self.element; - let cut_xbl_binding_inheritance = - element.each_xbl_cascade_data(|cascade_data, quirks_mode| { - let map = match cascade_data.normal_rules(self.pseudo_element) { - Some(m) => m, - None => return, - }; - - // NOTE(emilio): This is needed because the XBL stylist may - // think it has a different quirks mode than the document. - let mut matching_context = MatchingContext::new( - self.context.matching_mode(), - self.context.bloom_filter, - self.context.nth_index_cache.as_mut().map(|s| &mut **s), - quirks_mode, - ); - matching_context.pseudo_element_matching_fn = - self.context.pseudo_element_matching_fn; - - // SameTreeAuthorNormal instead of InnerShadowNormal to - // preserve behavior, though that's kinda fishy... - map.get_all_matching_rules( - self.element, - self.rule_hash_target, - self.rules, - &mut matching_context, - self.flags_setter, - CascadeLevel::SameTreeAuthorNormal, - self.shadow_cascade_order, - ); - }); - - self.matches_document_author_rules &= !cut_xbl_binding_inheritance; - } - fn collect_style_attribute_and_animation_rules(&mut self) { if let Some(sa) = self.style_attribute { self.rules @@ -396,7 +360,6 @@ where self.collect_host_rules(); self.collect_slotted_rules(); self.collect_normal_rules_from_containing_shadow_tree(); - self.collect_xbl_rules(); self.collect_document_author_rules(); self.collect_style_attribute_and_animation_rules(); } diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 9d94c467b58..1ef49c87f9f 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -66,10 +66,6 @@ pub const PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1; impl ::selectors::parser::PseudoElement for PseudoElement { type Impl = SelectorImpl; - - fn supports_pseudo_class(&self, _: &NonTSPseudoClass) -> bool { - false - } } impl ToCss for PseudoElement { @@ -293,6 +289,14 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass { fn is_active_or_hover(&self) -> bool { matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover) } + + #[inline] + fn is_user_action_state(&self) -> bool { + matches!( + *self, + NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus + ) + } } impl ToCss for NonTSPseudoClass { @@ -393,6 +397,7 @@ impl ::selectors::SelectorImpl for SelectorImpl { type AttrValue = String; type Identifier = Atom; type ClassName = Atom; + type PartName = Atom; type LocalName = LocalName; type NamespacePrefix = Prefix; type NamespaceUrl = Namespace; @@ -679,6 +684,10 @@ impl ElementSnapshot for ServoElementSnapshot { .map(|v| v.as_atom()) } + fn is_part(&self, _name: &Atom) -> bool { + false + } + fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { self.get_attr(&ns!(), &local_name!("class")) .map_or(false, |v| { diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 0675359ed77..3ce2a717dc8 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -727,27 +727,6 @@ impl<E: TElement> StyleSharingCache<E> { return None; } - // Note that in theory we shouldn't need this XBL check. However, XBL is - // absolutely broken in all sorts of ways. - // - // A style change that changes which XBL binding applies to an element - // arrives there, with the element still having the old prototype - // binding attached. And thus we try to match revalidation selectors - // with the old XBL binding, because we can't look at the new ones of - // course. And that causes us to revalidate with the wrong selectors and - // hit assertions. - // - // Other than this, we don't need anything else like the containing XBL - // binding parent or what not, since two elements with different XBL - // bindings will necessarily end up with different style. - if !target - .element - .has_same_xbl_proto_binding_as(candidate.element) - { - trace!("Miss: Different proto bindings"); - return None; - } - // If the elements are not assigned to the same slot they could match // different ::slotted() rules in the slot scope. // diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index d2c3ad0f88e..d8c21d5494c 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -468,7 +468,14 @@ where .fold(0, |s, (item, _)| s + item.len()) } + /// Returns the count of stylesheets for a given origin. + #[inline] + pub fn sheet_count(&self, origin: Origin) -> usize { + self.collections.borrow_for_origin(&origin).len() + } + /// Returns the `index`th stylesheet in the set for the given origin. + #[inline] pub fn get(&self, origin: Origin, index: usize) -> Option<&S> { self.collections.borrow_for_origin(&origin).get(index) } @@ -539,7 +546,7 @@ where } } -/// The set of stylesheets effective for a given XBL binding or Shadow Root. +/// The set of stylesheets effective for a given Shadow Root. #[derive(MallocSizeOf)] pub struct AuthorStylesheetSet<S> where diff --git a/components/style/stylesheets/origin.rs b/components/style/stylesheets/origin.rs index 783b8f26a8b..a65b61fca13 100644 --- a/components/style/stylesheets/origin.rs +++ b/components/style/stylesheets/origin.rs @@ -10,18 +10,17 @@ use std::ops::BitOrAssign; /// Each style rule has an origin, which determines where it enters the cascade. /// /// <https://drafts.csswg.org/css-cascade/#cascading-origins> -#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] #[repr(u8)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub enum Origin { /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent> - UserAgent = 1 << 0, + UserAgent = 0x1, /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user> - User = 1 << 1, + User = 0x2, /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author> - Author = 1 << 2, + Author = 0x4, } impl Origin { @@ -59,7 +58,7 @@ impl Origin { bitflags! { /// A set of origins. This is equivalent to Gecko's OriginFlags. - #[cfg_attr(feature = "servo", derive(MallocSizeOf))] + #[derive(MallocSizeOf)] pub struct OriginSet: u8 { /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent> const ORIGIN_USER_AGENT = Origin::UserAgent as u8; diff --git a/components/style/stylist.rs b/components/style/stylist.rs index ece14e9896f..2953e3f0671 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -48,8 +48,9 @@ use selectors::visitor::SelectorVisitor; use selectors::NthIndexCache; use servo_arc::{Arc, ArcBorrow}; use smallbitvec::SmallBitVec; -use std::ops; +use smallvec::SmallVec; use std::sync::Mutex; +use std::{mem, ops}; use style_traits::viewport::ViewportConstraints; /// The type of the stylesheets that the stylist contains. @@ -128,15 +129,28 @@ impl UserAgentCascadeDataCache { Ok(new_data) } - fn expire_unused(&mut self) { - // is_unique() returns false for static references, but we never have - // static references to UserAgentCascadeDatas. If we did, it may not - // make sense to put them in the cache in the first place. - self.entries.retain(|e| !e.is_unique()) + /// Returns all the cascade datas that are not being used (that is, that are + /// held alive just by this cache). + /// + /// We return them instead of dropping in place because some of them may + /// keep alive some other documents (like the SVG documents kept alive by + /// URL references), and thus we don't want to drop them while locking the + /// cache to not deadlock. + fn take_unused(&mut self) -> SmallVec<[Arc<UserAgentCascadeData>; 3]> { + let mut unused = SmallVec::new(); + for i in (0..self.entries.len()).rev() { + // is_unique() returns false for static references, but we never + // have static references to UserAgentCascadeDatas. If we did, it + // may not make sense to put them in the cache in the first place. + if self.entries[i].is_unique() { + unused.push(self.entries.remove(i)); + } + } + unused } - fn clear(&mut self) { - self.entries.clear(); + fn take_all(&mut self) -> Vec<Arc<UserAgentCascadeData>> { + mem::replace(&mut self.entries, Vec::new()) } #[cfg(feature = "gecko")] @@ -254,13 +268,14 @@ impl DocumentCascadeData { // First do UA sheets. { if flusher.flush_origin(Origin::UserAgent).dirty() { - let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); let origin_sheets = flusher.origin_sheets(Origin::UserAgent); - let ua_cascade_data = - ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?; - ua_cache.expire_unused(); - debug!("User agent data cache size {:?}", ua_cache.len()); - self.user_agent = ua_cascade_data; + let _unused_cascade_datas = { + let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); + self.user_agent = + ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?; + debug!("User agent data cache size {:?}", ua_cache.len()); + ua_cache.take_unused() + }; } } @@ -591,6 +606,18 @@ impl Stylist { .remove_stylesheet(Some(&self.device), sheet, guard) } + /// Appends a new stylesheet to the current set. + #[inline] + pub fn sheet_count(&self, origin: Origin) -> usize { + self.stylesheets.sheet_count(origin) + } + + /// Appends a new stylesheet to the current set. + #[inline] + pub fn sheet_at(&self, origin: Origin, index: usize) -> Option<&StylistSheet> { + self.stylesheets.get(origin, index) + } + /// Returns whether for any of the applicable style rule data a given /// condition is true. pub fn any_applicable_rule_data<E, F>(&self, element: E, mut f: F) -> bool @@ -605,7 +632,7 @@ impl Stylist { let mut maybe = false; let doc_author_rules_apply = - element.each_applicable_non_document_style_rule_data(|data, _, _| { + element.each_applicable_non_document_style_rule_data(|data, _| { maybe = maybe || f(&*data); }); @@ -1041,12 +1068,6 @@ impl Stylist { /// Returns whether, given a media feature change, any previously-applicable /// style has become non-applicable, or vice-versa for each origin, using /// `device`. - /// - /// Passing `device` is needed because this is used for XBL in Gecko, which - /// can be stale in various ways, so we need to pass the device of the - /// document itself, which is what is kept up-to-date. - /// - /// Arguably XBL should use something more lightweight than a Stylist. pub fn media_features_change_changed_style( &self, guards: &StylesheetGuards, @@ -1230,11 +1251,11 @@ impl Stylist { let mut results = SmallBitVec::new(); let matches_document_rules = - element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| { + element.each_applicable_non_document_style_rule_data(|data, host| { matching_context.with_shadow_host(host, |matching_context| { data.selectors_for_cache_revalidation.lookup( element, - quirks_mode, + self.quirks_mode, |selector_and_hashes| { results.push(matches_selector( &selector_and_hashes.selector, @@ -1356,7 +1377,7 @@ impl Stylist { /// Shutdown the static data that this module stores. pub fn shutdown() { - UA_CASCADE_DATA_CACHE.lock().unwrap().clear() + let _entries = UA_CASCADE_DATA_CACHE.lock().unwrap().take_all(); } } diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 4070e51815a..70ecad6a04e 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -55,7 +55,7 @@ pub enum LineDirection { pub type EndingShape = generic::EndingShape<Length, LengthPercentage>; /// A computed gradient item. -pub type GradientItem = generic::GradientItem<Color, LengthPercentage>; +pub type GradientItem = generic::GenericGradientItem<Color, LengthPercentage>; /// A computed color stop. pub type ColorStop = generic::ColorStop<Color, LengthPercentage>; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 3b32f8fecd7..3eb0c16836c 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -86,6 +86,7 @@ pub use self::transform::{TransformOrigin, TransformStyle, Translate}; #[cfg(feature = "gecko")] pub use self::ui::CursorImage; pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect}; +pub use super::specified::TextTransform; pub use super::specified::{BorderStyle, TextDecorationLine}; pub use super::{Auto, Either, None_}; pub use app_units::Au; diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 2fdb1e48c4f..c29d3d45210 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -19,6 +19,7 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; pub use crate::values::specified::TextAlignKeyword as TextAlign; +pub use crate::values::specified::TextTransform; pub use crate::values::specified::{OverflowWrap, WordBreak}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; diff --git a/components/style/values/generics/box.rs b/components/style/values/generics/box.rs index 3e8f959f456..5d6e8e25031 100644 --- a/components/style/values/generics/box.rs +++ b/components/style/values/generics/box.rs @@ -6,14 +6,15 @@ use crate::values::animated::ToAnimatedZero; -/// A generic value for the `vertical-align` property. #[derive( Animate, Clone, ComputeSquaredDistance, Copy, Debug, + FromPrimitive, MallocSizeOf, + Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, @@ -21,35 +22,51 @@ use crate::values::animated::ToAnimatedZero; ToResolvedValue, ToShmem, )] -pub enum VerticalAlign<LengthPercentage> { - /// `baseline` +#[repr(u8)] +#[allow(missing_docs)] +pub enum VerticalAlignKeyword { Baseline, - /// `sub` Sub, - /// `super` Super, - /// `top` Top, - /// `text-top` TextTop, - /// `middle` Middle, - /// `bottom` Bottom, - /// `text-bottom` TextBottom, - /// `-moz-middle-with-baseline` #[cfg(feature = "gecko")] MozMiddleWithBaseline, +} + +/// A generic value for the `vertical-align` property. +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericVerticalAlign<LengthPercentage> { + /// One of the vertical-align keywords. + Keyword(VerticalAlignKeyword), /// `<length-percentage>` Length(LengthPercentage), } +pub use self::GenericVerticalAlign as VerticalAlign; + impl<L> VerticalAlign<L> { /// Returns `baseline`. #[inline] pub fn baseline() -> Self { - VerticalAlign::Baseline + VerticalAlign::Keyword(VerticalAlignKeyword::Baseline) } } diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs index 17ac687a670..fbb6927b9f1 100644 --- a/components/style/values/generics/counters.rs +++ b/components/style/values/generics/counters.rs @@ -45,7 +45,7 @@ pub struct CounterPair<Integer> { ToResolvedValue, ToShmem, )] -pub struct CounterIncrement<I>(Counters<I>); +pub struct CounterIncrement<I>(pub Counters<I>); impl<I> CounterIncrement<I> { /// Returns a new value for `counter-increment`. @@ -77,7 +77,7 @@ impl<I> Deref for CounterIncrement<I> { ToResolvedValue, ToShmem, )] -pub struct CounterSetOrReset<I>(Counters<I>); +pub struct CounterSetOrReset<I>(pub Counters<I>); impl<I> CounterSetOrReset<I> { /// Returns a new value for `counter-set` / `counter-reset`. @@ -102,6 +102,7 @@ impl<I> Deref for CounterSetOrReset<I> { #[derive( Clone, Debug, + Default, MallocSizeOf, PartialEq, SpecifiedValueInfo, @@ -112,10 +113,13 @@ impl<I> Deref for CounterSetOrReset<I> { )] pub struct Counters<I>(#[css(iterable, if_empty = "none")] Box<[CounterPair<I>]>); -impl<I> Default for Counters<I> { +impl<I> Counters<I> { + /// Move out the Box into a vector. This could just return the Box<>, but + /// Vec<> is a bit more convenient because Box<[T]> doesn't implement + /// IntoIter: https://github.com/rust-lang/rust/issues/59878 #[inline] - fn default() -> Self { - Counters(vec![].into_boxed_slice()) + pub fn into_vec(self) -> Vec<CounterPair<I>> { + self.0.into_vec() } } diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index 71d6a1b3849..1442ce604a2 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -135,13 +135,23 @@ pub enum ShapeExtent { #[derive( Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] -pub enum GradientItem<Color, LengthPercentage> { - /// A color stop. - ColorStop(ColorStop<Color, LengthPercentage>), +#[repr(C, u8)] +pub enum GenericGradientItem<Color, LengthPercentage> { + /// A simple color stop, without position. + SimpleColorStop(Color), + /// A complex color stop, with a position. + ComplexColorStop { + /// The color for the stop. + color: Color, + /// The position for the stop. + position: LengthPercentage, + }, /// An interpolation hint. InterpolationHint(LengthPercentage), } +pub use self::GenericGradientItem as GradientItem; + /// A color stop. /// <https://drafts.csswg.org/css-images/#typedef-color-stop-list> #[derive( @@ -154,6 +164,20 @@ pub struct ColorStop<Color, LengthPercentage> { pub position: Option<LengthPercentage>, } +impl<Color, LengthPercentage> ColorStop<Color, LengthPercentage> { + /// Convert the color stop into an appropriate `GradientItem`. + #[inline] + pub fn into_item(self) -> GradientItem<Color, LengthPercentage> { + match self.position { + Some(position) => GradientItem::ComplexColorStop { + color: self.color, + position, + }, + None => GradientItem::SimpleColorStop(self.color), + } + } +} + /// Specified values for a paint worklet. /// <https://drafts.css-houdini.org/css-paint-api/> #[cfg_attr(feature = "servo", derive(MallocSizeOf))] diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index c90c270c59f..31a79c141e1 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -10,7 +10,7 @@ use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags}; use crate::properties::{PropertyId, ShorthandId}; use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use crate::values::generics::box_::Perspective as GenericPerspective; -use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign; +use crate::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword}; use crate::values::specified::length::{LengthPercentage, NonNegativeLength}; use crate::values::specified::{AllowQuirks, Number}; use crate::values::{CustomIdent, KeyframesName}; @@ -280,20 +280,9 @@ impl Parse for VerticalAlign { return Ok(GenericVerticalAlign::Length(lp)); } - try_match_ident_ignore_ascii_case! { input, - "baseline" => Ok(GenericVerticalAlign::Baseline), - "sub" => Ok(GenericVerticalAlign::Sub), - "super" => Ok(GenericVerticalAlign::Super), - "top" => Ok(GenericVerticalAlign::Top), - "text-top" => Ok(GenericVerticalAlign::TextTop), - "middle" => Ok(GenericVerticalAlign::Middle), - "bottom" => Ok(GenericVerticalAlign::Bottom), - "text-bottom" => Ok(GenericVerticalAlign::TextBottom), - #[cfg(feature = "gecko")] - "-moz-middle-with-baseline" => { - Ok(GenericVerticalAlign::MozMiddleWithBaseline) - }, - } + Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse( + input, + )?)) } } diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 572db0ca1eb..ad2ecb18086 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -8,6 +8,8 @@ //! [image]: https://drafts.csswg.org/css-images/#image-values use crate::custom_properties::SpecifiedValue; +#[cfg(feature = "gecko")] +use crate::gecko_bindings::structs; use crate::parser::{Parse, ParserContext}; #[cfg(feature = "gecko")] use crate::values::computed::{Context, Position as ComputedPosition, ToComputedValue}; @@ -266,16 +268,6 @@ impl Parse for Gradient { }, }; - #[cfg(feature = "gecko")] - { - use crate::gecko_bindings::structs; - if compat_mode == CompatMode::Moz && - !unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_gradients } - { - return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func))); - } - } - let (kind, items) = input.parse_nested_block(|i| { let shape = match shape { Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?, @@ -492,24 +484,24 @@ impl Gradient { if reverse_stops { p.reverse(); } - Ok(generic::GradientItem::ColorStop(generic::ColorStop { - color: color, - position: Some(p.into()), - })) + Ok(generic::GradientItem::ComplexColorStop { + color, + position: p.into(), + }) }) }) .unwrap_or(vec![]); if items.is_empty() { items = vec![ - generic::GradientItem::ColorStop(generic::ColorStop { + generic::GradientItem::ComplexColorStop { color: Color::transparent().into(), - position: Some(Percentage::zero().into()), - }), - generic::GradientItem::ColorStop(generic::ColorStop { + position: Percentage::zero().into(), + }, + generic::GradientItem::ComplexColorStop { color: Color::transparent().into(), - position: Some(Percentage::hundred().into()), - }), + position: Percentage::hundred().into(), + }, ]; } else if items.len() == 1 { let first = items[0].clone(); @@ -518,13 +510,16 @@ impl Gradient { items.sort_by(|a, b| { match (a, b) { ( - &generic::GradientItem::ColorStop(ref a), - &generic::GradientItem::ColorStop(ref b), - ) => match (&a.position, &b.position) { - ( - &Some(LengthPercentage::Percentage(a)), - &Some(LengthPercentage::Percentage(b)), - ) => { + &generic::GradientItem::ComplexColorStop { + position: ref a_position, + .. + }, + &generic::GradientItem::ComplexColorStop { + position: ref b_position, + .. + }, + ) => match (a_position, b_position) { + (&LengthPercentage::Percentage(a), &LengthPercentage::Percentage(b)) => { return a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal); }, _ => {}, @@ -548,6 +543,16 @@ impl Gradient { } } +#[inline] +fn simple_moz_gradient() -> bool { + #[cfg(feature = "gecko")] + unsafe { + return structs::StaticPrefs_sVarCache_layout_css_simple_moz_gradient_enabled; + } + #[cfg(not(feature = "gecko"))] + return false; +} + impl GradientKind { /// Parses a linear gradient. /// CompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword. @@ -583,16 +588,6 @@ impl GradientKind { }); (shape, position.ok(), None, None) }, - CompatMode::WebKit => { - let position = input.try(|i| Position::parse(context, i)); - let shape = input.try(|i| { - if position.is_ok() { - i.expect_comma()?; - } - EndingShape::parse(context, i, *compat_mode) - }); - (shape, position.ok(), None, None) - }, // The syntax of `-moz-` prefixed radial gradient is: // -moz-radial-gradient( // [ [ <position> || <angle> ]? [ ellipse | [ <length> | <percentage> ]{2} ] , | @@ -603,7 +598,7 @@ impl GradientKind { // where <extent-keyword> = closest-corner | closest-side | farthest-corner | farthest-side | // cover | contain // and <color-stop> = <color> [ <percentage> | <length> ]? - CompatMode::Moz => { + CompatMode::Moz if !simple_moz_gradient() => { let mut position = input.try(|i| LegacyPosition::parse(context, i)); let angle = input.try(|i| Angle::parse(context, i)).ok(); if position.is_err() { @@ -619,6 +614,16 @@ impl GradientKind { (shape, None, angle, position.ok()) }, + _ => { + let position = input.try(|i| Position::parse(context, i)); + let shape = input.try(|i| { + if position.is_ok() { + i.expect_comma()?; + } + EndingShape::parse(context, i, *compat_mode) + }); + (shape, position.ok(), None, None) + }, }; if shape.is_ok() || position.is_some() || angle.is_some() || moz_position.is_some() { @@ -631,7 +636,7 @@ impl GradientKind { #[cfg(feature = "gecko")] { - if *compat_mode == CompatMode::Moz { + if *compat_mode == CompatMode::Moz && !simple_moz_gradient() { // If this form can be represented in Modern mode, then convert the compat_mode to Modern. if angle.is_none() { *compat_mode = CompatMode::Modern; @@ -751,7 +756,7 @@ impl LineDirection { input: &mut Parser<'i, 't>, compat_mode: &mut CompatMode, ) -> Result<Self, ParseError<'i>> { - let mut _angle = if *compat_mode == CompatMode::Moz { + let mut _angle = if *compat_mode == CompatMode::Moz && !simple_moz_gradient() { input.try(|i| Angle::parse(context, i)).ok() } else { // Gradients allow unitless zero angles as an exception, see: @@ -784,7 +789,7 @@ impl LineDirection { #[cfg(feature = "gecko")] { // `-moz-` prefixed linear gradient can be both Angle and Position. - if *compat_mode == CompatMode::Moz { + if *compat_mode == CompatMode::Moz && !simple_moz_gradient() { let position = i.try(|i| LegacyPosition::parse(context, i)).ok(); if _angle.is_none() { _angle = i.try(|i| Angle::parse(context, i)).ok(); @@ -874,7 +879,7 @@ impl EndingShape { } // -moz- prefixed radial gradient doesn't allow EndingShape's Length or LengthPercentage // to come before shape keyword. Otherwise it conflicts with <position>. - if compat_mode != CompatMode::Moz { + if compat_mode != CompatMode::Moz || simple_moz_gradient() { if let Ok(length) = input.try(|i| Length::parse(context, i)) { if let Ok(y) = input.try(|i| LengthPercentage::parse(context, i)) { if compat_mode == CompatMode::Modern { @@ -958,13 +963,16 @@ impl GradientItem { if let Ok(multi_position) = input.try(|i| LengthPercentage::parse(context, i)) { let stop_color = stop.color.clone(); - items.push(generic::GradientItem::ColorStop(stop)); - items.push(generic::GradientItem::ColorStop(ColorStop { - color: stop_color, - position: Some(multi_position), - })); + items.push(stop.into_item()); + items.push( + ColorStop { + color: stop_color, + position: Some(multi_position), + } + .into_item(), + ); } else { - items.push(generic::GradientItem::ColorStop(stop)); + items.push(stop.into_item()); } seen_stop = true; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 64a76ccc582..7dbb93f6319 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -80,6 +80,7 @@ pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg_path::SVGPathData; pub use self::table::XSpan; +pub use self::text::TextTransform; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index f75af3f5585..c0ac56c9822 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -460,6 +460,11 @@ pub enum AutoFlow { Column, } +/// If `dense` is specified, `row` is implied. +fn is_row_dense(autoflow: &AutoFlow, dense: &bool) -> bool { + *autoflow == AutoFlow::Row && *dense +} + #[derive( Clone, Copy, @@ -477,6 +482,7 @@ pub enum AutoFlow { /// specifying exactly how auto-placed items get flowed into the grid pub struct GridAutoFlow { /// Specifiy how auto-placement algorithm fills each `row` or `column` in turn + #[css(contextual_skip_if = "is_row_dense")] pub autoflow: AutoFlow, /// Specify use `dense` packing algorithm or not #[css(represents_keyword)] diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 9d4c6ae649c..6be20f58618 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -351,6 +351,177 @@ impl TextDecorationLine { } } +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +/// Specified value of the text-transform property, stored in two parts: +/// the case-related transforms (mutually exclusive, only one may be in effect), and others (non-exclusive). +pub struct TextTransform { + /// Case transform, if any. + pub case_: TextTransformCase, + /// Non-case transforms. + pub other_: TextTransformOther, +} + +impl TextTransform { + #[inline] + /// Returns the initial value of text-transform + pub fn none() -> Self { + TextTransform { + case_: TextTransformCase::None, + other_: TextTransformOther::empty(), + } + } + #[inline] + /// Returns whether the value is 'none' + pub fn is_none(&self) -> bool { + self.case_ == TextTransformCase::None && self.other_.is_empty() + } +} + +impl Parse for TextTransform { + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + let mut result = TextTransform::none(); + + // Case keywords are mutually exclusive; other transforms may co-occur. + loop { + let location = input.current_source_location(); + let ident = match input.next() { + Ok(&Token::Ident(ref ident)) => ident, + Ok(other) => return Err(location.new_unexpected_token_error(other.clone())), + Err(..) => break, + }; + + match_ignore_ascii_case! { ident, + "none" if result.is_none() => { + return Ok(result); + }, + "uppercase" if result.case_ == TextTransformCase::None => { + result.case_ = TextTransformCase::Uppercase + }, + "lowercase" if result.case_ == TextTransformCase::None => { + result.case_ = TextTransformCase::Lowercase + }, + "capitalize" if result.case_ == TextTransformCase::None => { + result.case_ = TextTransformCase::Capitalize + }, + "full-width" if !result.other_.intersects(TextTransformOther::FULL_WIDTH) => { + result.other_.insert(TextTransformOther::FULL_WIDTH) + }, + "full-size-kana" if !result.other_.intersects(TextTransformOther::FULL_SIZE_KANA) => { + result.other_.insert(TextTransformOther::FULL_SIZE_KANA) + } + _ => return Err(location.new_custom_error( + SelectorParseErrorKind::UnexpectedIdent(ident.clone()) + )), + } + } + + if result.is_none() { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } else { + Ok(result) + } + } +} + +impl ToCss for TextTransform { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + if self.is_none() { + return dest.write_str("none"); + } + + if self.case_ != TextTransformCase::None { + self.case_.to_css(dest)?; + if !self.other_.is_empty() { + dest.write_str(" ")?; + } + } + + self.other_.to_css(dest) + } +} + +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +/// Specified keyword values for case transforms in the text-transform property. (These are exclusive.) +pub enum TextTransformCase { + /// No case transform. + None, + /// All uppercase. + Uppercase, + /// All lowercase. + Lowercase, + /// Capitalize each word. + Capitalize, +} + +bitflags! { + #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] + #[value_info(other_values = "none,full-width,full-size-kana")] + #[repr(C)] + /// Specified keyword values for non-case transforms in the text-transform property. (Non-exclusive.) + pub struct TextTransformOther: u8 { + /// full-width + const FULL_WIDTH = 1 << 0; + /// full-size-kana + const FULL_SIZE_KANA = 1 << 1; + } +} + +impl ToCss for TextTransformOther { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + let mut writer = SequenceWriter::new(dest, " "); + let mut any = false; + macro_rules! maybe_write { + ($ident:ident => $str:expr) => { + if self.contains(TextTransformOther::$ident) { + writer.raw_item($str)?; + any = true; + } + }; + } + + maybe_write!(FULL_WIDTH => "full-width"); + maybe_write!(FULL_SIZE_KANA => "full-size-kana"); + + debug_assert!(any || self.is_empty()); + + Ok(()) + } +} + /// Specified value of text-align keyword value. #[derive( Clone, @@ -394,8 +565,9 @@ pub enum TextAlignKeyword { } /// Specified value of text-align property. -#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] -#[derive(Clone, Copy, Debug, Eq, Hash, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[derive( + Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, +)] pub enum TextAlign { /// Keyword value of text-align property. Keyword(TextAlignKeyword), diff --git a/components/webvr/Cargo.toml b/components/webvr/Cargo.toml index faca3eb57a6..f4d195d29fe 100644 --- a/components/webvr/Cargo.toml +++ b/components/webvr/Cargo.toml @@ -22,7 +22,7 @@ gleam = "0.6" ipc-channel = "0.11" log = "0.4" msg = {path = "../msg"} -rust-webvr = {version = "0.10.2", features = ["openvr", "vrexternal"]} +rust-webvr = {version = "=0.11.0", features = ["openvr", "vrexternal"]} script_traits = {path = "../script_traits"} servo_config = {path = "../config"} webvr_traits = {path = "../webvr_traits" } diff --git a/components/webvr/webvr_thread.rs b/components/webvr/webvr_thread.rs index e7803509c82..e90ddbf7df5 100644 --- a/components/webvr/webvr_thread.rs +++ b/components/webvr/webvr_thread.rs @@ -16,7 +16,7 @@ use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::{thread, time}; use webvr_traits::webvr::*; -use webvr_traits::{WebVRMsg, WebVRResult}; +use webvr_traits::{WebVRMsg, WebVRPoseInformation, WebVRResult}; /// WebVRThread owns native VRDisplays, handles their life cycle inside Servo and /// acts a doorman for untrusted VR requests from DOM Objects. These are the key components @@ -128,6 +128,9 @@ impl WebVRThread { WebVRMsg::GetGamepads(synced_ids, sender) => { self.handle_get_gamepads(synced_ids, sender); }, + WebVRMsg::GetGamepadsForDisplay(display_id, sender) => { + self.handle_get_gamepads_for_display(display_id, sender); + }, WebVRMsg::Exit => break, } } @@ -251,6 +254,32 @@ impl WebVRThread { self.vr_compositor_chan.send(compositor).unwrap(); } + fn handle_get_gamepads_for_display( + &mut self, + display_id: u32, + sender: IpcSender<WebVRResult<Vec<(VRGamepadData, VRGamepadState)>>>, + ) { + match self.service.get_display(display_id) { + Some(display) => { + let gamepads = display.borrow_mut().fetch_gamepads(); + match gamepads { + Ok(gamepads) => { + let data = gamepads + .iter() + .map(|g| { + let g = g.borrow(); + (g.data(), g.state()) + }) + .collect(); + sender.send(Ok(data)).unwrap(); + }, + Err(e) => sender.send(Err(e)).unwrap(), + } + }, + None => sender.send(Err("Device not found".into())).unwrap(), + } + } + fn handle_get_gamepads( &mut self, synced_ids: Vec<u32>, @@ -386,10 +415,23 @@ impl webgl::WebVRRenderHandler for WebVRCompositorHandler { unsafe { (*compositor.0).start_present(None) }; } }, - webgl::WebVRCommand::SyncPoses(compositor_id, near, far, sender) => { + webgl::WebVRCommand::SyncPoses(compositor_id, near, far, get_gamepads, sender) => { if let Some(compositor) = self.compositors.get(&compositor_id) { let pose = unsafe { (*compositor.0).future_frame_data(near, far) }; - let _ = sender.send(Ok(pose)); + let mut pose_information = WebVRPoseInformation { + frame: pose, + gamepads: vec![], + }; + if get_gamepads { + let gamepads = unsafe { (*compositor.0).fetch_gamepads() }; + if let Ok(gamepads) = gamepads { + for gamepad in gamepads { + let g = gamepad.borrow(); + pose_information.gamepads.push((g.id(), g.state())); + } + } + } + let _ = sender.send(Ok(pose_information)); } else { let _ = sender.send(Err(())); } diff --git a/components/webvr_traits/Cargo.toml b/components/webvr_traits/Cargo.toml index 032729334f3..bf268053a9d 100644 --- a/components/webvr_traits/Cargo.toml +++ b/components/webvr_traits/Cargo.toml @@ -13,5 +13,5 @@ path = "lib.rs" [dependencies] ipc-channel = "0.11" msg = {path = "../msg"} -rust-webvr-api = {version = "0.10.3", features = ["ipc"]} +rust-webvr-api = {version = "=0.11.0", features = ["ipc"]} serde = "1.0" diff --git a/components/webvr_traits/lib.rs b/components/webvr_traits/lib.rs index ab31436c136..f96fee056e7 100644 --- a/components/webvr_traits/lib.rs +++ b/components/webvr_traits/lib.rs @@ -30,3 +30,9 @@ pub use rust_webvr_api::VRLayer as WebVRLayer; pub use rust_webvr_api::VRMainThreadHeartbeat as WebVRMainThreadHeartbeat; pub use rust_webvr_api::VRPose as WebVRPose; pub use rust_webvr_api::VRStageParameters as WebVRStageParameters; + +#[derive(Deserialize, Serialize)] +pub struct WebVRPoseInformation { + pub frame: WebVRFutureFrameData, + pub gamepads: Vec<(u32, WebVRGamepadState)>, +} diff --git a/components/webvr_traits/webvr_traits.rs b/components/webvr_traits/webvr_traits.rs index 0f6dd069867..d21e6c3ff16 100644 --- a/components/webvr_traits/webvr_traits.rs +++ b/components/webvr_traits/webvr_traits.rs @@ -30,5 +30,9 @@ pub enum WebVRMsg { Vec<u32>, IpcSender<WebVRResult<Vec<(Option<VRGamepadData>, VRGamepadState)>>>, ), + GetGamepadsForDisplay( + u32, + IpcSender<WebVRResult<Vec<(VRGamepadData, VRGamepadState)>>>, + ), Exit, } diff --git a/etc/ci/update-wpt-checkout b/etc/ci/update-wpt-checkout index a058779f554..c8b3ece372d 100755 --- a/etc/ci/update-wpt-checkout +++ b/etc/ci/update-wpt-checkout @@ -92,12 +92,12 @@ function unsafe_open_pull_request() { git checkout "${BRANCH_NAME}" || return 0 if [[ -z "${WPT_SYNC_TOKEN+set}" ]]; then - SECRET_RESPONSE=$(curl $TASKCLUSTER_PROXY_URL/api/secrets/v1/secret/project/servo/wpt-sync) - WPT_SYNC_TOKEN=`echo "${OPEN_PR_RESPONSE}" | jq '.token'` + SECRET_RESPONSE=$(curl ${TASKCLUSTER_PROXY_URL}/secrets/v1/secret/project/servo/wpt-sync) + WPT_SYNC_TOKEN=`echo "${SECRET_RESPONSE}" | jq --raw-output '.secret.token'` fi if [[ -z "${WPT_SYNC_TOKEN}" ]]; then - echo "Github auth token missing from .wpt-token file." + echo "Github auth token missing from WPT_SYNC_TOKEN." return 1 fi diff --git a/etc/taskcluster/decision_task.py b/etc/taskcluster/decision_task.py index e5b16c5b952..ac009c874ad 100644 --- a/etc/taskcluster/decision_task.py +++ b/etc/taskcluster/decision_task.py @@ -90,10 +90,10 @@ def main(task_for): daily_tasks_setup() with_rust_nightly() linux_nightly() - android_nightly("arm") - android_nightly("x86") + android_nightly() windows_nightly() macos_nightly() + update_wpt() # These are disabled in a "real" decision task, @@ -264,36 +264,27 @@ def android_arm32_dev(): ) -def android_nightly(job): - details = { - "arm": { - "mach_flag": "--android", - "name": "ARMv7", - "target": "armv7-linux-androideabi", - }, - "x86": { - "mach_flag": "--target i686-linux-android", - "name": "x86", - "target": "i686-linux-android", - } - } - +def android_nightly(): return ( android_build_task("Nightly build and upload") - .with_treeherder("Android " + details[job]["name"], "Nightly") + .with_treeherder("Android Nightlies") .with_features("taskclusterProxy") .with_scopes("secrets:get:project/servo/s3-upload-credentials") .with_script(""" - ./mach build {flag} --release - ./mach package {flag} --release --maven + ./mach build --release --android + ./mach package --release --android --maven + ./mach build --release --target i686-linux-android + ./mach package --release --target i686-linux-android --maven ./mach upload-nightly android --secret-from-taskcluster ./mach upload-nightly maven --secret-from-taskcluster - """.format(flag=details[job]["mach_flag"])) + """) .with_artifacts( - "/repo/target/android/%s/release/servoapp.apk" % details[job]["target"], - "/repo/target/android/%s/release/servoview.aar" % details[job]["target"], + "/repo/target/android/armv7-linux-androideabi/release/servoapp.apk", + "/repo/target/android/armv7-linux-androideabi/release/servoview.aar", + "/repo/target/android/i686-linux-android/release/servoapp.apk", + "/repo/target/android/i686-linux-android/release/servoview.aar", ) - .find_or_create(("build.android_%s_nightly." + CONFIG.git_sha) % details[job]["name"].lower()) + .find_or_create("build.android_nightlies." + CONFIG.git_sha) ) @@ -395,6 +386,7 @@ def windows_nightly(): return ( windows_build_task("Nightly build and upload") .with_treeherder("Windows x64", "Nightly") + .with_features("taskclusterProxy") .with_scopes("secrets:get:project/servo/s3-upload-credentials") .with_script("mach build --release", "mach package --release", @@ -451,7 +443,6 @@ def macos_nightly(): .with_scopes( "secrets:get:project/servo/s3-upload-credentials", "secrets:get:project/servo/github-homebrew-token", - "secrets:get:project/servo/wpt-sync", ) .with_script( "./mach build --release", @@ -459,15 +450,36 @@ def macos_nightly(): "./mach upload-nightly mac --secret-from-taskcluster", ) .with_artifacts("repo/target/release/servo-tech-demo.dmg") - .with_script( - "./etc/ci/update-wpt-checkout fetch-and-update-expectations", - "./etc/ci/update-wpt-checkout open-pr", - "./etc/ci/update-wpt-checkout cleanup", - ) .find_or_create("build.mac_x64_nightly." + CONFIG.git_sha) ) +def update_wpt(): + # Reuse the release build that was made for landing the PR + build_task = decisionlib.Task.find("build.macos_x64_release." + CONFIG.git_sha) + update_task = ( + macos_task("WPT update") + .with_python2() + .with_treeherder("macOS x64", "WPT update") + .with_features("taskclusterProxy") + .with_scopes("secrets:get:project/servo/wpt-sync") + .with_index_and_artifacts_expire_in(log_artifacts_expire_in) + .with_max_run_time_minutes(5 * 60) + ) + return ( + with_homebrew(update_task, "etc/taskcluster/macos/Brewfile-wpt") + .with_repo() + .with_curl_artifact_script(build_task, "target.tar.gz") + .with_script(""" + tar -xzf target.tar.gz + ./etc/ci/update-wpt-checkout fetch-and-update-expectations + ./etc/ci/update-wpt-checkout open-pr + ./etc/ci/update-wpt-checkout cleanup + """) + .find_or_create("wpt_update." + CONFIG.git_sha) + ) + + def macos_wpt(): build_task = ( macos_build_task("Release build") @@ -691,8 +703,19 @@ def windows_build_task(name, package=True, arch="x86_64"): return task +def with_homebrew(task, brewfile): + return task.with_script(""" + mkdir -p "$HOME/homebrew" + export PATH="$HOME/homebrew/bin:$PATH" + which brew || curl -L https://github.com/Homebrew/brew/tarball/master \ + | tar xz --strip 1 -C "$HOME/homebrew" + + time brew bundle install --no-upgrade --file={brewfile} + """.format(brewfile=brewfile)) + + def macos_build_task(name): - return ( + build_task = ( macos_task(name) # Allow long runtime in case the cache expired for all those Homebrew dependencies .with_max_run_time_minutes(60 * 2) @@ -700,13 +723,10 @@ def macos_build_task(name): .with_repo() .with_python2() .with_rustup() + ) + return ( + with_homebrew(build_task, "etc/taskcluster/macos/Brewfile") .with_script(""" - mkdir -p "$HOME/homebrew" - export PATH="$HOME/homebrew/bin:$PATH" - which brew || curl -L https://github.com/Homebrew/brew/tarball/master \ - | tar xz --strip 1 -C "$HOME/homebrew" - - time brew bundle install --no-upgrade --file=etc/taskcluster/macos/Brewfile export OPENSSL_INCLUDE_DIR="$(brew --prefix openssl)/include" export OPENSSL_LIB_DIR="$(brew --prefix openssl)/lib" """) diff --git a/etc/taskcluster/macos/Brewfile-wpt b/etc/taskcluster/macos/Brewfile-wpt new file mode 100644 index 00000000000..f563d5e7bf9 --- /dev/null +++ b/etc/taskcluster/macos/Brewfile-wpt @@ -0,0 +1 @@ +brew "jq"
\ No newline at end of file diff --git a/etc/taskcluster/windows/first-boot.ps1 b/etc/taskcluster/windows/first-boot.ps1 index 896f562bacc..9da5091e99c 100644 --- a/etc/taskcluster/windows/first-boot.ps1 +++ b/etc/taskcluster/windows/first-boot.ps1 @@ -39,6 +39,8 @@ $client.DownloadFile("https://github.com/taskcluster/generic-worker/releases/dow "/v14.1.0/generic-worker-nativeEngine-windows-amd64.exe", "C:\generic-worker\generic-worker.exe") $client.DownloadFile("https://github.com/taskcluster/livelog/releases/download" + "/v1.1.0/livelog-windows-amd64.exe", "C:\generic-worker\livelog.exe") +$client.DownloadFile("https://github.com/taskcluster/taskcluster-proxy/releases/download" + + "/v5.1.0/taskcluster-proxy-windows-amd64.exe", "C:\generic-worker\taskcluster-proxy.exe") Expand-ZIPFile -File "C:\nssm-2.24.zip" -Destination "C:\" ` -Url "https://www.nssm.cc/release/nssm-2.24.zip" Start-Process C:\generic-worker\generic-worker.exe -ArgumentList ` diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index 4c055116e39..9135e87b623 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -52,7 +52,7 @@ lazy_static = "1" libservo = {path = "../../components/servo"} libc = "0.2" log = "0.4" -rust-webvr = { version = "0.10.2", features = ["glwindow"] } +rust-webvr = { version = "=0.11.0", features = ["glwindow"] } tinyfiledialogs = "3.0" [target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies] diff --git a/python/mach_bootstrap.py b/python/mach_bootstrap.py index aded4c0a0e9..f94f2c6f772 100644 --- a/python/mach_bootstrap.py +++ b/python/mach_bootstrap.py @@ -293,6 +293,10 @@ def bootstrap(topdir): mach.define_category(category, meta['short'], meta['long'], meta['priority']) for path in MACH_MODULES: - mach.load_commands_from_file(os.path.join(topdir, path)) + # explicitly provide a module name + # workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1549636 + file = os.path.basename(path) + module_name = os.path.splitext(file)[0] + mach.load_commands_from_file(os.path.join(topdir, path), module_name) return mach diff --git a/python/servo/bootstrap.py b/python/servo/bootstrap.py index 28292ea3510..0b5da09501d 100644 --- a/python/servo/bootstrap.py +++ b/python/servo/bootstrap.py @@ -124,7 +124,7 @@ def linux(context, force=False): 'ccache', 'mesa-libGLU-devel', 'clang', 'clang-libs', 'gstreamer1-devel', 'gstreamer1-plugins-base-devel', 'gstreamer1-plugins-bad-free-devel', 'autoconf213'] if context.distro == "Ubuntu": - if context.distro_version == "17.04": + if context.distro_version in ["17.04", "19.04"]: pkgs_apt += ["libssl-dev"] elif int(context.distro_version.split(".")[0]) < 17: pkgs_apt += ["libssl-dev"] @@ -384,14 +384,16 @@ def get_linux_distribution(): base_version = '10.10' else: raise Exception('unsupported version of %s: %s' % (distro, version)) - distro, version = 'Ubuntu', base_version + elif distro.lower() == 'ubuntu': + if version > '19.04': + raise Exception('unsupported version of %s: %s' % (distro, version)) + # Fixme: we should allow checked/supported versions only elif distro.lower() not in [ 'centos', 'centos linux', 'debian', 'fedora', - 'ubuntu', ]: raise Exception('mach bootstrap does not support %s, please file a bug' % distro) diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py index 3b01ebaa074..abe4e1003f9 100644 --- a/python/servo/build_commands.py +++ b/python/servo/build_commands.py @@ -212,11 +212,6 @@ class MachCommands(CommandBase): print("Please specify either --target or --android.") sys.exit(1) - # https://github.com/servo/servo/issues/22069 - if debug_mozjs and magicleap: - print("Please specify either --debug-mozjs or --magicleap.") - sys.exit(1) - if android: target = self.config["android"]["target"] @@ -576,6 +571,7 @@ class MachCommands(CommandBase): env.setdefault("HARFBUZZ_SYS_NO_PKG_CONFIG", "1") env.setdefault("PKG_CONFIG_ALLOW_CROSS", "1") env.setdefault("CMAKE_TOOLCHAIN_FILE", path.join(ml_support, "toolchain.cmake")) + env.setdefault("_LIBCPP_INLINE_VISIBILITY", "__attribute__((__always_inline__))") # The Open SSL configuration env.setdefault("OPENSSL_DIR", path.join(target_path, target, "native", "openssl")) diff --git a/python/servo/package_commands.py b/python/servo/package_commands.py index 8ff1170872d..12ab0607a3a 100644 --- a/python/servo/package_commands.py +++ b/python/servo/package_commands.py @@ -546,7 +546,11 @@ class PackageCommands(CommandBase): import boto3 def get_taskcluster_secret(name): - url = os.environ["TASKCLUSTER_PROXY_URL"] + "/secrets/v1/secret/project/servo/" + name + url = ( + os.environ.get("TASKCLUSTER_PROXY_URL", "http://taskcluster") + + "/secrets/v1/secret/project/servo/" + + name + ) return json.load(urllib.urlopen(url))["secret"] def get_s3_secret(): diff --git a/resources/prefs.json b/resources/prefs.json index b882ad9ca46..23c42c3352d 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -42,8 +42,8 @@ "js.ion.enabled": true, "js.ion.offthread_compilation.enabled": true, "js.ion.unsafe_eager_compilation.enabled": false, - "js.mem.gc.allocation_threshold_avoid_interrupt_factor": 1, - "js.mem.gc.allocation_threshold_factor": 1, + "js.mem.gc.allocation_threshold_avoid_interrupt_factor": 100, + "js.mem.gc.allocation_threshold_factor": 100, "js.mem.gc.allocation_threshold_mb": 30, "js.mem.gc.compacting.enabled": true, "js.mem.gc.decommit_threshold_mb": 32, diff --git a/support/magicleap/Servo2D/Servo2D.mabu b/support/magicleap/Servo2D/Servo2D.mabu index 60404f038ea..43d6fe26b3f 100644 --- a/support/magicleap/Servo2D/Servo2D.mabu +++ b/support/magicleap/Servo2D/Servo2D.mabu @@ -14,10 +14,14 @@ LIBPATHS.debug = \ LIBPATHS.release = \ ../../../target/magicleap/aarch64-linux-android/release +LDFLAGS.device = \ + -L$(MLSDK)/lumin/stl/libc++/lib + STLIBS = \ mlservo SHLIBS = \ + c++abi \ log \ z diff --git a/support/magicleap/fake-ld.sh b/support/magicleap/fake-ld.sh index 9e6f7ca7132..634f1c324bf 100755 --- a/support/magicleap/fake-ld.sh +++ b/support/magicleap/fake-ld.sh @@ -11,7 +11,7 @@ set -o pipefail MAGICLEAP_TOOLCHAIN=${MAGICLEAP_TOOLCHAIN:-"${MAGICLEAP_SDK}/tools/toolchains"} TARGET=${TARGET:-"aarch64-linux-android"} LD=${LD:-"${MAGICLEAP_TOOLCHAIN}/bin/${TARGET}-ld"} -LDFLAGS=${LDFLAGS:-"-L${MAGICLEAP_SDK}/lumin/stl/libc++/lib -L${MAGICLEAP_SDK}/lumin/usr/lib -L${MAGICLEAP_TOOLCHAIN}/lib/gcc/${TARGET}/4.9.x ${MAGICLEAP_SDK}/lumin/usr/lib/crtbegin_so.o"} +LDFLAGS=${LDFLAGS:-"-L${MAGICLEAP_SDK}/lumin/stl/libc++/lib -L${MAGICLEAP_SDK}/lumin/usr/lib -L${MAGICLEAP_TOOLCHAIN}/lib/gcc/${TARGET}/4.9.x"} # Remove the -landroid flag, grr ARGS=("$@") diff --git a/tests/wpt/metadata/css/css-text/parsing/text-transform-valid.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-transform-valid.html.ini deleted file mode 100644 index c9e0f5c67db..00000000000 --- a/tests/wpt/metadata/css/css-text/parsing/text-transform-valid.html.ini +++ /dev/null @@ -1,43 +0,0 @@ -[text-transform-valid.html] - [e.style['text-transform'\] = "full-size-kana full-width capitalize" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-width" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "capitalize full-width" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-size-kana full-width" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "capitalize full-width full-size-kana" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-width full-size-kana" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-size-kana lowercase full-width" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-size-kana capitalize" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "lowercase full-size-kana full-width" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-width lowercase" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-size-kana" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-width full-size-kana uppercase" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "uppercase full-size-kana" should set the property value] - expected: FAIL - - [e.style['text-transform'\] = "full-width uppercase full-size-kana" should set the property value] - expected: FAIL - diff --git a/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-005.html.ini b/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-005.html.ini new file mode 100644 index 00000000000..a2b9601ab6f --- /dev/null +++ b/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-005.html.ini @@ -0,0 +1,4 @@ +[word-break-break-all-005.html] + type: reftest + expected: + if os == "linux": FAIL diff --git a/tests/wpt/metadata/css/css-variables/wide-keyword-fallback.html.ini b/tests/wpt/metadata/css/css-variables/wide-keyword-fallback.html.ini deleted file mode 100644 index 3bdd09230d8..00000000000 --- a/tests/wpt/metadata/css/css-variables/wide-keyword-fallback.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[wide-keyword-fallback.html] - expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini index bc63975b5c0..acc60d48f6a 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini @@ -2,9 +2,6 @@ [video/mp4; codecs="mp4v.20.8" (optional)] expected: FAIL - [video/webm with bogus codec] - expected: FAIL - [video/3gpp; codecs="samr" (optional)] expected: FAIL @@ -14,16 +11,10 @@ [audio/webm (optional)] expected: FAIL - [audio/mp4; codecs="mp4a.40.2" (optional)] - expected: FAIL - - [audio/mp4 with bogus codec] + [audio/webm with and without codecs] expected: FAIL - [audio/webm; codecs="vorbis" (optional)] - expected: FAIL - - [video/webm; codecs="vp9" (optional)] + [audio/mp4; codecs="mp4a.40.2" (optional)] expected: FAIL [video/ogg; codecs="theora" (optional)] @@ -32,15 +23,6 @@ [video/mp4; codecs="avc1.64001E" (optional)] expected: FAIL - [video/ogg; codecs="opus" (optional)] - expected: FAIL - - [fictional formats and codecs not supported] - expected: FAIL - - [video/mp4 with bogus codec] - expected: FAIL - [video/mp4 (optional)] expected: FAIL @@ -50,10 +32,10 @@ [audio/ogg; codecs="opus" (optional)] expected: FAIL - [video/webm; codecs="vp8.0" (optional)] + [video/webm (optional)] expected: FAIL - [video/webm (optional)] + [video/webm with and without codecs] expected: FAIL [video/mp4; codecs="avc1.4D401E" (optional)] @@ -62,9 +44,6 @@ [audio/wav; codecs="1" (optional)] expected: FAIL - [video/3gpp with bogus codec] - expected: FAIL - [audio/wav (optional)] expected: FAIL @@ -80,9 +59,6 @@ [audio/mp4 (optional)] expected: FAIL - [video/webm; codecs="opus" (optional)] - expected: FAIL - [video/3gpp (optional)] expected: FAIL @@ -95,24 +71,8 @@ [audio/ogg; codecs="vorbis" (optional)] expected: FAIL - [audio/webm; codecs="opus" (optional)] - expected: FAIL - - [video/webm; codecs="vorbis" (optional)] - expected: FAIL - - [audio/wav with bogus codec] + [video/ogg; codecs="opus" (optional)] expected: FAIL [video/3gpp; codecs="mp4v.20.8" (optional)] expected: FAIL - - [video/webm; codecs="vp9.0" (optional)] - expected: FAIL - - [video/webm; codecs="vp8" (optional)] - expected: FAIL - - [audio/webm with bogus codec] - expected: FAIL - |