diff options
Diffstat (limited to 'components')
74 files changed, 2732 insertions, 1528 deletions
diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 27d45e44d88..ebf4a2c27be 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -129,7 +129,9 @@ time timeupdate toggle track +transitioncancel transitionend +transitionrun unhandledrejection unload url @@ -141,5 +143,6 @@ webkitAnimationEnd webkitAnimationIteration webkitAnimationStart webkitTransitionEnd +webkitTransitionRun week width diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 2561ebf937f..b6f2030ad9c 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -297,8 +297,16 @@ mod gen { enabled: bool, #[serde(default)] test: bool, - #[serde(default)] - glwindow: bool, + glwindow: { + #[serde(default)] + enabled: bool, + #[serde(rename = "dom.webxr.glwindow.red-cyan")] + red_cyan: bool, + }, + hands: { + #[serde(default)] + enabled: bool, + }, layers: { enabled: bool, } diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index 29be9649c02..2fed3e7b6c8 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -11,40 +11,40 @@ name = "constellation" path = "lib.rs" [dependencies] -background_hang_monitor = { path = "../background_hang_monitor"} +background_hang_monitor = { path = "../background_hang_monitor" } backtrace = "0.3" bluetooth_traits = { path = "../bluetooth_traits" } -canvas_traits = {path = "../canvas_traits"} -compositing = {path = "../compositing"} +canvas_traits = { path = "../canvas_traits" } +compositing = { path = "../compositing" } crossbeam-channel = "0.4" -debugger = {path = "../debugger"} -devtools_traits = {path = "../devtools_traits"} -euclid = "0.20" +debugger = { path = "../debugger" } +devtools_traits = { path = "../devtools_traits" } embedder_traits = { path = "../embedder_traits" } -gfx = {path = "../gfx"} -gfx_traits = {path = "../gfx_traits"} +euclid = "0.20" +gfx = { path = "../gfx" } +gfx_traits = { path = "../gfx_traits" } http = "0.1" ipc-channel = "0.14" -layout_traits = {path = "../layout_traits"} keyboard-types = "0.4.3" +layout_traits = { path = "../layout_traits" } log = "0.4" -media = {path = "../media"} -metrics = {path = "../metrics"} -msg = {path = "../msg"} -net = {path = "../net"} -net_traits = {path = "../net_traits"} -profile_traits = {path = "../profile_traits"} -script_traits = {path = "../script_traits"} +media = { path = "../media" } +metrics = { path = "../metrics" } +msg = { path = "../msg" } +net = { path = "../net" } +net_traits = { path = "../net_traits" } +profile_traits = { path = "../profile_traits" } +script_traits = { path = "../script_traits" } serde = "1.0" -style_traits = {path = "../style_traits"} -servo_config = {path = "../config"} -servo_geometry = {path = "../geometry"} -servo_rand = {path = "../rand"} -servo_remutex = {path = "../remutex"} -servo_url = {path = "../url"} -webgpu = {path = "../webgpu"} -webrender_api = {git = "https://github.com/servo/webrender"} -webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]} +servo_config = { path = "../config" } +servo_geometry = { path = "../geometry" } +servo_rand = {path = "../rand" } +servo_remutex = { path = "../remutex" } +servo_url = { path = "../url" } +style_traits = { path = "../style_traits" } +webgpu = { path = "../webgpu" } +webrender_api = { git = "https://github.com/servo/webrender" } +webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] } [target.'cfg(all(not(target_os = "windows"), not(target_os = "ios"), not(target_os="android"), not(target_arch="arm"), not(target_arch="aarch64")))'.dependencies] gaol = "0.2.1" diff --git a/components/devtools/Cargo.toml b/components/devtools/Cargo.toml index 69d440f0f7a..8b7c6d6e199 100644 --- a/components/devtools/Cargo.toml +++ b/components/devtools/Cargo.toml @@ -22,5 +22,6 @@ log = "0.4" msg = {path = "../msg"} serde = "1.0" serde_json = "1.0" +servo_url = { path = "../url" } time = "0.1" uuid = {version = "0.8", features = ["v4"]} diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index bdc468f9f84..b90d84d04bd 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -8,10 +8,22 @@ //! Supports dynamic attaching and detaching which control notifications of navigation, etc. use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::actors::console::ConsoleActor; +use crate::actors::emulation::EmulationActor; +use crate::actors::inspector::InspectorActor; +use crate::actors::performance::PerformanceActor; +use crate::actors::profiler::ProfilerActor; +use crate::actors::root::RootActor; +use crate::actors::stylesheets::StyleSheetsActor; +use crate::actors::thread::ThreadActor; +use crate::actors::timeline::TimelineActor; use crate::protocol::JsonPacketStream; use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications}; +use devtools_traits::DevtoolsPageInfo; +use devtools_traits::NavigationState; +use ipc_channel::ipc::IpcSender; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use serde_json::{Map, Value}; +use std::cell::{Cell, RefCell}; use std::net::TcpStream; #[derive(Serialize)] @@ -108,8 +120,8 @@ pub struct BrowsingContextActorMsg { pub struct BrowsingContextActor { pub name: String, - pub title: String, - pub url: String, + pub title: RefCell<String>, + pub url: RefCell<String>, pub console: String, pub emulation: String, pub inspector: String, @@ -118,6 +130,10 @@ pub struct BrowsingContextActor { pub performance: String, pub styleSheets: String, pub thread: String, + pub streams: RefCell<Vec<TcpStream>>, + pub browsing_context_id: BrowsingContextId, + pub active_pipeline: Cell<PipelineId>, + pub script_chan: IpcSender<DevtoolScriptControlMsg>, } impl Actor for BrowsingContextActor { @@ -127,7 +143,7 @@ impl Actor for BrowsingContextActor { fn handle_message( &self, - registry: &ActorRegistry, + _registry: &ActorRegistry, msg_type: &str, msg: &Map<String, Value>, stream: &mut TcpStream, @@ -137,10 +153,9 @@ impl Actor for BrowsingContextActor { if let Some(options) = msg.get("options").and_then(|o| o.as_object()) { if let Some(val) = options.get("performReload") { if val.as_bool().unwrap_or(false) { - let console_actor = registry.find::<ConsoleActor>(&self.console); - let _ = console_actor + let _ = self .script_chan - .send(DevtoolScriptControlMsg::Reload(console_actor.pipeline)); + .send(DevtoolScriptControlMsg::Reload(self.active_pipeline.get())); } } } @@ -159,38 +174,31 @@ impl Actor for BrowsingContextActor { javascriptEnabled: true, traits: AttachedTraits { reconfigure: false, - frames: false, + frames: true, logInPage: false, canRewind: false, watchpoints: false, }, }; - let console_actor = registry.find::<ConsoleActor>(&self.console); - console_actor - .streams - .borrow_mut() - .push(stream.try_clone().unwrap()); + self.streams.borrow_mut().push(stream.try_clone().unwrap()); stream.write_json_packet(&msg); - console_actor - .script_chan - .send(WantsLiveNotifications(console_actor.pipeline, true)) + self.script_chan + .send(WantsLiveNotifications(self.active_pipeline.get(), true)) .unwrap(); ActorMessageStatus::Processed }, - //FIXME: The current implementation won't work for multiple connections. Need to ensure 105 + //FIXME: The current implementation won't work for multiple connections. Need to ensure // that the correct stream is removed. "detach" => { let msg = BrowsingContextDetachedReply { from: self.name(), type_: "detached".to_owned(), }; - let console_actor = registry.find::<ConsoleActor>(&self.console); - console_actor.streams.borrow_mut().pop(); + self.streams.borrow_mut().pop(); stream.write_json_packet(&msg); - console_actor - .script_chan - .send(WantsLiveNotifications(console_actor.pipeline, false)) + self.script_chan + .send(WantsLiveNotifications(self.active_pipeline.get(), false)) .unwrap(); ActorMessageStatus::Processed }, @@ -199,10 +207,11 @@ impl Actor for BrowsingContextActor { let msg = ListFramesReply { from: self.name(), frames: vec![FrameMsg { - id: 0, //FIXME should match outerwindow id + //FIXME: shouldn't ignore pipeline namespace field + id: self.active_pipeline.get().index.0.get(), parentID: 0, - url: self.url.clone(), - title: self.title.clone(), + url: self.url.borrow().clone(), + title: self.title.borrow().clone(), }], }; stream.write_json_packet(&msg); @@ -224,16 +233,82 @@ impl Actor for BrowsingContextActor { } impl BrowsingContextActor { + pub(crate) fn new( + console: String, + id: BrowsingContextId, + page_info: DevtoolsPageInfo, + pipeline: PipelineId, + script_sender: IpcSender<DevtoolScriptControlMsg>, + actors: &mut ActorRegistry, + ) -> BrowsingContextActor { + let emulation = EmulationActor::new(actors.new_name("emulation")); + + let name = actors.new_name("target"); + + let inspector = InspectorActor { + name: actors.new_name("inspector"), + walker: RefCell::new(None), + pageStyle: RefCell::new(None), + highlighter: RefCell::new(None), + script_chan: script_sender.clone(), + browsing_context: name.clone(), + }; + + let timeline = + TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone()); + + let profiler = ProfilerActor::new(actors.new_name("profiler")); + let performance = PerformanceActor::new(actors.new_name("performance")); + + // the strange switch between styleSheets and stylesheets is due + // to an inconsistency in devtools. See Bug #1498893 in bugzilla + let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets")); + let thread = ThreadActor::new(actors.new_name("context")); + + let DevtoolsPageInfo { title, url } = page_info; + let target = BrowsingContextActor { + name: name, + script_chan: script_sender, + title: RefCell::new(String::from(title)), + url: RefCell::new(url.into_string()), + console: console, + emulation: emulation.name(), + inspector: inspector.name(), + timeline: timeline.name(), + profiler: profiler.name(), + performance: performance.name(), + styleSheets: styleSheets.name(), + thread: thread.name(), + streams: RefCell::new(Vec::new()), + browsing_context_id: id, + active_pipeline: Cell::new(pipeline), + }; + + actors.register(Box::new(emulation)); + actors.register(Box::new(inspector)); + actors.register(Box::new(timeline)); + actors.register(Box::new(profiler)); + actors.register(Box::new(performance)); + actors.register(Box::new(styleSheets)); + actors.register(Box::new(thread)); + + let root = actors.find_mut::<RootActor>("root"); + root.tabs.push(target.name.clone()); + target + } + pub fn encodable(&self) -> BrowsingContextActorMsg { BrowsingContextActorMsg { actor: self.name(), traits: BrowsingContextTraits { isBrowsingContext: true, }, - title: self.title.clone(), - url: self.url.clone(), - browsingContextId: 0, //FIXME should come from constellation - outerWindowID: 0, //FIXME: this should probably be the pipeline id + title: self.title.borrow().clone(), + url: self.url.borrow().clone(), + //FIXME: shouldn't ignore pipeline namespace field + browsingContextId: self.browsing_context_id.index.0.get(), + //FIXME: shouldn't ignore pipeline namespace field + outerWindowID: self.active_pipeline.get().index.0.get(), consoleActor: self.console.clone(), emulationActor: self.emulation.clone(), inspectorActor: self.inspector.clone(), @@ -243,4 +318,52 @@ impl BrowsingContextActor { styleSheetsActor: self.styleSheets.clone(), } } + + pub(crate) fn navigate(&self, state: NavigationState) { + let (pipeline, title, url, state) = match state { + NavigationState::Start(url) => (None, None, url, "start"), + NavigationState::Stop(pipeline, info) => { + (Some(pipeline), Some(info.title), info.url, "stop") + }, + }; + if let Some(p) = pipeline { + self.active_pipeline.set(p); + } + *self.url.borrow_mut() = url.as_str().to_owned(); + if let Some(ref t) = title { + *self.title.borrow_mut() = t.clone(); + } + + let msg = TabNavigated { + from: self.name(), + type_: "tabNavigated".to_owned(), + url: url.as_str().to_owned(), + title: title, + nativeConsoleAPI: true, + state: state.to_owned(), + isFrameSwitching: false, + }; + for stream in &mut *self.streams.borrow_mut() { + stream.write_json_packet(&msg); + } + } + + pub(crate) fn title_changed(&self, pipeline: PipelineId, title: String) { + if pipeline != self.active_pipeline.get() { + return; + } + *self.title.borrow_mut() = title; + } +} + +#[derive(Serialize)] +struct TabNavigated { + from: String, + #[serde(rename = "type")] + type_: String, + url: String, + title: Option<String>, + nativeConsoleAPI: bool, + state: String, + isFrameSwitching: bool, } diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 845e39075b1..2a8b016b93d 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -8,19 +8,23 @@ //! inspection, JS evaluation, autocompletion) in Servo. use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::object::ObjectActor; +use crate::actors::worker::WorkerActor; use crate::protocol::JsonPacketStream; -use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg}; +use crate::UniqueId; use devtools_traits::CachedConsoleMessage; +use devtools_traits::ConsoleMessage; use devtools_traits::EvaluateJSReply::{ActorValue, BooleanValue, StringValue}; use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue}; use devtools_traits::{ CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError, }; use ipc_channel::ipc::{self, IpcSender}; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::TEST_PIPELINE_ID; use serde_json::{self, Map, Number, Value}; -use std::cell::RefCell; +use std::cell::{RefCell, RefMut}; +use std::collections::HashMap; use std::net::TcpStream; use time::precise_time_ns; use uuid::Uuid; @@ -104,15 +108,52 @@ struct SetPreferencesReply { updated: Vec<String>, } -pub struct ConsoleActor { +pub(crate) enum Root { + BrowsingContext(String), + DedicatedWorker(String), +} + +pub(crate) struct ConsoleActor { pub name: String, - pub pipeline: PipelineId, - pub script_chan: IpcSender<DevtoolScriptControlMsg>, - pub streams: RefCell<Vec<TcpStream>>, - pub cached_events: RefCell<Vec<CachedConsoleMessage>>, + pub root: Root, + pub cached_events: RefCell<HashMap<UniqueId, Vec<CachedConsoleMessage>>>, } impl ConsoleActor { + fn script_chan<'a>( + &self, + registry: &'a ActorRegistry, + ) -> &'a IpcSender<DevtoolScriptControlMsg> { + match &self.root { + Root::BrowsingContext(bc) => ®istry.find::<BrowsingContextActor>(bc).script_chan, + Root::DedicatedWorker(worker) => ®istry.find::<WorkerActor>(worker).script_chan, + } + } + + fn streams_mut<'a>(&self, registry: &'a ActorRegistry) -> RefMut<'a, Vec<TcpStream>> { + match &self.root { + Root::BrowsingContext(bc) => registry + .find::<BrowsingContextActor>(bc) + .streams + .borrow_mut(), + Root::DedicatedWorker(worker) => { + registry.find::<WorkerActor>(worker).streams.borrow_mut() + }, + } + } + + fn current_unique_id(&self, registry: &ActorRegistry) -> UniqueId { + match &self.root { + Root::BrowsingContext(bc) => UniqueId::Pipeline( + registry + .find::<BrowsingContextActor>(bc) + .active_pipeline + .get(), + ), + Root::DedicatedWorker(w) => UniqueId::Worker(registry.find::<WorkerActor>(w).id), + } + } + fn evaluateJS( &self, registry: &ActorRegistry, @@ -120,9 +161,15 @@ impl ConsoleActor { ) -> Result<EvaluateJSReply, ()> { let input = msg.get("text").unwrap().as_str().unwrap().to_owned(); let (chan, port) = ipc::channel().unwrap(); - self.script_chan + // FIXME: redesign messages so we don't have to fake pipeline ids when + // communicating with workers. + let pipeline = match self.current_unique_id(registry) { + UniqueId::Pipeline(p) => p, + UniqueId::Worker(_) => TEST_PIPELINE_ID, + }; + self.script_chan(registry) .send(DevtoolScriptControlMsg::EvaluateJS( - self.pipeline, + pipeline, input.clone(), chan, )) @@ -191,21 +238,35 @@ impl ConsoleActor { std::result::Result::Ok(reply) } - pub(crate) fn handle_page_error(&self, page_error: PageError) { + pub(crate) fn handle_page_error( + &self, + page_error: PageError, + id: UniqueId, + registry: &ActorRegistry, + ) { self.cached_events .borrow_mut() + .entry(id.clone()) + .or_insert(vec![]) .push(CachedConsoleMessage::PageError(page_error.clone())); - let msg = PageErrorMsg { - from: self.name(), - type_: "pageError".to_owned(), - pageError: page_error, - }; - for stream in &mut *self.streams.borrow_mut() { - stream.write_json_packet(&msg); + if id == self.current_unique_id(registry) { + let msg = PageErrorMsg { + from: self.name(), + type_: "pageError".to_owned(), + pageError: page_error, + }; + for stream in &mut *self.streams_mut(registry) { + stream.write_json_packet(&msg); + } } } - pub(crate) fn handle_console_api(&self, console_message: ConsoleMessage) { + pub(crate) fn handle_console_api( + &self, + console_message: ConsoleMessage, + id: UniqueId, + registry: &ActorRegistry, + ) { let level = match console_message.logLevel { LogLevel::Debug => "debug", LogLevel::Info => "info", @@ -216,6 +277,8 @@ impl ConsoleActor { .to_owned(); self.cached_events .borrow_mut() + .entry(id.clone()) + .or_insert(vec![]) .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI { type_: "ConsoleAPI".to_owned(), level: level.clone(), @@ -226,20 +289,22 @@ impl ConsoleActor { private: false, arguments: vec![console_message.message.clone()], })); - let msg = ConsoleAPICall { - from: self.name(), - type_: "consoleAPICall".to_owned(), - message: ConsoleMsg { - level: level, - timeStamp: precise_time_ns(), - arguments: vec![console_message.message], - filename: console_message.filename, - lineNumber: console_message.lineNumber, - columnNumber: console_message.columnNumber, - }, - }; - for stream in &mut *self.streams.borrow_mut() { - stream.write_json_packet(&msg); + if id == self.current_unique_id(registry) { + let msg = ConsoleAPICall { + from: self.name(), + type_: "consoleAPICall".to_owned(), + message: ConsoleMsg { + level: level, + timeStamp: precise_time_ns(), + arguments: vec![console_message.message], + filename: console_message.filename, + lineNumber: console_message.lineNumber, + columnNumber: console_message.columnNumber, + }, + }; + for stream in &mut *self.streams_mut(registry) { + stream.write_json_packet(&msg); + } } } } @@ -257,6 +322,13 @@ impl Actor for ConsoleActor { stream: &mut TcpStream, ) -> Result<ActorMessageStatus, ()> { Ok(match msg_type { + "clearMessagesCache" => { + self.cached_events + .borrow_mut() + .remove(&self.current_unique_id(registry)); + ActorMessageStatus::Processed + }, + "getCachedMessages" => { let str_types = msg .get("messageTypes") @@ -276,7 +348,13 @@ impl Actor for ConsoleActor { }; } let mut messages = vec![]; - for event in self.cached_events.borrow().iter() { + for event in self + .cached_events + .borrow() + .get(&self.current_unique_id(registry)) + .unwrap_or(&vec![]) + .iter() + { let include = match event { CachedConsoleMessage::PageError(_) if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) => @@ -365,6 +443,13 @@ impl Actor for ConsoleActor { // Emit an eager reply so that the client starts listening // for an async event with the resultID stream.write_json_packet(&early_reply); + + if msg.get("eager").and_then(|v| v.as_bool()).unwrap_or(false) { + // We don't support the side-effect free evaluation that eager evalaution + // really needs. + return Ok(ActorMessageStatus::Processed); + } + let reply = self.evaluateJS(®istry, &msg).unwrap(); let msg = EvaluateJSEvent { from: self.name(), @@ -395,3 +480,29 @@ impl Actor for ConsoleActor { }) } } + +#[derive(Serialize)] +struct ConsoleAPICall { + from: String, + #[serde(rename = "type")] + type_: String, + message: ConsoleMsg, +} + +#[derive(Serialize)] +struct ConsoleMsg { + level: String, + timeStamp: u64, + arguments: Vec<String>, + filename: String, + lineNumber: usize, + columnNumber: usize, +} + +#[derive(Serialize)] +struct PageErrorMsg { + from: String, + #[serde(rename = "type")] + type_: String, + pageError: PageError, +} diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index cd3e5bf323c..d2f0929ac89 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -6,6 +6,7 @@ //! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js). use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actors::browsing_context::BrowsingContextActor; use crate::protocol::JsonPacketStream; use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, GetRootNode}; use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute}; @@ -22,7 +23,7 @@ pub struct InspectorActor { pub pageStyle: RefCell<Option<String>>, pub highlighter: RefCell<Option<String>>, pub script_chan: IpcSender<DevtoolScriptControlMsg>, - pub pipeline: PipelineId, + pub browsing_context: String, } #[derive(Serialize)] @@ -596,13 +597,15 @@ impl Actor for InspectorActor { _msg: &Map<String, Value>, stream: &mut TcpStream, ) -> Result<ActorMessageStatus, ()> { + let browsing_context = registry.find::<BrowsingContextActor>(&self.browsing_context); + let pipeline = browsing_context.active_pipeline.get(); Ok(match msg_type { "getWalker" => { if self.walker.borrow().is_none() { let walker = WalkerActor { name: registry.new_name("walker"), script_chan: self.script_chan.clone(), - pipeline: self.pipeline, + pipeline: pipeline, }; let mut walker_name = self.walker.borrow_mut(); *walker_name = Some(walker.name()); @@ -610,13 +613,10 @@ impl Actor for InspectorActor { } let (tx, rx) = ipc::channel().unwrap(); - self.script_chan - .send(GetRootNode(self.pipeline, tx)) - .unwrap(); + self.script_chan.send(GetRootNode(pipeline, tx)).unwrap(); let root_info = rx.recv().unwrap().ok_or(())?; - let node = - root_info.encode(registry, false, self.script_chan.clone(), self.pipeline); + let node = root_info.encode(registry, false, self.script_chan.clone(), pipeline); let msg = GetWalkerReply { from: self.name(), @@ -634,7 +634,7 @@ impl Actor for InspectorActor { let style = PageStyleActor { name: registry.new_name("pageStyle"), script_chan: self.script_chan.clone(), - pipeline: self.pipeline, + pipeline: pipeline, }; let mut pageStyle = self.pageStyle.borrow_mut(); *pageStyle = Some(style.name()); diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs index da7483ffb34..0010afd3b9f 100644 --- a/components/devtools/actors/root.rs +++ b/components/devtools/actors/root.rs @@ -10,6 +10,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg}; use crate::actors::device::DeviceActor; use crate::actors::performance::PerformanceActor; +use crate::actors::worker::{WorkerActor, WorkerMsg}; use crate::protocol::{ActorDescription, JsonPacketStream}; use serde_json::{Map, Value}; use std::net::TcpStream; @@ -73,11 +74,6 @@ struct ListWorkersReply { } #[derive(Serialize)] -struct WorkerMsg { - id: u32, -} - -#[derive(Serialize)] struct ListServiceWorkerRegistrationsReply { from: String, registrations: Vec<u32>, // TODO: follow actual JSON structure. @@ -110,6 +106,7 @@ struct GetProcessResponse { pub struct RootActor { pub tabs: Vec<String>, + pub workers: Vec<String>, pub performance: String, pub device: String, pub preference: String, @@ -203,7 +200,11 @@ impl Actor for RootActor { "listWorkers" => { let reply = ListWorkersReply { from: self.name(), - workers: vec![], + workers: self + .workers + .iter() + .map(|name| registry.find::<WorkerActor>(name).encodable()) + .collect(), }; stream.write_json_packet(&reply); ActorMessageStatus::Processed diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs index 082d719de20..fd578d0d3dc 100644 --- a/components/devtools/actors/worker.rs +++ b/components/devtools/actors/worker.rs @@ -3,14 +3,49 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use devtools_traits::WorkerId; +use crate::protocol::JsonPacketStream; +use devtools_traits::DevtoolScriptControlMsg::WantsLiveNotifications; +use devtools_traits::{DevtoolScriptControlMsg, WorkerId}; +use ipc_channel::ipc::IpcSender; +use msg::constellation_msg::TEST_PIPELINE_ID; use serde_json::{Map, Value}; +use servo_url::ServoUrl; +use std::cell::RefCell; use std::net::TcpStream; +#[derive(Clone, Copy)] +#[allow(dead_code)] +pub enum WorkerType { + Dedicated = 0, + Shared = 1, + Service = 2, +} + pub struct WorkerActor { pub name: String, pub console: String, + pub thread: String, pub id: WorkerId, + pub url: ServoUrl, + pub type_: WorkerType, + pub script_chan: IpcSender<DevtoolScriptControlMsg>, + pub streams: RefCell<Vec<TcpStream>>, +} + +impl WorkerActor { + pub(crate) fn encodable(&self) -> WorkerMsg { + WorkerMsg { + actor: self.name.clone(), + consoleActor: self.console.clone(), + threadActor: self.thread.clone(), + id: self.id.0.to_string(), + url: self.url.to_string(), + traits: WorkerTraits { + isParentInterceptEnabled: false, + }, + type_: self.type_ as u32, + } + } } impl Actor for WorkerActor { @@ -19,11 +54,94 @@ impl Actor for WorkerActor { } fn handle_message( &self, - _: &ActorRegistry, - _: &str, - _: &Map<String, Value>, - _: &mut TcpStream, + _registry: &ActorRegistry, + msg_type: &str, + _msg: &Map<String, Value>, + stream: &mut TcpStream, ) -> Result<ActorMessageStatus, ()> { - Ok(ActorMessageStatus::Processed) + Ok(match msg_type { + "attach" => { + let msg = AttachedReply { + from: self.name(), + type_: "attached".to_owned(), + url: self.url.as_str().to_owned(), + }; + self.streams.borrow_mut().push(stream.try_clone().unwrap()); + stream.write_json_packet(&msg); + // FIXME: fix messages to not require forging a pipeline for worker messages + self.script_chan + .send(WantsLiveNotifications(TEST_PIPELINE_ID, true)) + .unwrap(); + ActorMessageStatus::Processed + }, + + "connect" => { + let msg = ConnectReply { + from: self.name(), + type_: "connected".to_owned(), + threadActor: self.thread.clone(), + consoleActor: self.console.clone(), + }; + stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + "detach" => { + let msg = DetachedReply { + from: self.name(), + type_: "detached".to_string(), + }; + // FIXME: we should ensure we're removing the correct stream. + self.streams.borrow_mut().pop(); + stream.write_json_packet(&msg); + self.script_chan + .send(WantsLiveNotifications(TEST_PIPELINE_ID, false)) + .unwrap(); + ActorMessageStatus::Processed + }, + + _ => ActorMessageStatus::Ignored, + }) } } + +#[derive(Serialize)] +struct DetachedReply { + from: String, + #[serde(rename = "type")] + type_: String, +} + +#[derive(Serialize)] +struct AttachedReply { + from: String, + #[serde(rename = "type")] + type_: String, + url: String, +} + +#[derive(Serialize)] +struct ConnectReply { + from: String, + #[serde(rename = "type")] + type_: String, + threadActor: String, + consoleActor: String, +} + +#[derive(Serialize)] +struct WorkerTraits { + isParentInterceptEnabled: bool, +} + +#[derive(Serialize)] +pub(crate) struct WorkerMsg { + actor: String, + consoleActor: String, + threadActor: String, + id: String, + url: String, + traits: WorkerTraits, + #[serde(rename = "type")] + type_: u32, +} diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 661d6ac94e1..2227be98549 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -19,31 +19,27 @@ extern crate serde; use crate::actor::{Actor, ActorRegistry}; use crate::actors::browsing_context::BrowsingContextActor; -use crate::actors::console::ConsoleActor; +use crate::actors::console::{ConsoleActor, Root}; use crate::actors::device::DeviceActor; -use crate::actors::emulation::EmulationActor; use crate::actors::framerate::FramerateActor; -use crate::actors::inspector::InspectorActor; use crate::actors::network_event::{EventActor, NetworkEventActor, ResponseStartMsg}; use crate::actors::performance::PerformanceActor; use crate::actors::preference::PreferenceActor; use crate::actors::process::ProcessActor; -use crate::actors::profiler::ProfilerActor; use crate::actors::root::RootActor; -use crate::actors::stylesheets::StyleSheetsActor; use crate::actors::thread::ThreadActor; -use crate::actors::timeline::TimelineActor; -use crate::actors::worker::WorkerActor; +use crate::actors::worker::{WorkerActor, WorkerType}; use crate::protocol::JsonPacketStream; use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::{ChromeToDevtoolsControlMsg, ConsoleMessage, DevtoolsControlMsg}; -use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo, LogLevel, NetworkEvent}; +use devtools_traits::{ + DevtoolScriptControlMsg, DevtoolsPageInfo, LogLevel, NavigationState, NetworkEvent, +}; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult}; use ipc_channel::ipc::{self, IpcSender}; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use std::borrow::ToOwned; -use std::cell::RefCell; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; use std::net::{Shutdown, TcpListener, TcpStream}; @@ -74,30 +70,10 @@ mod actors { } mod protocol; -#[derive(Serialize)] -struct ConsoleAPICall { - from: String, - #[serde(rename = "type")] - type_: String, - message: ConsoleMsg, -} - -#[derive(Serialize)] -struct ConsoleMsg { - level: String, - timeStamp: u64, - arguments: Vec<String>, - filename: String, - lineNumber: usize, - columnNumber: usize, -} - -#[derive(Serialize)] -struct PageErrorMsg { - from: String, - #[serde(rename = "type")] - type_: String, - pageError: PageError, +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +enum UniqueId { + Pipeline(PipelineId), + Worker(WorkerId), } #[derive(Serialize)] @@ -181,6 +157,7 @@ fn run_server( let root = Box::new(RootActor { tabs: vec![], + workers: vec![], device: device.name(), performance: performance.name(), preference: preference.name(), @@ -198,10 +175,11 @@ fn run_server( let mut accepted_connections: Vec<TcpStream> = Vec::new(); - let mut actor_pipelines: HashMap<PipelineId, String> = HashMap::new(); + let mut browsing_contexts: HashMap<BrowsingContextId, String> = HashMap::new(); + let mut pipelines: HashMap<PipelineId, BrowsingContextId> = HashMap::new(); let mut actor_requests: HashMap<String, String> = HashMap::new(); - let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new(); + let mut actor_workers: HashMap<WorkerId, String> = HashMap::new(); /// Process the input from a single devtools client until EOF. fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) { @@ -243,129 +221,139 @@ fn run_server( framerate_actor.add_tick(tick); } + fn handle_navigate( + actors: Arc<Mutex<ActorRegistry>>, + browsing_contexts: &HashMap<BrowsingContextId, String>, + browsing_context: BrowsingContextId, + state: NavigationState, + ) { + let actor_name = browsing_contexts.get(&browsing_context).unwrap(); + actors + .lock() + .unwrap() + .find::<BrowsingContextActor>(actor_name) + .navigate(state); + } + + fn handle_title_changed( + actors: Arc<Mutex<ActorRegistry>>, + pipelines: &HashMap<PipelineId, BrowsingContextId>, + browsing_contexts: &HashMap<BrowsingContextId, String>, + pipeline: PipelineId, + title: String, + ) { + let bc = match pipelines.get(&pipeline) { + Some(bc) => bc, + None => return, + }; + let name = match browsing_contexts.get(&bc) { + Some(name) => name, + None => return, + }; + let actors = actors.lock().unwrap(); + let browsing_context = actors.find::<BrowsingContextActor>(name); + browsing_context.title_changed(pipeline, title); + } + // We need separate actor representations for each script global that exists; // clients can theoretically connect to multiple globals simultaneously. // TODO: move this into the root or target modules? fn handle_new_global( actors: Arc<Mutex<ActorRegistry>>, - ids: (PipelineId, Option<WorkerId>), + ids: (BrowsingContextId, PipelineId, Option<WorkerId>), script_sender: IpcSender<DevtoolScriptControlMsg>, - actor_pipelines: &mut HashMap<PipelineId, String>, - actor_workers: &mut HashMap<(PipelineId, WorkerId), String>, + browsing_contexts: &mut HashMap<BrowsingContextId, String>, + pipelines: &mut HashMap<PipelineId, BrowsingContextId>, + actor_workers: &mut HashMap<WorkerId, String>, page_info: DevtoolsPageInfo, ) { let mut actors = actors.lock().unwrap(); - let (pipeline, worker_id) = ids; - - //TODO: move all this actor creation into a constructor method on BrowsingContextActor - let ( - target, - console, - emulation, - inspector, - timeline, - profiler, - performance, - styleSheets, - thread, - ) = { - let console = ConsoleActor { - name: actors.new_name("console"), - script_chan: script_sender.clone(), - pipeline: pipeline, - streams: RefCell::new(Vec::new()), - cached_events: RefCell::new(Vec::new()), - }; - - let emulation = EmulationActor::new(actors.new_name("emulation")); - - let inspector = InspectorActor { - name: actors.new_name("inspector"), - walker: RefCell::new(None), - pageStyle: RefCell::new(None), - highlighter: RefCell::new(None), - script_chan: script_sender.clone(), - pipeline: pipeline, - }; + let (browsing_context, pipeline, worker_id) = ids; - let timeline = TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender); + let console_name = actors.new_name("console"); - let profiler = ProfilerActor::new(actors.new_name("profiler")); - let performance = PerformanceActor::new(actors.new_name("performance")); + let parent_actor = if let Some(id) = worker_id { + assert!(pipelines.get(&pipeline).is_some()); + assert!(browsing_contexts.get(&browsing_context).is_some()); - // the strange switch between styleSheets and stylesheets is due - // to an inconsistency in devtools. See Bug #1498893 in bugzilla - let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets")); let thread = ThreadActor::new(actors.new_name("context")); + let thread_name = thread.name(); + actors.register(Box::new(thread)); - let DevtoolsPageInfo { title, url } = page_info; - let target = BrowsingContextActor { - name: actors.new_name("target"), - title: String::from(title), - url: url.into_string(), - console: console.name(), - emulation: emulation.name(), - inspector: inspector.name(), - timeline: timeline.name(), - profiler: profiler.name(), - performance: performance.name(), - styleSheets: styleSheets.name(), - thread: thread.name(), + let worker_name = actors.new_name("worker"); + let worker = WorkerActor { + name: worker_name.clone(), + console: console_name.clone(), + thread: thread_name, + id: id, + url: page_info.url.clone(), + type_: WorkerType::Dedicated, + script_chan: script_sender, + streams: Default::default(), }; - let root = actors.find_mut::<RootActor>("root"); - root.tabs.push(target.name.clone()); - - ( - target, - console, - emulation, - inspector, - timeline, - profiler, - performance, - styleSheets, - thread, + root.workers.push(worker.name.clone()); + + actor_workers.insert(id, worker_name.clone()); + actors.register(Box::new(worker)); + + Root::DedicatedWorker(worker_name) + } else { + pipelines.insert(pipeline, browsing_context); + Root::BrowsingContext( + if let Some(actor) = browsing_contexts.get(&browsing_context) { + actor.to_owned() + } else { + let browsing_context_actor = BrowsingContextActor::new( + console_name.clone(), + browsing_context, + page_info, + pipeline, + script_sender, + &mut *actors, + ); + let name = browsing_context_actor.name(); + browsing_contexts.insert(browsing_context, name.clone()); + actors.register(Box::new(browsing_context_actor)); + name + }, ) }; - if let Some(id) = worker_id { - let worker = WorkerActor { - name: actors.new_name("worker"), - console: console.name(), - id: id, - }; - actor_workers.insert((pipeline, id), worker.name.clone()); - actors.register(Box::new(worker)); - } + let console = ConsoleActor { + name: console_name, + cached_events: Default::default(), + root: parent_actor, + }; - actor_pipelines.insert(pipeline, target.name.clone()); - actors.register(Box::new(target)); actors.register(Box::new(console)); - actors.register(Box::new(emulation)); - actors.register(Box::new(inspector)); - actors.register(Box::new(timeline)); - actors.register(Box::new(profiler)); - actors.register(Box::new(performance)); - actors.register(Box::new(styleSheets)); - actors.register(Box::new(thread)); } fn handle_page_error( actors: Arc<Mutex<ActorRegistry>>, id: PipelineId, + worker_id: Option<WorkerId>, page_error: PageError, - actor_pipelines: &HashMap<PipelineId, String>, + browsing_contexts: &HashMap<BrowsingContextId, String>, + actor_workers: &HashMap<WorkerId, String>, + pipelines: &HashMap<PipelineId, BrowsingContextId>, ) { - let console_actor_name = - match find_console_actor(actors.clone(), id, None, &HashMap::new(), actor_pipelines) { - Some(name) => name, - None => return, - }; + let console_actor_name = match find_console_actor( + actors.clone(), + id, + worker_id, + actor_workers, + browsing_contexts, + pipelines, + ) { + Some(name) => name, + None => return, + }; let actors = actors.lock().unwrap(); let console_actor = actors.find::<ConsoleActor>(&console_actor_name); - console_actor.handle_page_error(page_error); + let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker); + console_actor.handle_page_error(page_error, id, &*actors); } fn handle_console_message( @@ -373,37 +361,42 @@ fn run_server( id: PipelineId, worker_id: Option<WorkerId>, console_message: ConsoleMessage, - actor_pipelines: &HashMap<PipelineId, String>, - actor_workers: &HashMap<(PipelineId, WorkerId), String>, + browsing_contexts: &HashMap<BrowsingContextId, String>, + actor_workers: &HashMap<WorkerId, String>, + pipelines: &HashMap<PipelineId, BrowsingContextId>, ) { let console_actor_name = match find_console_actor( actors.clone(), id, worker_id, actor_workers, - actor_pipelines, + browsing_contexts, + pipelines, ) { Some(name) => name, None => return, }; let actors = actors.lock().unwrap(); let console_actor = actors.find::<ConsoleActor>(&console_actor_name); - console_actor.handle_console_api(console_message); + let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker); + console_actor.handle_console_api(console_message, id, &*actors); } fn find_console_actor( actors: Arc<Mutex<ActorRegistry>>, - id: PipelineId, + pipeline: PipelineId, worker_id: Option<WorkerId>, - actor_workers: &HashMap<(PipelineId, WorkerId), String>, - actor_pipelines: &HashMap<PipelineId, String>, + actor_workers: &HashMap<WorkerId, String>, + browsing_contexts: &HashMap<BrowsingContextId, String>, + pipelines: &HashMap<PipelineId, BrowsingContextId>, ) -> Option<String> { let actors = actors.lock().unwrap(); if let Some(worker_id) = worker_id { - let actor_name = (*actor_workers).get(&(id, worker_id))?; + let actor_name = actor_workers.get(&worker_id)?; Some(actors.find::<WorkerActor>(actor_name).console.clone()) } else { - let actor_name = (*actor_pipelines).get(&id)?; + let id = pipelines.get(&pipeline)?; + let actor_name = browsing_contexts.get(id)?; Some( actors .find::<BrowsingContextActor>(actor_name) @@ -416,9 +409,10 @@ fn run_server( fn handle_network_event( actors: Arc<Mutex<ActorRegistry>>, mut connections: Vec<TcpStream>, - actor_pipelines: &HashMap<PipelineId, String>, + browsing_contexts: &HashMap<BrowsingContextId, String>, actor_requests: &mut HashMap<String, String>, - actor_workers: &HashMap<(PipelineId, WorkerId), String>, + actor_workers: &HashMap<WorkerId, String>, + pipelines: &HashMap<PipelineId, BrowsingContextId>, pipeline_id: PipelineId, request_id: String, network_event: NetworkEvent, @@ -428,7 +422,8 @@ fn run_server( pipeline_id, None, actor_workers, - actor_pipelines, + browsing_contexts, + pipelines, ) { Some(name) => name, None => return, @@ -589,6 +584,7 @@ fn run_server( .expect("Thread spawning failed"); while let Ok(msg) = receiver.recv() { + debug!("{:?}", msg); match msg { DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => { let actors = actors.clone(); @@ -602,6 +598,16 @@ fn run_server( actor_name, tick, )) => handle_framerate_tick(actors.clone(), actor_name, tick), + DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::TitleChanged( + pipeline, + title, + )) => handle_title_changed( + actors.clone(), + &pipelines, + &browsing_contexts, + pipeline, + title, + ), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal( ids, script_sender, @@ -610,10 +616,15 @@ fn run_server( actors.clone(), ids, script_sender, - &mut actor_pipelines, + &mut browsing_contexts, + &mut pipelines, &mut actor_workers, pageinfo, ), + DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::Navigate( + browsing_context, + state, + )) => handle_navigate(actors.clone(), &browsing_contexts, browsing_context, state), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI( id, console_message, @@ -623,13 +634,22 @@ fn run_server( id, worker_id, console_message, - &actor_pipelines, + &browsing_contexts, &actor_workers, + &pipelines, ), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError( id, page_error, - )) => handle_page_error(actors.clone(), id, page_error, &actor_pipelines), + )) => handle_page_error( + actors.clone(), + id, + None, + page_error, + &browsing_contexts, + &actor_workers, + &pipelines, + ), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError( id, css_error, @@ -646,8 +666,9 @@ fn run_server( id, None, console_message, - &actor_pipelines, + &browsing_contexts, &actor_workers, + &pipelines, ) }, DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent( @@ -667,9 +688,10 @@ fn run_server( handle_network_event( actors.clone(), connections, - &actor_pipelines, + &browsing_contexts, &mut actor_requests, &actor_workers, + &pipelines, pipeline_id, request_id, network_event, diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 5396d64fade..7196c3a5b32 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -21,7 +21,7 @@ extern crate serde; use http::method::Method; use http::HeaderMap; use ipc_channel::ipc::IpcSender; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use servo_url::ServoUrl; use std::net::TcpStream; use time::{self, Duration, Tm}; @@ -29,7 +29,7 @@ use uuid::Uuid; // Information would be attached to NewGlobal to be received and show in devtools. // Extend these fields if we need more information. -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct DevtoolsPageInfo { pub title: String, pub url: ServoUrl, @@ -65,16 +65,27 @@ pub enum ChromeToDevtoolsControlMsg { NetworkEvent(String, NetworkEvent), } +/// The state of a page navigation. +#[derive(Debug, Deserialize, Serialize)] +pub enum NavigationState { + /// A browsing context is about to navigate to a given URL. + Start(ServoUrl), + /// A browsing context has completed navigating to the provided pipeline. + Stop(PipelineId, DevtoolsPageInfo), +} + #[derive(Debug, Deserialize, Serialize)] /// Events that the devtools server must act upon. pub enum ScriptToDevtoolsControlMsg { /// A new global object was created, associated with a particular pipeline. /// The means of communicating directly with it are provided. NewGlobal( - (PipelineId, Option<WorkerId>), + (BrowsingContextId, PipelineId, Option<WorkerId>), IpcSender<DevtoolScriptControlMsg>, DevtoolsPageInfo, ), + /// The given browsing context is performing a navigation. + Navigate(BrowsingContextId, NavigationState), /// A particular page has invoked the console API. ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>), /// An animation frame with the given timestamp was processed in a script thread. @@ -86,6 +97,9 @@ pub enum ScriptToDevtoolsControlMsg { /// Report a page error for the given pipeline ReportPageError(PipelineId, PageError), + + /// Report a page title change + TitleChanged(PipelineId, String), } /// Serialized JS return values diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 9dc59da1937..79a90f397a7 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -8,157 +8,183 @@ use crate::context::LayoutContext; use crate::display_list::items::OpaqueNode; use crate::flow::{Flow, GetBaseFlow}; use crate::opaque_node::OpaqueNodeMethods; -use crossbeam_channel::Receiver; use fxhash::{FxHashMap, FxHashSet}; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; use script_traits::UntrustedNodeAddress; -use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg}; -use style::animation::{update_style_for_animation, Animation}; +use script_traits::{ + AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg, TransitionEventType, +}; +use servo_arc::Arc; +use style::animation::{ + update_style_for_animation, Animation, ElementAnimationState, PropertyAnimation, +}; use style::dom::TElement; use style::font_metrics::ServoMetricsProvider; use style::selector_parser::RestyleDamage; use style::timer::Timer; -/// Processes any new animations that were discovered after style recalculation. -/// Also expire any old animations that have completed, inserting them into -/// `expired_animations`. -pub fn update_animation_state<E>( +/// Collect newly transitioning nodes, which is used by the script process during +/// forced, synchronous reflows to root DOM nodes for the duration of their transitions. +pub fn collect_newly_transitioning_nodes( + animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>, + mut out: Option<&mut Vec<UntrustedNodeAddress>>, +) { + // This extends the output vector with an iterator that contains a copy of the node + // address for every new animation. This is a bit goofy, but the script thread + // currently stores a rooted node for every property that is transitioning. + if let Some(ref mut out) = out { + out.extend(animation_states.iter().flat_map(|(node, state)| { + let num_transitions = state + .new_animations + .iter() + .filter(|animation| animation.is_transition()) + .count(); + std::iter::repeat(node.to_untrusted_node_address()).take(num_transitions) + })); + } +} + +/// Processes any new animations that were discovered after style recalculation. Also +/// finish any animations that have completed, inserting them into `finished_animations`. +pub fn update_animation_states( constellation_chan: &IpcSender<ConstellationMsg>, script_chan: &IpcSender<ConstellationControlMsg>, - running_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>, - expired_animations: &mut FxHashMap<OpaqueNode, Vec<Animation>>, - mut keys_to_remove: FxHashSet<OpaqueNode>, - mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>, - new_animations_receiver: &Receiver<Animation>, + animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>, + invalid_nodes: FxHashSet<OpaqueNode>, pipeline_id: PipelineId, timer: &Timer, -) where - E: TElement, -{ - let mut new_running_animations = vec![]; - while let Ok(animation) = new_animations_receiver.try_recv() { - let mut should_push = true; - if let Animation::Keyframes(ref node, _, ref name, ref state) = animation { - // If the animation was already present in the list for the - // node, just update its state, else push the new animation to - // run. - if let Some(ref mut animations) = running_animations.get_mut(node) { - // TODO: This being linear is probably not optimal. - for anim in animations.iter_mut() { - if let Animation::Keyframes(_, _, ref anim_name, ref mut anim_state) = *anim { - if *name == *anim_name { - debug!("update_animation_state: Found other animation {}", name); - anim_state.update_from_other(&state, timer); - should_push = false; - break; - } - } - } - } - } - - if should_push { - new_running_animations.push(animation); +) { + let had_running_animations = animation_states + .values() + .any(|state| !state.running_animations.is_empty()); + + // Cancel all animations on any invalid nodes. These entries will later + // be removed from the list of states, because their states will become + // empty. + for node in &invalid_nodes { + if let Some(mut state) = animation_states.remove(node) { + state.cancel_all_animations(); } } - if running_animations.is_empty() && new_running_animations.is_empty() { - // Nothing to do. Return early so we don't flood the compositor with - // `ChangeRunningAnimationsState` messages. - return; + let now = timer.seconds(); + let mut have_running_animations = false; + for (node, animation_state) in animation_states.iter_mut() { + update_animation_state(script_chan, animation_state, pipeline_id, now, *node); + have_running_animations = + have_running_animations || !animation_state.running_animations.is_empty(); } - let now = timer.seconds(); - // Expire old running animations. - // - // TODO: Do not expunge Keyframes animations, since we need that state if - // the animation gets re-triggered. Probably worth splitting in two - // different maps, or at least using a linked list? - for (key, running_animations) in running_animations.iter_mut() { - let mut animations_still_running = vec![]; - for mut running_animation in running_animations.drain(..) { - let still_running = !running_animation.is_expired() && - match running_animation { - Animation::Transition(_, started_at, ref property_animation) => { - now < started_at + (property_animation.duration) - }, - Animation::Keyframes(_, _, _, ref mut state) => { - // This animation is still running, or we need to keep - // iterating. - now < state.started_at + state.duration || state.tick() - }, - }; - - debug!( - "update_animation_state({:?}): {:?}", - still_running, running_animation - ); + // Remove empty states from our collection of states in order to free + // up space as soon as we are no longer tracking any animations for + // a node. + animation_states.retain(|_, state| !state.is_empty()); - if still_running { - animations_still_running.push(running_animation); - continue; - } + let present = match (had_running_animations, have_running_animations) { + (true, false) => AnimationState::NoAnimationsPresent, + (false, true) => AnimationState::AnimationsPresent, + _ => return, + }; + constellation_chan + .send(ConstellationMsg::ChangeRunningAnimationsState( + pipeline_id, + present, + )) + .unwrap(); +} - if let Animation::Transition(node, _, ref property_animation) = running_animation { - script_chan - .send(ConstellationControlMsg::TransitionEnd( - node.to_untrusted_node_address(), - property_animation.property_name().into(), - property_animation.duration, - )) - .unwrap(); - } +pub fn update_animation_state( + script_channel: &IpcSender<ConstellationControlMsg>, + animation_state: &mut ElementAnimationState, + pipeline_id: PipelineId, + now: f64, + node: OpaqueNode, +) { + let send_transition_event = |property_animation: &PropertyAnimation, event_type| { + script_channel + .send(ConstellationControlMsg::TransitionEvent { + pipeline_id, + event_type, + node: node.to_untrusted_node_address(), + property_name: property_animation.property_name().into(), + elapsed_time: property_animation.duration, + }) + .unwrap() + }; - debug!("expiring animation for {:?}", running_animation); - expired_animations - .entry(*key) - .or_insert_with(Vec::new) - .push(running_animation); - } + handle_cancelled_animations(animation_state, send_transition_event); + handle_running_animations(animation_state, now, send_transition_event); + handle_new_animations(animation_state, send_transition_event); +} + +/// Walk through the list of running animations and remove all of the ones that +/// have ended. +pub fn handle_running_animations( + animation_state: &mut ElementAnimationState, + now: f64, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { + let mut running_animations = + std::mem::replace(&mut animation_state.running_animations, Vec::new()); + for mut running_animation in running_animations.drain(..) { + let still_running = !running_animation.is_expired() && + match running_animation { + Animation::Transition(_, started_at, ref property_animation) => { + now < started_at + (property_animation.duration) + }, + Animation::Keyframes(_, _, _, ref mut state) => { + // This animation is still running, or we need to keep + // iterating. + now < state.started_at + state.duration || state.tick() + }, + }; - if animations_still_running.is_empty() { - keys_to_remove.insert(*key); + // If the animation is still running, add it back to the list of running animations. + if still_running { + animation_state.running_animations.push(running_animation); } else { - *running_animations = animations_still_running + debug!("Finishing transition: {:?}", running_animation); + if let Animation::Transition(_, _, ref property_animation) = running_animation { + send_transition_event(property_animation, TransitionEventType::TransitionEnd); + } + animation_state.finished_animations.push(running_animation); } } +} - for key in keys_to_remove { - running_animations.remove(&key).unwrap(); +/// Send events for cancelled animations. Currently this only handles cancelled +/// transitions, but eventually this should handle cancelled CSS animations as +/// well. +pub fn handle_cancelled_animations( + animation_state: &mut ElementAnimationState, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { + for animation in animation_state.cancelled_animations.drain(..) { + match animation { + Animation::Transition(_, _, ref property_animation) => { + send_transition_event(property_animation, TransitionEventType::TransitionCancel) + }, + Animation::Keyframes(..) => { + warn!("Got unexpected animation in finished transitions list.") + }, + } } +} - // Add new running animations. - for new_running_animation in new_running_animations { - if new_running_animation.is_transition() { - match newly_transitioning_nodes { - Some(ref mut nodes) => { - nodes.push(new_running_animation.node().to_untrusted_node_address()); - }, - None => { - warn!("New transition encountered from compositor-initiated layout."); - }, - } +pub fn handle_new_animations( + animation_state: &mut ElementAnimationState, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { + for animation in animation_state.new_animations.drain(..) { + match animation { + Animation::Transition(_, _, ref property_animation) => { + send_transition_event(property_animation, TransitionEventType::TransitionRun) + }, + Animation::Keyframes(..) => {}, } - - running_animations - .entry(*new_running_animation.node()) - .or_insert_with(Vec::new) - .push(new_running_animation) + animation_state.running_animations.push(animation); } - - let animation_state = if running_animations.is_empty() { - AnimationState::NoAnimationsPresent - } else { - AnimationState::AnimationsPresent - }; - - constellation_chan - .send(ConstellationMsg::ChangeRunningAnimationsState( - pipeline_id, - animation_state, - )) - .unwrap(); } /// Recalculates style for a set of animations. This does *not* run with the DOM @@ -167,46 +193,48 @@ pub fn update_animation_state<E>( pub fn recalc_style_for_animations<E>( context: &LayoutContext, flow: &mut dyn Flow, - animations: &FxHashMap<OpaqueNode, Vec<Animation>>, + animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>, ) -> FxHashSet<OpaqueNode> where E: TElement, { - let mut invalid_nodes = animations.keys().cloned().collect(); - do_recalc_style_for_animations::<E>(context, flow, animations, &mut invalid_nodes); + let mut invalid_nodes = animation_states.keys().cloned().collect(); + do_recalc_style_for_animations::<E>(context, flow, animation_states, &mut invalid_nodes); invalid_nodes } fn do_recalc_style_for_animations<E>( context: &LayoutContext, flow: &mut dyn Flow, - animations: &FxHashMap<OpaqueNode, Vec<Animation>>, + animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>, invalid_nodes: &mut FxHashSet<OpaqueNode>, ) where E: TElement, { let mut damage = RestyleDamage::empty(); flow.mutate_fragments(&mut |fragment| { - if let Some(ref animations) = animations.get(&fragment.node) { - invalid_nodes.remove(&fragment.node); - for animation in animations.iter() { - let old_style = fragment.style.clone(); - update_style_for_animation::<E>( - &context.style_context, - animation, - &mut fragment.style, - &ServoMetricsProvider, - ); - let difference = - RestyleDamage::compute_style_difference(&old_style, &fragment.style); - damage |= difference.damage; - } + let animations = match animation_states.get(&fragment.node) { + Some(state) => &state.running_animations, + None => return, + }; + + invalid_nodes.remove(&fragment.node); + for animation in animations.iter() { + let old_style = fragment.style.clone(); + update_style_for_animation::<E>( + &context.style_context, + animation, + Arc::make_mut(&mut fragment.style), + &ServoMetricsProvider, + ); + let difference = RestyleDamage::compute_style_difference(&old_style, &fragment.style); + damage |= difference.damage; } }); let base = flow.mut_base(); base.restyle_damage.insert(damage); for kid in base.children.iter_mut() { - do_recalc_style_for_animations::<E>(context, kid, animations, invalid_nodes) + do_recalc_style_for_animations::<E>(context, kid, animation_states, invalid_nodes) } } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 2338fba1f3a..4a346315e8a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -25,7 +25,7 @@ mod dom_wrapper; use crate::dom_wrapper::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use app_units::Au; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::{Receiver, Sender}; use embedder_traits::resources::{self, Resource}; use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D}; use fnv::FnvHashMap; @@ -99,9 +99,9 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; use std::thread; use std::time::Duration; -use style::animation::Animation; +use style::animation::ElementAnimationState; +use style::context::SharedStyleContext; use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters}; -use style::context::{SharedStyleContext, ThreadLocalStyleContextCreationInfo}; use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode}; use style::driver; use style::error_reporting::RustLogReporter; @@ -185,13 +185,6 @@ pub struct LayoutThread { /// This can be used to easily check for invalid stale data. generation: Cell<u32>, - /// A channel on which new animations that have been triggered by style recalculation can be - /// sent. - new_animations_sender: Sender<Animation>, - - /// Receives newly-discovered animations. - new_animations_receiver: Receiver<Animation>, - /// The number of Web fonts that have been requested but not yet loaded. outstanding_web_fonts: Arc<AtomicUsize>, @@ -201,11 +194,8 @@ pub struct LayoutThread { /// The document-specific shared lock used for author-origin stylesheets document_shared_lock: Option<SharedRwLock>, - /// The list of currently-running animations. - running_animations: ServoArc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>, - - /// The list of animations that have expired since the last style recalculation. - expired_animations: ServoArc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>, + /// The animation state for all of our nodes. + animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationState>>>, /// A counter for epoch messages epoch: Cell<Epoch>, @@ -538,9 +528,6 @@ impl LayoutThread { window_size.device_pixel_ratio, ); - // Create the channel on which new animations can be sent. - let (new_animations_sender, new_animations_receiver) = unbounded(); - // Proxy IPC messages from the pipeline to the layout thread. let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(pipeline_port); @@ -569,13 +556,10 @@ impl LayoutThread { font_cache_sender: ipc_font_cache_sender, parallel_flag: true, generation: Cell::new(0), - new_animations_sender: new_animations_sender, - new_animations_receiver: new_animations_receiver, outstanding_web_fonts: Arc::new(AtomicUsize::new(0)), root_flow: RefCell::new(None), document_shared_lock: None, - running_animations: ServoArc::new(RwLock::new(Default::default())), - expired_animations: ServoArc::new(RwLock::new(Default::default())), + animation_states: ServoArc::new(RwLock::new(Default::default())), // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR epoch: Cell::new(Epoch(1)), viewport_size: Size2D::new(Au(0), Au(0)), @@ -642,9 +626,6 @@ impl LayoutThread { snapshot_map: &'a SnapshotMap, origin: ImmutableOrigin, ) -> LayoutContext<'a> { - let thread_local_style_context_creation_data = - ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone()); - LayoutContext { id: self.id, origin, @@ -653,10 +634,8 @@ impl LayoutThread { options: GLOBAL_STYLE_DATA.options.clone(), guards, visited_styles_enabled: false, - running_animations: self.running_animations.clone(), - expired_animations: self.expired_animations.clone(), + animation_states: self.animation_states.clone(), registered_speculative_painters: &self.registered_painters, - local_context_creation_data: Mutex::new(thread_local_style_context_creation_data), timer: self.timer.clone(), traversal_flags: TraversalFlags::empty(), snapshot_map: snapshot_map, @@ -877,7 +856,13 @@ impl LayoutThread { self.paint_time_metrics.set_navigation_start(time); }, Msg::GetRunningAnimations(sender) => { - let _ = sender.send(self.running_animations.read().len()); + let running_animation_count = self + .animation_states + .read() + .values() + .map(|state| state.running_animations.len()) + .sum(); + let _ = sender.send(running_animation_count); }, } @@ -1732,7 +1717,7 @@ impl LayoutThread { let invalid_nodes = { // Perform an abbreviated style recalc that operates without access to the DOM. - let animations = self.running_animations.read(); + let animation_states = self.animation_states.read(); profile( profile_time::ProfilerCategory::LayoutStyleRecalc, self.profiler_metadata(), @@ -1741,7 +1726,7 @@ impl LayoutThread { animation::recalc_style_for_animations::<ServoLayoutElement>( &layout_context, FlowRef::deref_mut(&mut root_flow), - &animations, + &animation_states, ) }, ) @@ -1777,15 +1762,18 @@ impl LayoutThread { .map(|nodes| nodes.lock().unwrap()); let newly_transitioning_nodes = newly_transitioning_nodes.as_mut().map(|nodes| &mut **nodes); - // Kick off animations if any were triggered, expire completed ones. - animation::update_animation_state::<ServoLayoutElement>( + let mut animation_states = self.animation_states.write(); + + animation::collect_newly_transitioning_nodes( + &animation_states, + newly_transitioning_nodes, + ); + + animation::update_animation_states( &self.constellation_chan, &self.script_chan, - &mut *self.running_animations.write(), - &mut *self.expired_animations.write(), + &mut *animation_states, invalid_nodes, - newly_transitioning_nodes, - &self.new_animations_receiver, self.id, &self.timer, ); diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index fc7ae221e03..1a0d99cdb27 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -23,7 +23,7 @@ mod dom_wrapper; use crate::dom_wrapper::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use app_units::Au; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::{Receiver, Sender}; use embedder_traits::resources::{self, Resource}; use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D}; use fnv::FnvHashMap; @@ -81,9 +81,9 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; use std::thread; use std::time::Duration; -use style::animation::Animation; -use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters}; -use style::context::{SharedStyleContext, ThreadLocalStyleContextCreationInfo}; +use style::context::{ + QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext, +}; use style::dom::{TDocument, TElement, TNode}; use style::driver; use style::error_reporting::RustLogReporter; @@ -159,13 +159,6 @@ pub struct LayoutThread { /// This can be used to easily check for invalid stale data. generation: Cell<u32>, - /// A channel on which new animations that have been triggered by style recalculation can be - /// sent. - new_animations_sender: Sender<Animation>, - - /// Receives newly-discovered animations. - _new_animations_receiver: Receiver<Animation>, - /// The number of Web fonts that have been requested but not yet loaded. outstanding_web_fonts: Arc<AtomicUsize>, @@ -499,9 +492,6 @@ impl LayoutThread { window_size.device_pixel_ratio, ); - // Create the channel on which new animations can be sent. - let (new_animations_sender, new_animations_receiver) = unbounded(); - // Proxy IPC messages from the pipeline to the layout thread. let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(pipeline_port); @@ -528,8 +518,6 @@ impl LayoutThread { font_cache_receiver: font_cache_receiver, font_cache_sender: ipc_font_cache_sender, generation: Cell::new(0), - new_animations_sender: new_animations_sender, - _new_animations_receiver: new_animations_receiver, outstanding_web_fonts: Arc::new(AtomicUsize::new(0)), box_tree_root: Default::default(), fragment_tree_root: Default::default(), @@ -596,9 +584,6 @@ impl LayoutThread { snapshot_map: &'a SnapshotMap, origin: ImmutableOrigin, ) -> LayoutContext<'a> { - let thread_local_style_context_creation_data = - ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone()); - LayoutContext { id: self.id, origin, @@ -607,10 +592,8 @@ impl LayoutThread { options: GLOBAL_STYLE_DATA.options.clone(), guards, visited_styles_enabled: false, - running_animations: Default::default(), - expired_animations: Default::default(), + animation_states: Default::default(), registered_speculative_painters: &self.registered_painters, - local_context_creation_data: Mutex::new(thread_local_style_context_creation_data), timer: self.timer.clone(), traversal_flags: TraversalFlags::empty(), snapshot_map: snapshot_map, diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index d1248453c10..64f40d55689 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -18,19 +18,19 @@ doctest = false base64 = "0.10.1" brotli = "3" bytes = "0.4" -content-security-policy = {version = "0.4.0", features = ["serde"]} -cookie_rs = {package = "cookie", version = "0.11"} +content-security-policy = { version = "0.4.0", features = ["serde"] } +cookie_rs = { package = "cookie", version = "0.11" } crossbeam-channel = "0.4" data-url = "0.1.0" -devtools_traits = {path = "../devtools_traits"} +devtools_traits = { path = "../devtools_traits" } embedder_traits = { path = "../embedder_traits" } flate2 = "1" futures = "0.1" headers = "0.2" http = "0.1" hyper = "0.12" -hyper_serde = "0.11" hyper-openssl = "0.7" +hyper_serde = "0.11" immeta = "0.4" ipc-channel = "0.14" lazy_static = "1" @@ -39,31 +39,31 @@ log = "0.4" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = "0.1" mime = "0.3" -mime_guess = "2.0.0-alpha.6" -msg = {path = "../msg"} -net_traits = {path = "../net_traits"} +mime_guess = "2.0.0" +msg = { path = "../msg" } +net_traits = { path = "../net_traits" } openssl = "0.10" openssl-sys = "0.9" percent-encoding = "2.0" -pixels = {path = "../pixels"} -profile_traits = {path = "../profile_traits"} +pixels = { path = "../pixels" } +profile_traits = { path = "../profile_traits" } rayon = "1" serde = "1.0" serde_json = "1.0" -servo_allocator = {path = "../allocator"} -servo_arc = {path = "../servo_arc"} -servo_config = {path = "../config"} -servo_url = {path = "../url"} -tokio = "0.1" +servo_allocator = { path = "../allocator" } +servo_arc = { path = "../servo_arc" } +servo_config = { path = "../config" } +servo_url = { path = "../url" } time = "0.1.17" +tokio = "0.1" url = "2.0" -uuid = {version = "0.8", features = ["v4"]} -webrender_api = {git = "https://github.com/servo/webrender"} +uuid = { version = "0.8", features = ["v4"] } +webrender_api = { git = "https://github.com/servo/webrender" } ws = { version = "0.9", features = ["ssl"] } [dev-dependencies] -std_test_override = { path = "../std_test_override" } futures = "0.1" +std_test_override = { path = "../std_test_override" } tokio-openssl = "0.3" [[test]] diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 2ed136079b6..f4d8808c869 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -30,25 +30,25 @@ phf_shared = "0.8" serde_json = "1.0" [dependencies] -accountable-refcell = {version = "0.2.0", optional = true} +accountable-refcell = { version = "0.2.0", optional = true } app_units = "0.7" -backtrace = {version = "0.3", optional = true} +backtrace = { version = "0.3", optional = true } base64 = "0.10.1" bincode = "1" bitflags = "1.0" -bluetooth_traits = {path = "../bluetooth_traits"} -canvas_traits = {path = "../canvas_traits"} +bluetooth_traits = { path = "../bluetooth_traits" } +canvas_traits = { path = "../canvas_traits" } caseless = "0.2" -content-security-policy = {version = "0.4.0", features = ["serde"]} -cookie = "0.11" chrono = "0.4" +content-security-policy = { version = "0.4.0", features = ["serde"] } +cookie = "0.11" crossbeam-channel = "0.4" cssparser = "0.27" -deny_public_fields = {path = "../deny_public_fields"} -devtools_traits = {path = "../devtools_traits"} -dom_struct = {path = "../dom_struct"} -domobject_derive = {path = "../domobject_derive"} -embedder_traits = {path = "../embedder_traits"} +deny_public_fields = { path = "../deny_public_fields" } +devtools_traits = { path = "../devtools_traits" } +dom_struct = { path = "../dom_struct" } +domobject_derive = { path = "../domobject_derive" } +embedder_traits = { path = "../embedder_traits" } encoding_rs = "0.8" enum-iterator = "0.3" euclid = "0.20" @@ -62,61 +62,61 @@ image = "0.23" indexmap = "1.0.2" ipc-channel = "0.14" itertools = "0.8" -jstraceable_derive = {path = "../jstraceable_derive"} -js = {package = "mozjs", git = "https://github.com/servo/rust-mozjs"} +js = { package = "mozjs", git = "https://github.com/servo/rust-mozjs" } +jstraceable_derive = { path = "../jstraceable_derive" } keyboard-types = "0.4.4" lazy_static = "1" libc = "0.2" log = "0.4" malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = "0.1" -media = {path = "../media"} -metrics = {path = "../metrics"} -mitochondria = "1.1.2" +media = { path = "../media" } +metrics = { path = "../metrics" } mime = "0.3.13" -mime_guess = "2.0.0-alpha.6" -msg = {path = "../msg"} -net_traits = {path = "../net_traits"} +mime_guess = "2.0.0" +mitochondria = "1.1.2" +msg = { path = "../msg" } +net_traits = { path = "../net_traits" } num-traits = "0.2" parking_lot = "0.9" percent-encoding = "2.0" phf = "0.8" -pixels = {path = "../pixels"} -profile_traits = {path = "../profile_traits"} +pixels = { path = "../pixels" } +profile_traits = { path = "../profile_traits" } ref_filter_map = "1.0.1" ref_slice = "1.0" regex = "1.1" -script_layout_interface = {path = "../script_layout_interface"} -script_plugins = {path = "../script_plugins"} -script_traits = {path = "../script_traits"} +script_layout_interface = { path = "../script_layout_interface" } +script_plugins = { path = "../script_plugins" } +script_traits = { path = "../script_traits" } selectors = { path = "../selectors" } -serde = {version = "1", features = ["derive"]} +serde = { version = "1", features = ["derive"] } serde_bytes = "0.11" -servo_allocator = {path = "../allocator"} -servo_arc = {path = "../servo_arc"} -servo_atoms = {path = "../atoms"} -servo_config = {path = "../config"} -servo_geometry = {path = "../geometry" } -servo-media = {git = "https://github.com/servo/media"} -servo_rand = {path = "../rand"} -servo_url = {path = "../url"} -sparkle = "0.1" +servo-media = { git = "https://github.com/servo/media" } +servo_allocator = { path = "../allocator" } +servo_arc = { path = "../servo_arc" } +servo_atoms = { path = "../atoms" } +servo_config = { path = "../config" } +servo_geometry = { path = "../geometry" } +servo_rand = { path = "../rand" } +servo_url = { path = "../url" } smallvec = { version = "0.6", features = ["std", "union"] } -style = {path = "../style", features = ["servo"]} -style_traits = {path = "../style_traits"} +sparkle = "0.1" +style = { path = "../style", features = ["servo"] } +style_traits = { path = "../style_traits" } swapper = "0.1" -tendril = {version = "0.4.1", features = ["encoding_rs"]} +tendril = { version = "0.4.1", features = ["encoding_rs"] } time = "0.1.12" unicode-bidi = "0.3.4" unicode-segmentation = "1.1.0" url = "2.0" utf-8 = "0.7" -uuid = {version = "0.8", features = ["v4", "serde"]} -xml5ever = "0.16" +uuid = { version = "0.8", features = ["v4", "serde"] } webdriver = "0.40" -webgpu = {path = "../webgpu"} -webrender_api = {git = "https://github.com/servo/webrender"} -webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]} +webgpu = { path = "../webgpu" } +webrender_api = { git = "https://github.com/servo/webrender" } +webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] } +xml5ever = "0.16" [target.'cfg(not(target_os = "ios"))'.dependencies] mozangle = "0.2" diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 49d855ff20f..2aad7d101c7 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -34,7 +34,7 @@ pub fn handle_evaluate_js(global: &GlobalScope, eval: String, reply: IpcSender<E let cx = global.get_cx(); let _ac = enter_realm(global); rooted!(in(*cx) let mut rval = UndefinedValue()); - global.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); + global.evaluate_script_on_global_with_result(&eval, "<eval>", rval.handle_mut(), 1); if rval.is_undefined() { EvaluateJSReply::VoidValue diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 97fffe52853..c2543cc984b 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -157,8 +157,8 @@ use webgpu::{ WebGPUPipelineLayout, WebGPUQueue, WebGPUShaderModule, }; use webrender_api::{DocumentId, ImageKey}; -use webxr_api::Ray; use webxr_api::SwapChainId as WebXRSwapChainId; +use webxr_api::{Finger, Hand, Ray}; unsafe_no_jsmanaged_fields!(Tm); @@ -554,6 +554,7 @@ unsafe_no_jsmanaged_fields!( webxr_api::Frame, webxr_api::InputSource, webxr_api::InputId, + webxr_api::Joint, webxr_api::HitTestId, webxr_api::HitTestResult ); @@ -881,6 +882,58 @@ where } } +unsafe impl<J> JSTraceable for Hand<J> +where + J: JSTraceable, +{ + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + // exhaustive match so we don't miss new fields + let Hand { + ref wrist, + ref thumb_metacarpal, + ref thumb_phalanx_proximal, + ref thumb_phalanx_distal, + ref thumb_phalanx_tip, + ref index, + ref middle, + ref ring, + ref little, + } = *self; + wrist.trace(trc); + thumb_metacarpal.trace(trc); + thumb_phalanx_proximal.trace(trc); + thumb_phalanx_distal.trace(trc); + thumb_phalanx_tip.trace(trc); + index.trace(trc); + middle.trace(trc); + ring.trace(trc); + little.trace(trc); + } +} + +unsafe impl<J> JSTraceable for Finger<J> +where + J: JSTraceable, +{ + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + // exhaustive match so we don't miss new fields + let Finger { + ref metacarpal, + ref phalanx_proximal, + ref phalanx_intermediate, + ref phalanx_distal, + ref phalanx_tip, + } = *self; + metacarpal.trace(trc); + phalanx_proximal.trace(trc); + phalanx_intermediate.trace(trc); + phalanx_distal.trace(trc); + phalanx_tip.trace(trc); + } +} + /// Holds a set of JSTraceables that need to be rooted struct RootedTraceableSet { set: Vec<*const dyn JSTraceable>, diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 22caa2993bd..cf6eb1668f8 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -7,15 +7,24 @@ use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; use crate::dom::workerglobalscope::WorkerGlobalScope; use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg}; +use js::rust::describe_scripted_caller; use std::io; // https://developer.mozilla.org/en-US/docs/Web/API/Console pub struct Console(()); impl Console { + #[allow(unsafe_code)] fn send_to_devtools(global: &GlobalScope, level: LogLevel, message: DOMString) { if let Some(chan) = global.devtools_chan() { - let console_message = prepare_message(level, message); + let caller = unsafe { describe_scripted_caller(*global.get_cx()) }.unwrap_or_default(); + let console_message = ConsoleMessage { + message: String::from(message), + logLevel: level, + filename: caller.filename, + lineNumber: caller.line as usize, + columnNumber: caller.col as usize, + }; let worker_id = global .downcast::<WorkerGlobalScope>() .map(|worker| worker.get_worker_id()); @@ -128,14 +137,3 @@ impl Console { }) } } - -fn prepare_message(log_level: LogLevel, message: DOMString) -> ConsoleMessage { - // TODO: Sending fake values for filename, lineNumber and columnNumber in LogMessage; adjust later - ConsoleMessage { - message: String::from(message), - logLevel: log_level, - filename: "test".to_owned(), - lineNumber: 1, - columnNumber: 1, - } -} diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 150f7920fff..a4970afcca4 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -43,7 +43,7 @@ use js::jsapi::JS_AddInterruptCallback; use js::jsapi::{Heap, JSContext, JSObject}; use js::jsval::UndefinedValue; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; -use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId}; +use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId}; use net_traits::image_cache::ImageCache; use net_traits::request::{CredentialsMode, Destination, ParserMetadata}; use net_traits::request::{Referrer, RequestBuilder, RequestMode}; @@ -180,6 +180,7 @@ pub struct DedicatedWorkerGlobalScope { parent_sender: Box<dyn ScriptChan + Send>, #[ignore_malloc_size_of = "Arc"] image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, } impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope { @@ -221,6 +222,7 @@ impl DedicatedWorkerGlobalScope { receiver: Receiver<DedicatedWorkerScriptMsg>, closing: Arc<AtomicBool>, image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, ) -> DedicatedWorkerGlobalScope { DedicatedWorkerGlobalScope { workerglobalscope: WorkerGlobalScope::new_inherited( @@ -237,6 +239,7 @@ impl DedicatedWorkerGlobalScope { parent_sender: parent_sender, worker: DomRefCell::new(None), image_cache: image_cache, + browsing_context, } } @@ -253,6 +256,7 @@ impl DedicatedWorkerGlobalScope { receiver: Receiver<DedicatedWorkerScriptMsg>, closing: Arc<AtomicBool>, image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, ) -> DomRoot<DedicatedWorkerGlobalScope> { let cx = runtime.cx(); let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited( @@ -267,6 +271,7 @@ impl DedicatedWorkerGlobalScope { receiver, closing, image_cache, + browsing_context, )); unsafe { DedicatedWorkerGlobalScopeBinding::Wrap(SafeJSContext::from_ptr(cx), scope) } } @@ -286,6 +291,7 @@ impl DedicatedWorkerGlobalScope { worker_type: WorkerType, closing: Arc<AtomicBool>, image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, ) { let serialized_worker_url = worker_url.to_string(); let name = format!("WebWorker for {}", serialized_worker_url); @@ -354,6 +360,7 @@ impl DedicatedWorkerGlobalScope { receiver, closing, image_cache, + browsing_context, ); // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter // registration (#6631), so we instead use a random number and cross our fingers. @@ -467,6 +474,7 @@ impl DedicatedWorkerGlobalScope { } fn handle_mixed_message(&self, msg: MixedMessage) { + // FIXME(#26324): `self.worker` is None in devtools messages. match msg { MixedMessage::FromDevtools(msg) => match msg { DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => { @@ -551,6 +559,10 @@ impl DedicatedWorkerGlobalScope { .expect("Sending to parent failed"); Ok(()) } + + pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> { + self.browsing_context + } } #[allow(unsafe_code)] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 2d0f2ce1c26..3606ffc7c9d 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -934,6 +934,14 @@ impl Document { pub fn title_changed(&self) { if self.browsing_context().is_some() { self.send_title_to_embedder(); + let global = self.window.upcast::<GlobalScope>(); + if let Some(ref chan) = global.devtools_chan() { + let title = String::from(self.Title()); + let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged( + global.pipeline_id(), + title, + )); + } } } diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index aba0179a59b..3a0e7490fc9 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -647,6 +647,7 @@ fn invoke( atom!("animationiteration") => Some(atom!("webkitAnimationIteration")), atom!("animationstart") => Some(atom!("webkitAnimationStart")), atom!("transitionend") => Some(atom!("webkitTransitionEnd")), + atom!("transitionrun") => Some(atom!("webkitTransitionRun")), _ => None, } { let original_type = event.type_(); diff --git a/components/script/dom/fakexrdevice.rs b/components/script/dom/fakexrdevice.rs index 5b1ca75fb3d..811a7e01b42 100644 --- a/components/script/dom/fakexrdevice.rs +++ b/components/script/dom/fakexrdevice.rs @@ -269,6 +269,7 @@ impl FakeXRDeviceMethods for FakeXRDevice { id, supports_grip: true, profiles, + hand_support: None, }; let init = MockInputInit { diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 695cdbe6a49..4c4d86f1642 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -568,6 +568,27 @@ impl HTMLElementMethods for HTMLElement { }, ); } + + // https://html.spec.whatwg.org/multipage/#dom-contenteditable + fn ContentEditable(&self) -> DOMString { + // TODO: https://github.com/servo/servo/issues/12776 + self.upcast::<Element>() + .get_attribute(&ns!(), &local_name!("contenteditable")) + .map(|attr| DOMString::from(&**attr.value())) + .unwrap_or_else(|| DOMString::from("inherit")) + } + + // https://html.spec.whatwg.org/multipage/#dom-contenteditable + fn SetContentEditable(&self, _: DOMString) { + // TODO: https://github.com/servo/servo/issues/12776 + warn!("The contentEditable attribute is not implemented yet"); + } + + // https://html.spec.whatwg.org/multipage/#dom-contenteditable + fn IsContentEditable(&self) -> bool { + // TODO: https://github.com/servo/servo/issues/12776 + false + } } fn append_text_node_to_fragment(document: &Document, fragment: &DocumentFragment, text: String) { diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index aac4cc30b97..e57ecf6cd39 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -9,6 +9,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::USVString; +use crate::dom::document::Document; use crate::dom::globalscope::GlobalScope; use crate::dom::urlhelper::UrlHelper; use crate::dom::window::Window; @@ -62,17 +63,10 @@ impl Location { self.window.get_url() } - fn set_url_component(&self, value: USVString, setter: fn(&mut ServoUrl, USVString)) { - let mut url = self.window.get_url(); - let referrer = Referrer::ReferrerUrl(url.clone()); - setter(&mut url, value); - self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); - } - fn check_same_origin_domain(&self) -> ErrorResult { - let entry_document = GlobalScope::entry().as_window().Document(); let this_document = self.window.Document(); - if entry_document + if self + .entry_document() .origin() .same_origin_domain(this_document.origin()) { @@ -82,6 +76,10 @@ impl Location { } } + fn entry_document(&self) -> DomRoot<Document> { + GlobalScope::entry().as_window().Document() + } + // https://html.spec.whatwg.org/multipage/#dom-location-reload pub fn reload_without_origin_check(&self) { let url = self.get_url(); @@ -98,17 +96,24 @@ impl Location { impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-assign fn Assign(&self, url: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - // TODO: per spec, we should use the _API base URL_ specified by the - // _entry settings object_. - let base_url = self.window.get_url(); - if let Ok(url) = base_url.join(&url.0) { - let referrer = Referrer::ReferrerUrl(base_url.clone()); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not same + // origin-domain with the entry settings object's origin, then throw a + // "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Parse url relative to the entry settings object. If that failed, + // throw a "SyntaxError" DOMException. + let base_url = self.entry_document().url(); + let url = match base_url.join(&url.0) { + Ok(url) => url, + Err(_) => return Err(Error::Syntax), + }; + // Step 4: Location-object navigate to the resulting URL record. + let referrer = Referrer::ReferrerUrl(self.get_url()); self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); - Ok(()) - } else { - Err(Error::Syntax) } + Ok(()) } // https://html.spec.whatwg.org/multipage/#dom-location-reload @@ -122,17 +127,21 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-replace fn Replace(&self, url: USVString) -> ErrorResult { - // Note: no call to self.check_same_origin_domain() - // TODO: per spec, we should use the _API base URL_ specified by the - // _entry settings object_. - let base_url = self.window.get_url(); - if let Ok(url) = base_url.join(&url.0) { - let referrer = Referrer::ReferrerUrl(base_url.clone()); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: Parse url relative to the entry settings object. If that failed, + // throw a "SyntaxError" DOMException. + let base_url = self.entry_document().url(); + let url = match base_url.join(&url.0) { + Ok(url) => url, + Err(_) => return Err(Error::Syntax), + }; + // Step 3: Location-object navigate to the resulting URL record with + // the replacement flag set. + let referrer = Referrer::ReferrerUrl(self.get_url()); self.navigate(url, referrer, HistoryEntryReplacement::Enabled, false); - Ok(()) - } else { - Err(Error::Syntax) } + Ok(()) } // https://html.spec.whatwg.org/multipage/#dom-location-hash @@ -142,12 +151,28 @@ impl LocationMethods for Location { } // https://html.spec.whatwg.org/multipage/#dom-location-hash - fn SetHash(&self, mut value: USVString) -> ErrorResult { - if value.0.is_empty() { - value = USVString("#".to_owned()); + fn SetHash(&self, value: USVString) -> ErrorResult { + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: Let input be the given value with a single leading "#" removed, if any. + // Step 5: Set copyURL's fragment to the empty string. + // Step 6: Basic URL parse input, with copyURL as url and fragment state as + // state override. + copy_url.as_mut_url().set_fragment(match value.0.as_str() { + "" => Some("#"), + _ if value.0.starts_with('#') => Some(&value.0[1..]), + _ => Some(&value.0), + }); + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); } - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetHash); Ok(()) } @@ -159,8 +184,24 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-host fn SetHost(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetHost); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps. + if !copy_url.cannot_be_a_base() { + // Step 5: Basic URL parse the given value, with copyURL as url and host state + // as state override. + let _ = copy_url.as_mut_url().set_host(Some(&value.0)); + // Step 6: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -178,8 +219,24 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-hostname fn SetHostname(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetHostname); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps. + if !copy_url.cannot_be_a_base() { + // Step 5: Basic URL parse the given value, with copyURL as url and hostname + // state as state override. + let _ = copy_url.as_mut_url().set_host(Some(&value.0)); + // Step 6: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -191,14 +248,20 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-href fn SetHref(&self, value: USVString) -> ErrorResult { - // Note: no call to self.check_same_origin_domain() - let current_url = self.window.get_url(); - let url = match current_url.join(&value.0) { - Ok(url) => url, - Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), - }; - let referrer = Referrer::ReferrerUrl(current_url.clone()); - self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Note: no call to self.check_same_origin_domain() + // Step 2: Parse the given value relative to the entry settings object. + // If that failed, throw a TypeError exception. + let base_url = self.entry_document().url(); + let url = match base_url.join(&value.0) { + Ok(url) => url, + Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), + }; + // Step 3: Location-object-setter navigate to the resulting URL record. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); + } Ok(()) } @@ -210,8 +273,25 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-pathname fn SetPathname(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetPathname); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps. + if !copy_url.cannot_be_a_base() { + // Step 5: Set copyURL's path to the empty list. + // Step 6: Basic URL parse the given value, with copyURL as url and path + // start state as state override. + copy_url.as_mut_url().set_path(&value.0); + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -223,8 +303,30 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-port fn SetPort(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetPort); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL cannot have a username/password/port, then return. + // https://url.spec.whatwg.org/#cannot-have-a-username-password-port + if copy_url.host().is_some() && + !copy_url.cannot_be_a_base() && + copy_url.scheme() != "file" + { + // Step 5: If the given value is the empty string, then set copyURL's + // port to null. + // Step 6: Otherwise, basic URL parse the given value, with copyURL as url + // and port state as state override. + let _ = url::quirks::set_port(copy_url.as_mut_url(), &value.0); + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -236,8 +338,34 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-protocol fn SetProtocol(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetProtocol); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: Let possibleFailure be the result of basic URL parsing the given + // value, followed by ":", with copyURL as url and scheme start state as + // state override. + let scheme = match value.0.find(':') { + Some(position) => &value.0[..position], + None => &value.0, + }; + if let Err(_) = copy_url.as_mut_url().set_scheme(scheme) { + // Step 5: If possibleFailure is failure, then throw a "SyntaxError" DOMException. + return Err(Error::Syntax); + } + // Step 6: If copyURL's scheme is not an HTTP(S) scheme, then terminate these steps. + if copy_url.scheme().eq_ignore_ascii_case("http") || + copy_url.scheme().eq_ignore_ascii_case("https") + { + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -249,8 +377,30 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-search fn SetSearch(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetSearch); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If the given value is the empty string, set copyURL's query to null. + // Step 5: Otherwise, run these substeps: + // 1. Let input be the given value with a single leading "?" removed, if any. + // 2. Set copyURL's query to the empty string. + // 3. Basic URL parse input, with copyURL as url and query state as state + // override, and the relevant Document's document's character encoding as + // encoding override. + copy_url.as_mut_url().set_query(match value.0.as_str() { + "" => None, + _ if value.0.starts_with('?') => Some(&value.0[1..]), + _ => Some(&value.0), + }); + // Step 6: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } Ok(()) } } diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index b27c02208be..b1ea4f5cde2 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -496,7 +496,9 @@ macro_rules! global_event_handlers( event_handler!(suspend, GetOnsuspend, SetOnsuspend); event_handler!(timeupdate, GetOntimeupdate, SetOntimeupdate); event_handler!(toggle, GetOntoggle, SetOntoggle); + event_handler!(transitioncancel, GetOntransitioncancel, SetOntransitioncancel); event_handler!(transitionend, GetOntransitionend, SetOntransitionend); + event_handler!(transitionrun, GetOntransitionrun, SetOntransitionrun); event_handler!(volumechange, GetOnvolumechange, SetOnvolumechange); event_handler!(waiting, GetOnwaiting, SetOnwaiting); ) diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index a410220b427..a0f6241d55e 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -572,12 +572,15 @@ pub mod xmlhttprequesteventtarget; pub mod xmlhttprequestupload; pub mod xmlserializer; pub mod xrframe; +pub mod xrhand; pub mod xrhittestresult; pub mod xrhittestsource; pub mod xrinputsource; pub mod xrinputsourcearray; pub mod xrinputsourceevent; pub mod xrinputsourceschangeevent; +pub mod xrjointpose; +pub mod xrjointspace; pub mod xrlayer; pub mod xrmediabinding; pub mod xrpose; diff --git a/components/script/dom/serviceworkerregistration.rs b/components/script/dom/serviceworkerregistration.rs index f242da12404..9bb578e0d59 100644 --- a/components/script/dom/serviceworkerregistration.rs +++ b/components/script/dom/serviceworkerregistration.rs @@ -112,7 +112,7 @@ impl ServiceWorkerRegistration { let worker_id = WorkerId(Uuid::new_v4()); let devtools_chan = global.devtools_chan().cloned(); - let init = prepare_workerscope_init(&global, None); + let init = prepare_workerscope_init(&global, None, None); ScopeThings { script_url: script_url, init: init, diff --git a/components/script/dom/vertexarrayobject.rs b/components/script/dom/vertexarrayobject.rs index 34ef5b428bb..fcf6461e2a8 100644 --- a/components/script/dom/vertexarrayobject.rs +++ b/components/script/dom/vertexarrayobject.rs @@ -7,7 +7,7 @@ use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebG use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use crate::dom::bindings::root::{Dom, MutNullableDom}; use crate::dom::webglbuffer::WebGLBuffer; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{ ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVersion, WebGLVertexArrayId, }; @@ -45,26 +45,25 @@ impl VertexArrayObject { self.is_deleted.get() } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { assert!(self.id.is_some()); if self.is_deleted.get() { return; } self.is_deleted.set(true); let cmd = WebGLCommand::DeleteVertexArray(self.id.unwrap()); - if fallible { - self.context.send_command_ignored(cmd); - } else { - self.context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => self.context.send_command_ignored(cmd), + Operation::Infallible => self.context.send_command(cmd), } for attrib_data in &**self.vertex_attribs.borrow() { if let Some(buffer) = attrib_data.buffer() { - buffer.decrement_attached_counter(fallible); + buffer.decrement_attached_counter(operation_fallibility); } } if let Some(buffer) = self.element_array_buffer.get() { - buffer.decrement_attached_counter(fallible); + buffer.decrement_attached_counter(operation_fallibility); } } @@ -156,7 +155,7 @@ impl VertexArrayObject { offset as u32, )); if let Some(old) = data.buffer() { - old.decrement_attached_counter(false); + old.decrement_attached_counter(Operation::Infallible); } *data = VertexAttribData { @@ -188,7 +187,7 @@ impl VertexArrayObject { if b.id() != buffer.id() { continue; } - b.decrement_attached_counter(false); + b.decrement_attached_counter(Operation::Infallible); } attrib.buffer = None; } @@ -197,7 +196,7 @@ impl VertexArrayObject { .get() .map_or(false, |b| buffer == &*b) { - buffer.decrement_attached_counter(false); + buffer.decrement_attached_counter(Operation::Infallible); self.element_array_buffer.set(None); } } @@ -256,7 +255,7 @@ impl VertexArrayObject { impl Drop for VertexArrayObject { fn drop(&mut self) { if self.id.is_some() { - self.delete(true); + self.delete(Operation::Fallible); } } } diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 949f3a3bbdc..6ed42deea01 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -18,7 +18,6 @@ use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlcanvaselement::HTMLCanvasElement; -use crate::dom::htmliframeelement::HTMLIFrameElement; use crate::dom::webglactiveinfo::WebGLActiveInfo; use crate::dom::webglbuffer::WebGLBuffer; use crate::dom::webglframebuffer::{WebGLFramebuffer, WebGLFramebufferAttachmentRoot}; @@ -26,7 +25,7 @@ use crate::dom::webglprogram::WebGLProgram; use crate::dom::webglquery::WebGLQuery; use crate::dom::webglrenderbuffer::WebGLRenderbuffer; use crate::dom::webglrenderingcontext::{ - uniform_get, uniform_typed, LayoutCanvasWebGLRenderingContextHelpers, VertexAttrib, + uniform_get, uniform_typed, LayoutCanvasWebGLRenderingContextHelpers, Operation, VertexAttrib, WebGLRenderingContext, }; use crate::dom::webglsampler::{WebGLSampler, WebGLSamplerValue}; @@ -339,7 +338,7 @@ impl WebGL2RenderingContext { fn unbind_from(&self, slot: &MutNullableDom<WebGLBuffer>, buffer: &WebGLBuffer) { if slot.get().map_or(false, |b| buffer == &*b) { - buffer.decrement_attached_counter(false); + buffer.decrement_attached_counter(Operation::Infallible); slot.set(None); } } @@ -868,8 +867,10 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn GetBufferParameter(&self, cx: JSContext, target: u32, parameter: u32) -> JSVal { - self.base.GetBufferParameter(cx, target, parameter) + fn GetBufferParameter(&self, _cx: JSContext, target: u32, parameter: u32) -> JSVal { + let buffer = + handle_potential_webgl_error!(self.base, self.bound_buffer(target), return NullValue()); + self.base.get_buffer_param(buffer, parameter) } #[allow(unsafe_code)] @@ -1225,7 +1226,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn BufferData(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) { + fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) { let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self.base, self.bound_buffer(target), return); @@ -1233,7 +1234,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn BufferData_(&self, target: u32, size: i64, usage: u32) { + fn BufferData(&self, target: u32, size: i64, usage: u32) { let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self.base, self.bound_buffer(target), return); @@ -1457,7 +1458,8 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } } - /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6 + #[allow(unsafe_code)] fn CompressedTexImage2D( &self, target: u32, @@ -1467,19 +1469,32 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { height: i32, border: i32, pixels: CustomAutoRooterGuard<ArrayBufferView>, + src_offset: u32, + src_length_override: u32, ) { - self.base.CompressedTexImage2D( + let mut data = unsafe { pixels.as_slice() }; + let start = src_offset as usize; + let end = (src_offset + src_length_override) as usize; + if start > data.len() || end > data.len() { + self.base.webgl_error(InvalidValue); + return; + } + if src_length_override != 0 { + data = &data[start..end]; + } + self.base.compressed_tex_image_2d( target, level, internal_format, width, height, border, - pixels, + data, ) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 + #[allow(unsafe_code)] fn CompressedTexSubImage2D( &self, target: u32, @@ -1490,9 +1505,21 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { height: i32, format: u32, pixels: CustomAutoRooterGuard<ArrayBufferView>, + src_offset: u32, + src_length_override: u32, ) { - self.base.CompressedTexSubImage2D( - target, level, xoffset, yoffset, width, height, format, pixels, + let mut data = unsafe { pixels.as_slice() }; + let start = src_offset as usize; + let end = (src_offset + src_length_override) as usize; + if start > data.len() || end > data.len() { + self.base.webgl_error(InvalidValue); + return; + } + if src_length_override != 0 { + data = &data[start..end]; + } + self.base.compressed_tex_sub_image_2d( + target, level, xoffset, yoffset, width, height, format, data, ) } @@ -1665,7 +1692,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.unbind_from(&binding.buffer, &buffer); } - buffer.mark_for_deletion(false); + buffer.mark_for_deletion(Operation::Infallible); } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 @@ -2136,7 +2163,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform1iv(location, v, src_offset, src_length) + self.base.uniform1iv(location, v, src_offset, src_length) } // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 @@ -2198,7 +2225,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform1fv(location, v, src_offset, src_length); + self.base.uniform1fv(location, v, src_offset, src_length); } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2214,7 +2241,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform2fv(location, v, src_offset, src_length); + self.base.uniform2fv(location, v, src_offset, src_length); } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2230,7 +2257,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform2iv(location, v, src_offset, src_length) + self.base.uniform2iv(location, v, src_offset, src_length) } // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 @@ -2279,7 +2306,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform3fv(location, v, src_offset, src_length); + self.base.uniform3fv(location, v, src_offset, src_length); } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2295,7 +2322,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform3iv(location, v, src_offset, src_length) + self.base.uniform3iv(location, v, src_offset, src_length) } // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 @@ -2344,7 +2371,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform4iv(location, v, src_offset, src_length) + self.base.uniform4iv(location, v, src_offset, src_length) } // https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 @@ -2393,7 +2420,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_offset: u32, src_length: u32, ) { - self.base.Uniform4fv(location, v, src_offset, src_length); + self.base.uniform4fv(location, v, src_offset, src_length); } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2406,7 +2433,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_length: u32, ) { self.base - .UniformMatrix2fv(location, transpose, v, src_offset, src_length) + .uniform_matrix_2fv(location, transpose, v, src_offset, src_length) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2419,7 +2446,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_length: u32, ) { self.base - .UniformMatrix3fv(location, transpose, v, src_offset, src_length) + .uniform_matrix_3fv(location, transpose, v, src_offset, src_length) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2432,7 +2459,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { src_length: u32, ) { self.base - .UniformMatrix4fv(location, transpose, v, src_offset, src_length) + .uniform_matrix_4fv(location, transpose, v, src_offset, src_length) } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 @@ -2798,7 +2825,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { &self, target: u32, level: i32, - internal_format: u32, + internal_format: i32, width: i32, height: i32, border: i32, @@ -2824,7 +2851,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { &self, target: u32, level: i32, - internal_format: u32, + internal_format: i32, format: u32, data_type: u32, source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement, @@ -2834,30 +2861,6 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 - fn TexImageDOM( - &self, - target: u32, - level: i32, - internal_format: u32, - width: i32, - height: i32, - format: u32, - data_type: u32, - source: &HTMLIFrameElement, - ) -> Fallible<()> { - self.base.TexImageDOM( - target, - level, - internal_format, - width, - height, - format, - data_type, - source, - ) - } - - /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn TexSubImage2D( &self, target: u32, @@ -3086,7 +3089,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } } - query.delete(false); + query.delete(Operation::Infallible); } } @@ -3112,7 +3115,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { slot.set(None); } } - sampler.delete(false); + sampler.delete(Operation::Infallible); } } @@ -3343,7 +3346,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { fn DeleteSync(&self, sync: Option<&WebGLSync>) { if let Some(sync) = sync { handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return); - sync.delete(false); + sync.delete(Operation::Infallible); } } @@ -3422,7 +3425,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.webgl_error(InvalidOperation); return; } - tf.delete(false); + tf.delete(Operation::Infallible); self.current_transform_feedback.set(None); } } @@ -3656,7 +3659,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { for slot in &[&generic_slot, &indexed_binding.buffer] { if let Some(old) = slot.get() { - old.decrement_attached_counter(false); + old.decrement_attached_counter(Operation::Infallible); } slot.set(buffer); } @@ -3734,7 +3737,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { for slot in &[&generic_slot, &indexed_binding.buffer] { if let Some(old) = slot.get() { - old.decrement_attached_counter(false); + old.decrement_attached_counter(Operation::Infallible); } slot.set(buffer); } @@ -3998,7 +4001,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } if width < 0 || height < 0 { - return; + return self.base.webgl_error(InvalidValue); } self.base diff --git a/components/script/dom/webglbuffer.rs b/components/script/dom/webglbuffer.rs index 5ee1e7e1373..f81270d8240 100644 --- a/components/script/dom/webglbuffer.rs +++ b/components/script/dom/webglbuffer.rs @@ -9,7 +9,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::webgl_channel; use canvas_traits::webgl::{WebGLBufferId, WebGLCommand, WebGLError, WebGLResult}; use dom_struct::dom_struct; @@ -97,24 +97,23 @@ impl WebGLBuffer { self.capacity.get() } - pub fn mark_for_deletion(&self, fallible: bool) { + pub fn mark_for_deletion(&self, operation_fallibility: Operation) { if self.marked_for_deletion.get() { return; } self.marked_for_deletion.set(true); if self.is_deleted() { - self.delete(fallible); + self.delete(operation_fallibility); } } - fn delete(&self, fallible: bool) { + fn delete(&self, operation_fallibility: Operation) { assert!(self.is_deleted()); let context = self.upcast::<WebGLObject>().context(); let cmd = WebGLCommand::DeleteBuffer(self.id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } @@ -164,7 +163,7 @@ impl WebGLBuffer { ); } - pub fn decrement_attached_counter(&self, fallible: bool) { + pub fn decrement_attached_counter(&self, operation_fallibility: Operation) { self.attached_counter.set( self.attached_counter .get() @@ -172,7 +171,7 @@ impl WebGLBuffer { .expect("refcount underflowed"), ); if self.is_deleted() { - self.delete(fallible); + self.delete(operation_fallibility); } } @@ -183,6 +182,6 @@ impl WebGLBuffer { impl Drop for WebGLBuffer { fn drop(&mut self) { - self.mark_for_deletion(true); + self.mark_for_deletion(Operation::Fallible); } } diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs index 4b2f7c7d56e..9f38365c4c7 100644 --- a/components/script/dom/webglframebuffer.rs +++ b/components/script/dom/webglframebuffer.rs @@ -11,7 +11,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::webglobject::WebGLObject; use crate::dom::webglrenderbuffer::WebGLRenderbuffer; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use crate::dom::webgltexture::WebGLTexture; use crate::dom::xrsession::XRSession; use canvas_traits::webgl::{webgl_channel, WebGLError, WebGLResult, WebGLVersion}; @@ -207,15 +207,14 @@ impl WebGLFramebuffer { )); } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if !self.is_deleted.get() { self.is_deleted.set(true); let context = self.upcast::<WebGLObject>().context(); let cmd = WebGLCommand::DeleteFramebuffer(self.id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } } @@ -977,7 +976,7 @@ impl WebGLFramebuffer { impl Drop for WebGLFramebuffer { fn drop(&mut self) { - let _ = self.delete(true); + let _ = self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 79598f7f5c1..88cecf0296f 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -12,7 +12,7 @@ use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::webglactiveinfo::WebGLActiveInfo; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use crate::dom::webglshader::WebGLShader; use crate::dom::webgluniformlocation::WebGLUniformLocation; use canvas_traits::webgl::{webgl_channel, WebGLProgramId, WebGLResult}; @@ -84,17 +84,16 @@ impl WebGLProgram { } /// glDeleteProgram - pub fn mark_for_deletion(&self, fallible: bool) { + pub fn mark_for_deletion(&self, operation_fallibility: Operation) { if self.marked_for_deletion.get() { return; } self.marked_for_deletion.set(true); let cmd = WebGLCommand::DeleteProgram(self.id); let context = self.upcast::<WebGLObject>().context(); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } if self.is_deleted() { self.detach_shaders(); @@ -639,7 +638,7 @@ impl WebGLProgram { impl Drop for WebGLProgram { fn drop(&mut self) { self.in_use(false); - self.mark_for_deletion(true); + self.mark_for_deletion(Operation::Fallible); } } diff --git a/components/script/dom/webglquery.rs b/components/script/dom/webglquery.rs index d7418471936..454fe72954e 100644 --- a/components/script/dom/webglquery.rs +++ b/components/script/dom/webglquery.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use crate::task_source::TaskSource; use canvas_traits::webgl::WebGLError::*; use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLQueryId}; @@ -96,16 +96,15 @@ impl WebGLQuery { Ok(()) } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if !self.marked_for_deletion.get() { self.marked_for_deletion.set(true); let context = self.upcast::<WebGLObject>().context(); let command = WebGLCommand::DeleteQuery(self.gl_id); - if fallible { - context.send_command_ignored(command); - } else { - context.send_command(command); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(command), + Operation::Infallible => context.send_command(command), } } } @@ -187,6 +186,6 @@ impl WebGLQuery { impl Drop for WebGLQuery { fn drop(&mut self) { - self.delete(true); + self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webglrenderbuffer.rs b/components/script/dom/webglrenderbuffer.rs index f1a3ba99c53..204fe9ab46e 100644 --- a/components/script/dom/webglrenderbuffer.rs +++ b/components/script/dom/webglrenderbuffer.rs @@ -11,7 +11,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::webglframebuffer::WebGLFramebuffer; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{ webgl_channel, GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult, WebGLVersion, @@ -90,7 +90,7 @@ impl WebGLRenderbuffer { .send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id))); } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if !self.is_deleted.get() { self.is_deleted.set(true); @@ -113,10 +113,9 @@ impl WebGLRenderbuffer { } let cmd = WebGLCommand::DeleteRenderbuffer(self.id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } } @@ -277,6 +276,6 @@ impl WebGLRenderbuffer { impl Drop for WebGLRenderbuffer { fn drop(&mut self) { - self.delete(true); + self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 53dff84c6ec..e616410b31d 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -155,6 +155,12 @@ pub enum VertexAttrib { Uint(u32, u32, u32, u32), } +#[derive(Clone, Copy, Debug)] +pub enum Operation { + Fallible, + Infallible, +} + #[dom_struct] pub struct WebGLRenderingContext { reflector_: Reflector, @@ -1142,7 +1148,7 @@ impl WebGLRenderingContext { self.current_vao.set(None); self.send_command(WebGLCommand::BindVertexArray(None)); } - vao.delete(false); + vao.delete(Operation::Infallible); } } @@ -1160,7 +1166,7 @@ impl WebGLRenderingContext { self.current_vao_webgl2.set(None); self.send_command(WebGLCommand::BindVertexArray(None)); } - vao.delete(false); + vao.delete(Operation::Infallible); } } @@ -1335,7 +1341,7 @@ impl WebGLRenderingContext { self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id()))); if let Some(old) = slot.get() { - old.decrement_attached_counter(false); + old.decrement_attached_counter(Operation::Infallible); } slot.set(buffer); @@ -1533,6 +1539,354 @@ impl WebGLRenderingContext { let last_slot = constants::COLOR_ATTACHMENT0 + self.limits().max_color_attachments - 1; constants::COLOR_ATTACHMENT0 <= attachment && attachment <= last_slot } + + pub fn compressed_tex_image_2d<'a>( + &self, + target: u32, + level: i32, + internal_format: u32, + width: i32, + height: i32, + border: i32, + data: &'a [u8], + ) { + let validator = CompressedTexImage2DValidator::new( + self, + target, + level, + width, + height, + border, + internal_format, + data.len(), + ); + let CommonCompressedTexImage2DValidatorResult { + texture, + target, + level, + width, + height, + compression, + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, + }; + + let size = Size2D::new(width, height); + let buff = IpcSharedMemory::from_bytes(data); + let pixels = TexPixels::from_array(buff, size); + let data = pixels.data; + + handle_potential_webgl_error!( + self, + texture.initialize( + target, + size.width, + size.height, + 1, + compression.format, + level, + Some(TexDataType::UnsignedByte) + ) + ); + + self.send_command(WebGLCommand::CompressedTexImage2D { + target: target.as_gl_constant(), + level, + internal_format, + size: Size2D::new(width, height), + data: data.into(), + }); + + if let Some(fb) = self.bound_draw_framebuffer.get() { + fb.invalidate_texture(&*texture); + } + } + + pub fn compressed_tex_sub_image_2d<'a>( + &self, + target: u32, + level: i32, + xoffset: i32, + yoffset: i32, + width: i32, + height: i32, + format: u32, + data: &'a [u8], + ) { + let validator = CompressedTexSubImage2DValidator::new( + self, + target, + level, + xoffset, + yoffset, + width, + height, + format, + data.len(), + ); + let CommonCompressedTexImage2DValidatorResult { + texture: _, + target, + level, + width, + height, + .. + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, + }; + + let buff = IpcSharedMemory::from_bytes(data); + let pixels = TexPixels::from_array(buff, Size2D::new(width, height)); + let data = pixels.data; + + self.send_command(WebGLCommand::CompressedTexSubImage2D { + target: target.as_gl_constant(), + level: level as i32, + xoffset, + yoffset, + size: Size2D::new(width, height), + format, + data: data.into(), + }); + } + + pub fn uniform1iv( + &self, + location: Option<&WebGLUniformLocation>, + val: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL | + constants::INT | + constants::SAMPLER_2D | + constants::SAMPLER_CUBE => {}, + _ => return Err(InvalidOperation), + } + + let val = self.uniform_vec_section_int(val, src_offset, src_length, 1, location)?; + + match location.type_() { + constants::SAMPLER_2D | constants::SAMPLER_CUBE => { + for &v in val + .iter() + .take(cmp::min(location.size().unwrap_or(1) as usize, val.len())) + { + if v < 0 || v as u32 >= self.limits.max_combined_texture_image_units { + return Err(InvalidValue); + } + } + }, + _ => {}, + } + self.send_command(WebGLCommand::Uniform1iv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform1fv( + &self, + location: Option<&WebGLUniformLocation>, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL | constants::FLOAT => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_float(val, src_offset, src_length, 1, location)?; + self.send_command(WebGLCommand::Uniform1fv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform2fv( + &self, + location: Option<&WebGLUniformLocation>, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_float(val, src_offset, src_length, 2, location)?; + self.send_command(WebGLCommand::Uniform2fv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform2iv( + &self, + location: Option<&WebGLUniformLocation>, + val: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL_VEC2 | constants::INT_VEC2 => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_int(val, src_offset, src_length, 2, location)?; + self.send_command(WebGLCommand::Uniform2iv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform3fv( + &self, + location: Option<&WebGLUniformLocation>, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_float(val, src_offset, src_length, 3, location)?; + self.send_command(WebGLCommand::Uniform3fv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform3iv( + &self, + location: Option<&WebGLUniformLocation>, + val: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL_VEC3 | constants::INT_VEC3 => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_int(val, src_offset, src_length, 3, location)?; + self.send_command(WebGLCommand::Uniform3iv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform4iv( + &self, + location: Option<&WebGLUniformLocation>, + val: Int32ArrayOrLongSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL_VEC4 | constants::INT_VEC4 => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_int(val, src_offset, src_length, 4, location)?; + self.send_command(WebGLCommand::Uniform4iv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform4fv( + &self, + location: Option<&WebGLUniformLocation>, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {}, + _ => return Err(InvalidOperation), + } + let val = self.uniform_vec_section_float(val, src_offset, src_length, 4, location)?; + self.send_command(WebGLCommand::Uniform4fv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform_matrix_2fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT2 => {}, + _ => return Err(InvalidOperation), + } + let val = + self.uniform_matrix_section(val, src_offset, src_length, transpose, 4, location)?; + self.send_command(WebGLCommand::UniformMatrix2fv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform_matrix_3fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT3 => {}, + _ => return Err(InvalidOperation), + } + let val = + self.uniform_matrix_section(val, src_offset, src_length, transpose, 9, location)?; + self.send_command(WebGLCommand::UniformMatrix3fv(location.id(), val)); + Ok(()) + }); + } + + pub fn uniform_matrix_4fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT4 => {}, + _ => return Err(InvalidOperation), + } + let val = + self.uniform_matrix_section(val, src_offset, src_length, transpose, 16, location)?; + self.send_command(WebGLCommand::UniformMatrix4fv(location.id(), val)); + Ok(()) + }); + } + + pub fn get_buffer_param(&self, buffer: Option<DomRoot<WebGLBuffer>>, parameter: u32) -> JSVal { + let buffer = + handle_potential_webgl_error!(self, buffer.ok_or(InvalidOperation), return NullValue()); + + match parameter { + constants::BUFFER_SIZE => Int32Value(buffer.capacity() as i32), + constants::BUFFER_USAGE => Int32Value(buffer.usage() as i32), + _ => { + self.webgl_error(InvalidEnum); + NullValue() + }, + } + } } #[cfg(not(feature = "webgl_backtrace"))] @@ -1594,21 +1948,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn GetBufferParameter(&self, _cx: SafeJSContext, target: u32, parameter: u32) -> JSVal { - let buffer = handle_potential_webgl_error!( - self, - self.bound_buffer(target) - .and_then(|buf| buf.ok_or(InvalidOperation)), - return NullValue() - ); - - match parameter { - constants::BUFFER_SIZE => Int32Value(buffer.capacity() as i32), - constants::BUFFER_USAGE => Int32Value(buffer.usage() as i32), - _ => { - self.webgl_error(InvalidEnum); - NullValue() - }, - } + let buffer = + handle_potential_webgl_error!(self, self.bound_buffer(target), return NullValue()); + self.get_buffer_param(buffer, parameter) } #[allow(unsafe_code)] @@ -2110,14 +2452,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn BufferData(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) { + fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) { let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); self.buffer_data(target, data, usage, bound_buffer) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn BufferData_(&self, target: u32, size: i64, usage: u32) { + fn BufferData(&self, target: u32, size: i64, usage: u32) { let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); self.buffer_data_(target, size, usage, bound_buffer) @@ -2142,55 +2484,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { border: i32, data: CustomAutoRooterGuard<ArrayBufferView>, ) { - let validator = CompressedTexImage2DValidator::new( - self, - target, - level, - width, - height, - border, - internal_format, - data.len(), - ); - let CommonCompressedTexImage2DValidatorResult { - texture, - target, - level, - width, - height, - compression, - } = match validator.validate() { - Ok(result) => result, - Err(_) => return, - }; - - let buff = IpcSharedMemory::from_bytes(unsafe { data.as_slice() }); - let pixels = TexPixels::from_array(buff, Size2D::new(width, height)); - - handle_potential_webgl_error!( - self, - texture.initialize( - target, - pixels.size.width, - pixels.size.height, - 1, - compression.format, - level, - Some(TexDataType::UnsignedByte) - ) - ); - - self.send_command(WebGLCommand::CompressedTexImage2D { - target: target.as_gl_constant(), - level, - internal_format, - size: Size2D::new(width, height), - data: pixels.data.into(), - }); - - if let Some(fb) = self.bound_draw_framebuffer.get() { - fb.invalidate_texture(&*texture); - } + let data = unsafe { data.as_slice() }; + self.compressed_tex_image_2d(target, level, internal_format, width, height, border, data) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -2206,41 +2501,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { format: u32, data: CustomAutoRooterGuard<ArrayBufferView>, ) { - let validator = CompressedTexSubImage2DValidator::new( - self, - target, - level, - xoffset, - yoffset, - width, - height, - format, - data.len(), - ); - let CommonCompressedTexImage2DValidatorResult { - texture: _, - target, - level, - width, - height, - .. - } = match validator.validate() { - Ok(result) => result, - Err(_) => return, - }; - - let buff = IpcSharedMemory::from_bytes(unsafe { data.as_slice() }); - let pixels = TexPixels::from_array(buff, Size2D::new(width, height)); - - self.send_command(WebGLCommand::CompressedTexSubImage2D { - target: target.as_gl_constant(), - level: level as i32, - xoffset, - yoffset, - size: Size2D::new(width, height), - format, - data: pixels.data.into(), - }); + let data = unsafe { data.as_slice() }; + self.compressed_tex_sub_image_2d( + target, level, xoffset, yoffset, width, height, format, data, + ) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -2583,9 +2847,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { .map_or(false, |b| buffer == &*b) { self.bound_buffer_array.set(None); - buffer.decrement_attached_counter(false); + buffer.decrement_attached_counter(Operation::Infallible); } - buffer.mark_for_deletion(false); + buffer.mark_for_deletion(Operation::Infallible); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 @@ -2605,7 +2869,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { WebGLFramebufferBindingRequest::Default )) ); - framebuffer.delete(false) + framebuffer.delete(Operation::Infallible) } } @@ -2622,7 +2886,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { None )) ); - renderbuffer.delete(false) + renderbuffer.delete(Operation::Infallible) } } @@ -2657,7 +2921,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { )); } - texture.delete(false) + texture.delete(Operation::Infallible) } } @@ -2665,7 +2929,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { fn DeleteProgram(&self, program: Option<&WebGLProgram>) { if let Some(program) = program { handle_potential_webgl_error!(self, self.validate_ownership(program), return); - program.mark_for_deletion(false) + program.mark_for_deletion(Operation::Infallible) } } @@ -2673,7 +2937,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { fn DeleteShader(&self, shader: Option<&WebGLShader>) { if let Some(shader) = shader { handle_potential_webgl_error!(self, self.validate_ownership(shader), return); - shader.mark_for_deletion(false) + shader.mark_for_deletion(Operation::Infallible) } } @@ -3548,40 +3812,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 - fn Uniform1iv( - &self, - location: Option<&WebGLUniformLocation>, - val: Int32ArrayOrLongSequence, - src_offset: u32, - src_length: u32, - ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL | - constants::INT | - constants::SAMPLER_2D | - constants::SAMPLER_CUBE => {}, - _ => return Err(InvalidOperation), - } - - let val = self.uniform_vec_section_int(val, src_offset, src_length, 1, location)?; - - match location.type_() { - constants::SAMPLER_2D | constants::SAMPLER_CUBE => { - for &v in val - .iter() - .take(cmp::min(location.size().unwrap_or(1) as usize, val.len())) - { - if v < 0 || v as u32 >= self.limits.max_combined_texture_image_units { - return Err(InvalidValue); - } - } - }, - _ => {}, - } - self.send_command(WebGLCommand::Uniform1iv(location.id(), val)); - Ok(()) - }); + fn Uniform1iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) { + self.uniform1iv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3589,18 +3821,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { &self, location: Option<&WebGLUniformLocation>, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL | constants::FLOAT => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_float(val, src_offset, src_length, 1, location)?; - self.send_command(WebGLCommand::Uniform1fv(location.id(), val)); - Ok(()) - }); + self.uniform1fv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3620,18 +3842,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { &self, location: Option<&WebGLUniformLocation>, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_float(val, src_offset, src_length, 2, location)?; - self.send_command(WebGLCommand::Uniform2fv(location.id(), val)); - Ok(()) - }); + self.uniform2fv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3647,22 +3859,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 - fn Uniform2iv( - &self, - location: Option<&WebGLUniformLocation>, - val: Int32ArrayOrLongSequence, - src_offset: u32, - src_length: u32, - ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL_VEC2 | constants::INT_VEC2 => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_int(val, src_offset, src_length, 2, location)?; - self.send_command(WebGLCommand::Uniform2iv(location.id(), val)); - Ok(()) - }); + fn Uniform2iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) { + self.uniform2iv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3682,18 +3880,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { &self, location: Option<&WebGLUniformLocation>, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_float(val, src_offset, src_length, 3, location)?; - self.send_command(WebGLCommand::Uniform3fv(location.id(), val)); - Ok(()) - }); + self.uniform3fv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3709,22 +3897,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 - fn Uniform3iv( - &self, - location: Option<&WebGLUniformLocation>, - val: Int32ArrayOrLongSequence, - src_offset: u32, - src_length: u32, - ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL_VEC3 | constants::INT_VEC3 => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_int(val, src_offset, src_length, 3, location)?; - self.send_command(WebGLCommand::Uniform3iv(location.id(), val)); - Ok(()) - }); + fn Uniform3iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) { + self.uniform3iv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3740,22 +3914,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 - fn Uniform4iv( - &self, - location: Option<&WebGLUniformLocation>, - val: Int32ArrayOrLongSequence, - src_offset: u32, - src_length: u32, - ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL_VEC4 | constants::INT_VEC4 => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_int(val, src_offset, src_length, 4, location)?; - self.send_command(WebGLCommand::Uniform4iv(location.id(), val)); - Ok(()) - }); + fn Uniform4iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) { + self.uniform4iv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3775,18 +3935,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { &self, location: Option<&WebGLUniformLocation>, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {}, - _ => return Err(InvalidOperation), - } - let val = self.uniform_vec_section_float(val, src_offset, src_length, 4, location)?; - self.send_command(WebGLCommand::Uniform4fv(location.id(), val)); - Ok(()) - }); + self.uniform4fv(location, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3795,19 +3945,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::FLOAT_MAT2 => {}, - _ => return Err(InvalidOperation), - } - let val = - self.uniform_matrix_section(val, src_offset, src_length, transpose, 4, location)?; - self.send_command(WebGLCommand::UniformMatrix2fv(location.id(), val)); - Ok(()) - }); + self.uniform_matrix_2fv(location, transpose, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3816,19 +3955,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::FLOAT_MAT3 => {}, - _ => return Err(InvalidOperation), - } - let val = - self.uniform_matrix_section(val, src_offset, src_length, transpose, 9, location)?; - self.send_command(WebGLCommand::UniformMatrix3fv(location.id(), val)); - Ok(()) - }); + self.uniform_matrix_3fv(location, transpose, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -3837,19 +3965,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, val: Float32ArrayOrUnrestrictedFloatSequence, - src_offset: u32, - src_length: u32, ) { - self.with_location(location, |location| { - match location.type_() { - constants::FLOAT_MAT4 => {}, - _ => return Err(InvalidOperation), - } - let val = - self.uniform_matrix_section(val, src_offset, src_length, transpose, 16, location)?; - self.send_command(WebGLCommand::UniformMatrix4fv(location.id(), val)); - Ok(()) - }); + self.uniform_matrix_4fv(location, transpose, val, 0, 0) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -4055,7 +4172,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { &self, target: u32, level: i32, - internal_format: u32, + internal_format: i32, width: i32, height: i32, border: i32, @@ -4071,7 +4188,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { self, target, level, - internal_format, + internal_format as u32, width, height, border, @@ -4153,7 +4270,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { &self, target: u32, level: i32, - internal_format: u32, + internal_format: i32, format: u32, data_type: u32, source: TexImageSource, @@ -4171,7 +4288,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { self, target, level, - internal_format, + internal_format as u32, pixels.size.width as i32, pixels.size.height as i32, 0, diff --git a/components/script/dom/webglsampler.rs b/components/script/dom/webglsampler.rs index c80e2a6a508..063decffe8f 100644 --- a/components/script/dom/webglsampler.rs +++ b/components/script/dom/webglsampler.rs @@ -7,7 +7,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::WebGLError::*; use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLSamplerId}; use dom_struct::dom_struct; @@ -90,16 +90,15 @@ impl WebGLSampler { ) } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if !self.marked_for_deletion.get() { self.marked_for_deletion.set(true); let command = WebGLCommand::DeleteSampler(self.gl_id); let context = self.upcast::<WebGLObject>().context(); - if fallible { - context.send_command_ignored(command); - } else { - context.send_command(command); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(command), + Operation::Infallible => context.send_command(command), } } } @@ -180,6 +179,6 @@ impl WebGLSampler { impl Drop for WebGLSampler { fn drop(&mut self) { - self.delete(true); + self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 9be334f6595..0e2a8253968 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -12,7 +12,7 @@ use crate::dom::webgl_extensions::ext::extshadertexturelod::EXTShaderTextureLod; use crate::dom::webgl_extensions::ext::oesstandardderivatives::OESStandardDerivatives; use crate::dom::webgl_extensions::WebGLExtensions; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{webgl_channel, GlType, WebGLVersion}; use canvas_traits::webgl::{GLLimits, WebGLCommand, WebGLError}; use canvas_traits::webgl::{WebGLResult, WebGLSLVersion, WebGLShaderId}; @@ -344,15 +344,14 @@ impl WebGLShader { /// Mark this shader as deleted (if it wasn't previously) /// and delete it as if calling glDeleteShader. /// Currently does not check if shader is attached - pub fn mark_for_deletion(&self, fallible: bool) { + pub fn mark_for_deletion(&self, operation_fallibility: Operation) { if !self.marked_for_deletion.get() { self.marked_for_deletion.set(true); let context = self.upcast::<WebGLObject>().context(); let cmd = WebGLCommand::DeleteShader(self.id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } } @@ -400,6 +399,6 @@ impl WebGLShader { impl Drop for WebGLShader { fn drop(&mut self) { - self.mark_for_deletion(true); + self.mark_for_deletion(Operation::Fallible); } } diff --git a/components/script/dom/webglsync.rs b/components/script/dom/webglsync.rs index dd6acb5be9a..a6288f69c1e 100644 --- a/components/script/dom/webglsync.rs +++ b/components/script/dom/webglsync.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use crate::task_source::TaskSource; use canvas_traits::webgl::{webgl_channel, WebGLCommand, WebGLSyncId}; use dom_struct::dom_struct; @@ -82,15 +82,14 @@ impl WebGLSync { self.client_wait_status.get() } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if self.is_valid() { self.marked_for_deletion.set(true); let context = self.upcast::<WebGLObject>().context(); let cmd = WebGLCommand::DeleteSync(self.sync_id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } } @@ -131,6 +130,6 @@ impl WebGLSync { impl Drop for WebGLSync { fn drop(&mut self) { - self.delete(true); + self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webgltexture.rs b/components/script/dom/webgltexture.rs index 682920df96e..3059dc7bf4e 100644 --- a/components/script/dom/webgltexture.rs +++ b/components/script/dom/webgltexture.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::webgl_validations::types::TexImageTarget; use crate::dom::webglframebuffer::WebGLFramebuffer; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{webgl_channel, TexDataType, TexFormat, WebGLResult, WebGLTextureId}; use canvas_traits::webgl::{DOMToTextureCommand, WebGLCommand, WebGLError}; use dom_struct::dom_struct; @@ -183,7 +183,7 @@ impl WebGLTexture { self.populate_mip_chain(self.base_mipmap_level, last_level) } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if !self.is_deleted.get() { self.is_deleted.set(true); let context = self.upcast::<WebGLObject>().context(); @@ -210,10 +210,9 @@ impl WebGLTexture { } let cmd = WebGLCommand::DeleteTexture(self.id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } } @@ -420,7 +419,7 @@ impl WebGLTexture { impl Drop for WebGLTexture { fn drop(&mut self) { - self.delete(true); + self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webgltransformfeedback.rs b/components/script/dom/webgltransformfeedback.rs index fe4b0753843..3b0b2abf0a2 100644 --- a/components/script/dom/webgltransformfeedback.rs +++ b/components/script/dom/webgltransformfeedback.rs @@ -6,7 +6,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{webgl_channel, WebGLCommand}; use dom_struct::dom_struct; use std::cell::Cell; @@ -98,15 +98,14 @@ impl WebGLTransformFeedback { self.is_paused.get() } - pub fn delete(&self, fallible: bool) { + pub fn delete(&self, operation_fallibility: Operation) { if self.is_valid() && self.id() != 0 { self.marked_for_deletion.set(true); let context = self.upcast::<WebGLObject>().context(); let cmd = WebGLCommand::DeleteTransformFeedback(self.id); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); + match operation_fallibility { + Operation::Fallible => context.send_command_ignored(cmd), + Operation::Infallible => context.send_command(cmd), } } } @@ -126,6 +125,6 @@ impl WebGLTransformFeedback { impl Drop for WebGLTransformFeedback { fn drop(&mut self) { - self.delete(true); + self.delete(Operation::Fallible); } } diff --git a/components/script/dom/webglvertexarrayobject.rs b/components/script/dom/webglvertexarrayobject.rs index b425a7c4219..07f53af0316 100644 --- a/components/script/dom/webglvertexarrayobject.rs +++ b/components/script/dom/webglvertexarrayobject.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::vertexarrayobject::{VertexArrayObject, VertexAttribData}; use crate::dom::webglbuffer::WebGLBuffer; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId}; use dom_struct::dom_struct; @@ -41,8 +41,8 @@ impl WebGLVertexArrayObject { self.array_object.is_deleted() } - pub fn delete(&self, fallible: bool) { - self.array_object.delete(fallible); + pub fn delete(&self, operation_fallibility: Operation) { + self.array_object.delete(operation_fallibility); } pub fn ever_bound(&self) -> bool { diff --git a/components/script/dom/webglvertexarrayobjectoes.rs b/components/script/dom/webglvertexarrayobjectoes.rs index f10c10d949b..215d39c1a5a 100644 --- a/components/script/dom/webglvertexarrayobjectoes.rs +++ b/components/script/dom/webglvertexarrayobjectoes.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::vertexarrayobject::{VertexArrayObject, VertexAttribData}; use crate::dom::webglbuffer::WebGLBuffer; use crate::dom::webglobject::WebGLObject; -use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use crate::dom::webglrenderingcontext::{Operation, WebGLRenderingContext}; use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId}; use dom_struct::dom_struct; @@ -41,8 +41,8 @@ impl WebGLVertexArrayObjectOES { self.array_object.is_deleted() } - pub fn delete(&self, fallible: bool) { - self.array_object.delete(fallible); + pub fn delete(&self, operation_fallibility: Operation) { + self.array_object.delete(operation_fallibility); } pub fn ever_bound(&self) -> bool { diff --git a/components/script/dom/webidls/ElementContentEditable.webidl b/components/script/dom/webidls/ElementContentEditable.webidl index ba7966c225d..8429700e93e 100644 --- a/components/script/dom/webidls/ElementContentEditable.webidl +++ b/components/script/dom/webidls/ElementContentEditable.webidl @@ -5,7 +5,7 @@ // https://html.spec.whatwg.org/multipage/#elementcontenteditable [Exposed=Window] interface mixin ElementContentEditable { - // [CEReactions] - // attribute DOMString contentEditable; - // readonly attribute boolean isContentEditable; + [CEReactions] + attribute DOMString contentEditable; + readonly attribute boolean isContentEditable; }; diff --git a/components/script/dom/webidls/EventHandler.webidl b/components/script/dom/webidls/EventHandler.webidl index 99be904979b..a3514182806 100644 --- a/components/script/dom/webidls/EventHandler.webidl +++ b/components/script/dom/webidls/EventHandler.webidl @@ -92,7 +92,9 @@ interface mixin GlobalEventHandlers { // https://drafts.csswg.org/css-transitions/#interface-globaleventhandlers-idl partial interface mixin GlobalEventHandlers { + attribute EventHandler ontransitionrun; attribute EventHandler ontransitionend; + attribute EventHandler ontransitioncancel; }; // https://w3c.github.io/selection-api/#extensions-to-globaleventhandlers-interface diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index f6278370cce..926d42292a2 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -286,17 +286,6 @@ interface mixin WebGL2RenderingContextBase const GLenum MAX_CLIENT_WAIT_TIMEOUT_WEBGL = 0x9247; /* Buffer objects */ - // WebGL1: - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - void bufferData(GLenum target, /*[AllowShared]*/ BufferSource? data, GLenum usage); - void bufferData(GLenum target, GLsizeiptr size, GLenum usage); - void bufferSubData(GLenum target, GLintptr dstByteOffset, /*[AllowShared]*/ BufferSource srcData); - // WebGL2: - void bufferData(GLenum target, /*[AllowShared]*/ ArrayBufferView srcData, GLenum usage, GLuint srcOffset, - optional GLuint length = 0); - void bufferSubData(GLenum target, GLintptr dstByteOffset, /*[AllowShared]*/ ArrayBufferView srcData, - GLuint srcOffset, optional GLuint length = 0); - void copyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); // MapBufferRange, in particular its read-only and write-only modes, @@ -326,96 +315,51 @@ interface mixin WebGL2RenderingContextBase // void texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, // GLsizei height, GLsizei depth); - // WebGL1 legacy entrypoints: - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - [Throws] - void texImage2D(GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, GLenum format, - GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); - [Throws] - void texImage2D(GLenum target, GLint level, GLenum internalformat, - GLenum format, GLenum type, TexImageSource source); // May throw DOMException - - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - [Throws] - void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLsizei width, GLsizei height, - GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); - [Throws] - void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLenum format, GLenum type, TexImageSource source); // May throw DOMException - - // WebGL2 entrypoints: - // void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLint border, GLenum format, GLenum type, GLintptr pboOffset); - // void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLint border, GLenum format, GLenum type, - // TexImageSource source); // May throw DOMException - // void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView srcData, - // GLuint srcOffset); - - // void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLsizei depth, GLint border, GLenum format, GLenum type, GLintptr pboOffset); - // void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLsizei depth, GLint border, GLenum format, GLenum type, - // TexImageSource source); // May throw DOMException - // void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLsizei depth, GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView? srcData); - // void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, - // GLsizei depth, GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView srcData, - // GLuint srcOffset); - - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, - // GLsizei height, GLenum format, GLenum type, GLintptr pboOffset); - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, - // GLsizei height, GLenum format, GLenum type, - // TexImageSource source); // May throw DOMException - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, - // GLsizei height, GLenum format, GLenum type, [AllowShared] ArrayBufferView srcData, - // GLuint srcOffset); - - // void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - // GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, - // GLintptr pboOffset); - // void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - // GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, - // TexImageSource source); // May throw DOMException - // void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - // GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, - // [AllowShared] ArrayBufferView? srcData, optional GLuint srcOffset = 0); - - // void copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - // GLint x, GLint y, GLsizei width, GLsizei height); - - // void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, - // GLsizei height, GLint border, GLsizei imageSize, GLintptr offset); - // void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, - // GLsizei height, GLint border, [AllowShared] ArrayBufferView srcData, - // optional GLuint srcOffset = 0, optional GLuint srcLengthOverride = 0); - - // void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, - // GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLintptr offset); - // void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, - // GLsizei height, GLsizei depth, GLint border, [AllowShared] ArrayBufferView srcData, - // optional GLuint srcOffset = 0, optional GLuint srcLengthOverride = 0); - - // void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLintptr offset); - // void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, GLenum format, - // [AllowShared] ArrayBufferView srcData, - // optional GLuint srcOffset = 0, - // optional GLuint srcLengthOverride = 0); - - // void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, - // GLenum format, GLsizei imageSize, GLintptr offset); - // void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, - // GLenum format, [AllowShared] ArrayBufferView srcData, - // optional GLuint srcOffset = 0, - // optional GLuint srcLengthOverride = 0); + //[Throws] + //void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLsizei depth, GLint border, GLenum format, GLenum type, GLintptr pboOffset); + //[Throws] + //void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLsizei depth, GLint border, GLenum format, GLenum type, + // TexImageSource source); // May throw DOMException + //[Throws] + //void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLsizei depth, GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView? srcData); + //[Throws] + //void texImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLsizei depth, GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView srcData, + // GLuint srcOffset); + + //[Throws] + //void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + // GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, + // GLintptr pboOffset); + //[Throws] + //void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + // GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, + // TexImageSource source); // May throw DOMException + //[Throws] + //void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + // GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, + // [AllowShared] ArrayBufferView? srcData, optional GLuint srcOffset = 0); + + //void copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + // GLint x, GLint y, GLsizei width, GLsizei height); + + //void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, + // GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLintptr offset); + //void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, + // GLsizei height, GLsizei depth, GLint border, [AllowShared] ArrayBufferView srcData, + // optional GLuint srcOffset = 0, optional GLuint srcLengthOverride = 0); + + //void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, + // GLenum format, GLsizei imageSize, GLintptr offset); + //void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, + // GLenum format, [AllowShared] ArrayBufferView srcData, + // optional GLuint srcOffset = 0, + // optional GLuint srcLengthOverride = 0); /* Programs and shaders */ [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram program, DOMString name); @@ -463,17 +407,6 @@ interface mixin WebGL2RenderingContextBase void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount); void drawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset); - /* Reading back pixels */ - // WebGL1: - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, - /*[AllowShared]*/ ArrayBufferView? dstData); - // WebGL2: - void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, - GLintptr offset); - void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, - /*[AllowShared]*/ ArrayBufferView dstData, GLuint dstOffset); - /* Multiple Render Targets */ void drawBuffers(sequence<GLenum> buffers); @@ -542,9 +475,114 @@ interface mixin WebGL2RenderingContextBase void bindVertexArray(WebGLVertexArrayObject? array); }; +interface mixin WebGL2RenderingContextOverloads +{ + // WebGL1: + void bufferData(GLenum target, GLsizeiptr size, GLenum usage); + void bufferData(GLenum target, /*[AllowShared]*/ BufferSource? srcData, GLenum usage); + void bufferSubData(GLenum target, GLintptr dstByteOffset, /*[AllowShared]*/ BufferSource srcData); + // WebGL2: + void bufferData(GLenum target, /*[AllowShared]*/ ArrayBufferView srcData, GLenum usage, GLuint srcOffset, + optional GLuint length = 0); + void bufferSubData(GLenum target, GLintptr dstByteOffset, /*[AllowShared]*/ ArrayBufferView srcData, + GLuint srcOffset, optional GLuint length = 0); + + // WebGL1 legacy entrypoints: + [Throws] + void texImage2D(GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); + [Throws] + void texImage2D(GLenum target, GLint level, GLint internalformat, + GLenum format, GLenum type, TexImageSource source); // May throw DOMException + + [Throws] + void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); + [Throws] + void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLenum format, GLenum type, TexImageSource source); // May throw DOMException + + // WebGL2 entrypoints: + //[Throws] + //void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLint border, GLenum format, GLenum type, GLintptr pboOffset); + //[Throws] + //void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLint border, GLenum format, GLenum type, + // TexImageSource source); // May throw DOMException + //[Throws] + //void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, + // GLint border, GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView srcData, + // GLuint srcOffset); + + //[Throws] + //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + // GLsizei height, GLenum format, GLenum type, GLintptr pboOffset); + //[Throws] + //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + // GLsizei height, GLenum format, GLenum type, + // TexImageSource source); // May throw DOMException + //[Throws] + //void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + // GLsizei height, GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView srcData, + // GLuint srcOffset); + + //void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, + // GLsizei height, GLint border, GLsizei imageSize, GLintptr offset); + void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, + GLsizei height, GLint border, /*[AllowShared]*/ ArrayBufferView srcData, + optional GLuint srcOffset = 0, optional GLuint srcLengthOverride = 0); + + //void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLintptr offset); + void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, + /*[AllowShared]*/ ArrayBufferView srcData, + optional GLuint srcOffset = 0, + optional GLuint srcLengthOverride = 0); + + void uniform1fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + void uniform2fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + void uniform3fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + void uniform4fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + + void uniform1iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + void uniform2iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + void uniform3iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + void uniform4iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, + optional GLuint srcLength = 0); + + void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + + /* Reading back pixels */ + // WebGL1: + void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + /*[AllowShared]*/ ArrayBufferView? dstData); + // WebGL2: + void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + GLintptr offset); + void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + /*[AllowShared]*/ ArrayBufferView dstData, GLuint dstOffset); +}; + [Exposed=Window, Func="WebGL2RenderingContext::is_webgl2_enabled"] interface WebGL2RenderingContext { }; WebGL2RenderingContext includes WebGLRenderingContextBase; WebGL2RenderingContext includes WebGL2RenderingContextBase; +WebGL2RenderingContext includes WebGL2RenderingContextOverloads; diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index a9af389b042..ac00757ef3f 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -421,8 +421,6 @@ interface mixin WebGLRenderingContextBase const GLenum RGB5_A1 = 0x8057; const GLenum RGB565 = 0x8D62; const GLenum DEPTH_COMPONENT16 = 0x81A5; - // https://github.com/KhronosGroup/WebGL/pull/2371 - // const GLenum STENCIL_INDEX = 0x1901; const GLenum STENCIL_INDEX8 = 0x8D48; const GLenum DEPTH_STENCIL = 0x84F9; @@ -492,11 +490,6 @@ interface mixin WebGLRenderingContextBase void blendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - // void bufferData(GLenum target, object? data, GLenum usage); - // void bufferData(GLenum target, GLsizeiptr size, GLenum usage); - // void bufferSubData(GLenum target, GLintptr offset, /*[AllowShared]*/ BufferSource data); - [WebGLHandlesContextLoss] GLenum checkFramebufferStatus(GLenum target); void clear(GLbitfield mask); void clearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); @@ -505,14 +498,6 @@ interface mixin WebGLRenderingContextBase void colorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); void compileShader(WebGLShader shader); - void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, - /*[AllowShared]*/ ArrayBufferView data); - void compressedTexSubImage2D(GLenum target, GLint level, - GLint xoffset, GLint yoffset, - GLsizei width, GLsizei height, GLenum format, - /*[AllowShared]*/ ArrayBufferView data); - void copyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); @@ -602,10 +587,6 @@ interface mixin WebGLRenderingContextBase void pixelStorei(GLenum pname, GLint param); void polygonOffset(GLfloat factor, GLfloat units); - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - // void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, - // GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); - void renderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); void sampleCoverage(GLclampf value, GLboolean invert); @@ -620,26 +601,9 @@ interface mixin WebGLRenderingContextBase void stencilOp(GLenum fail, GLenum zfail, GLenum zpass); void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - // void texImage2D(GLenum target, GLint level, GLenum internalformat, - // GLsizei width, GLsizei height, GLint border, GLenum format, - // GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); - // void texImage2D(GLenum target, GLint level, GLenum internalformat, - // GLenum format, GLenum type, TexImageSource source); // May throw DOMException - [Throws, Pref="dom.webgl.dom_to_texture.enabled"] - void texImageDOM(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, - GLenum format, GLenum type, HTMLIFrameElement source); // May throw DOMException - void texParameterf(GLenum target, GLenum pname, GLfloat param); void texParameteri(GLenum target, GLenum pname, GLint param); - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, - // GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLenum format, GLenum type, TexImageSource source); // May throw DOMException - void uniform1f(WebGLUniformLocation? location, GLfloat x); void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y); void uniform3f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z); @@ -650,31 +614,6 @@ interface mixin WebGLRenderingContextBase void uniform3i(WebGLUniformLocation? location, GLint x, GLint y, GLint z); void uniform4i(WebGLUniformLocation? location, GLint x, GLint y, GLint z, GLint w); - void uniform1fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - void uniform2fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - void uniform3fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - void uniform4fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - - void uniform1iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - void uniform2iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - void uniform3iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - void uniform4iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, - optional GLuint srcLength = 0); - - void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - void useProgram(WebGLProgram? program); void validateProgram(WebGLProgram program); @@ -694,34 +633,64 @@ interface mixin WebGLRenderingContextBase void viewport(GLint x, GLint y, GLsizei width, GLsizei height); }; -[Exposed=Window] -interface WebGLRenderingContext +interface mixin WebGLRenderingContextOverloads { - // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - - void bufferData(GLenum target, /*[AllowShared]*/ BufferSource? data, GLenum usage); void bufferData(GLenum target, GLsizeiptr size, GLenum usage); + void bufferData(GLenum target, /*[AllowShared]*/ BufferSource? data, GLenum usage); void bufferSubData(GLenum target, GLintptr offset, /*[AllowShared]*/ BufferSource data); - // FIXME: https://github.com/servo/servo/issues/20516 + void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + /*[AllowShared]*/ ArrayBufferView data); + void compressedTexSubImage2D(GLenum target, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, + /*[AllowShared]*/ ArrayBufferView data); + + void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); + [Throws] - void texImage2D(GLenum target, GLint level, GLenum internalformat, + void texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); [Throws] - void texImage2D(GLenum target, GLint level, GLenum internalformat, + void texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, TexImageSource source); // May throw DOMException - // FIXME: https://github.com/servo/servo/issues/20516 [Throws] void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLsizei width, GLsizei height, - GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); + GLsizei width, GLsizei height, + GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); [Throws] void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, TexImageSource source); // May throw DOMException - void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? pixels); + void uniform1fv(WebGLUniformLocation? location, Float32List v); + void uniform2fv(WebGLUniformLocation? location, Float32List v); + void uniform3fv(WebGLUniformLocation? location, Float32List v); + void uniform4fv(WebGLUniformLocation? location, Float32List v); + + void uniform1iv(WebGLUniformLocation? location, Int32List v); + void uniform2iv(WebGLUniformLocation? location, Int32List v); + void uniform3iv(WebGLUniformLocation? location, Int32List v); + void uniform4iv(WebGLUniformLocation? location, Int32List v); + + void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value); + void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value); + void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value); +}; + +interface mixin WebGLRenderingContextExtensions { + [Throws, Pref="dom.webgl.dom_to_texture.enabled"] + void texImageDOM(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLenum format, GLenum type, HTMLIFrameElement source); // May throw DOMException +}; + +[Exposed=(Window)] +interface WebGLRenderingContext +{ }; WebGLRenderingContext includes WebGLRenderingContextBase; +WebGLRenderingContext includes WebGLRenderingContextOverloads; +WebGLRenderingContext includes WebGLRenderingContextExtensions; diff --git a/components/script/dom/webidls/XRFrame.webidl b/components/script/dom/webidls/XRFrame.webidl index 3c202d5e061..e7a4eef1ddd 100644 --- a/components/script/dom/webidls/XRFrame.webidl +++ b/components/script/dom/webidls/XRFrame.webidl @@ -10,5 +10,6 @@ interface XRFrame { [Throws] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace); [Throws] XRPose? getPose(XRSpace space, XRSpace relativeTo); + [Pref="dom.webxr.hands.enabled", Throws] XRJointPose? getJointPose(XRJointSpace space, XRSpace relativeTo); sequence<XRHitTestResult> getHitTestResults(XRHitTestSource hitTestSource); }; diff --git a/components/script/dom/webidls/XRHand.webidl b/components/script/dom/webidls/XRHand.webidl new file mode 100644 index 00000000000..cff30268ba6 --- /dev/null +++ b/components/script/dom/webidls/XRHand.webidl @@ -0,0 +1,41 @@ +/* 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://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md + +[SecureContext, Exposed=Window, Pref="dom.webxr.hands.enabled"] +interface XRHand { + readonly attribute long length; + getter XRJointSpace(unsigned long index); + + const unsigned long WRIST = 0; + const unsigned long THUMB_METACARPAL = 1; + const unsigned long THUMB_PHALANX_PROXIMAL = 2; + const unsigned long THUMB_PHALANX_DISTAL = 3; + const unsigned long THUMB_PHALANX_TIP = 4; + + const unsigned long INDEX_METACARPAL = 5; + const unsigned long INDEX_PHALANX_PROXIMAL = 6; + const unsigned long INDEX_PHALANX_INTERMEDIATE = 7; + const unsigned long INDEX_PHALANX_DISTAL = 8; + const unsigned long INDEX_PHALANX_TIP = 9; + + const unsigned long MIDDLE_METACARPAL = 10; + const unsigned long MIDDLE_PHALANX_PROXIMAL = 11; + const unsigned long MIDDLE_PHALANX_INTERMEDIATE = 12; + const unsigned long MIDDLE_PHALANX_DISTAL = 13; + const unsigned long MIDDLE_PHALANX_TIP = 14; + + const unsigned long RING_METACARPAL = 15; + const unsigned long RING_PHALANX_PROXIMAL = 16; + const unsigned long RING_PHALANX_INTERMEDIATE = 17; + const unsigned long RING_PHALANX_DISTAL = 18; + const unsigned long RING_PHALANX_TIP = 19; + + const unsigned long LITTLE_METACARPAL = 20; + const unsigned long LITTLE_PHALANX_PROXIMAL = 21; + const unsigned long LITTLE_PHALANX_INTERMEDIATE = 22; + const unsigned long LITTLE_PHALANX_DISTAL = 23; + const unsigned long LITTLE_PHALANX_TIP = 24; +}; diff --git a/components/script/dom/webidls/XRInputSource.webidl b/components/script/dom/webidls/XRInputSource.webidl index 487c959859d..b663666c872 100644 --- a/components/script/dom/webidls/XRInputSource.webidl +++ b/components/script/dom/webidls/XRInputSource.webidl @@ -24,4 +24,7 @@ interface XRInputSource { [SameObject] readonly attribute XRSpace? gripSpace; // [SameObject] readonly attribute Gamepad? gamepad; /* [SameObject] */ readonly attribute /* FrozenArray<DOMString> */ any profiles; + + [Pref="dom.webxr.hands.enabled"] + readonly attribute XRHand? hand; }; diff --git a/components/script/dom/webidls/XRJointPose.webidl b/components/script/dom/webidls/XRJointPose.webidl new file mode 100644 index 00000000000..70750b66cc4 --- /dev/null +++ b/components/script/dom/webidls/XRJointPose.webidl @@ -0,0 +1,10 @@ +/* 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://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md + +[SecureContext, Exposed=Window, Pref="dom.webxr.hands.enabled"] +interface XRJointPose: XRPose { + readonly attribute float? radius; +}; diff --git a/components/script/dom/webidls/XRJointSpace.webidl b/components/script/dom/webidls/XRJointSpace.webidl new file mode 100644 index 00000000000..0815a73ec58 --- /dev/null +++ b/components/script/dom/webidls/XRJointSpace.webidl @@ -0,0 +1,8 @@ +/* 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://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md + +[SecureContext, Exposed=Window, Pref="dom.webxr.hands.enabled"] +interface XRJointSpace: XRSpace {}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 9f37ec2240a..c23efc961ee 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -2008,15 +2008,21 @@ impl Window { // Step 8 if doc.prompt_to_unload(false) { - if self.window_proxy().parent().is_some() { + let window_proxy = self.window_proxy(); + if window_proxy.parent().is_some() { // Step 10 // If browsingContext is a nested browsing context, // then put it in the delaying load events mode. - self.window_proxy().start_delaying_load_events_mode(); + window_proxy.start_delaying_load_events_mode(); } // TODO: step 11, navigationType. // Step 12, 13 - ScriptThread::navigate(pipeline_id, load_data, replace); + ScriptThread::navigate( + window_proxy.browsing_context_id(), + pipeline_id, + load_data, + replace, + ); }; } diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index d040d7d7468..c9555c88517 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -20,6 +20,7 @@ use crate::dom::dedicatedworkerglobalscope::{ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::messageevent::MessageEvent; +use crate::dom::window::Window; use crate::dom::workerglobalscope::prepare_workerscope_init; use crate::realms::enter_realm; use crate::script_runtime::JSContext; @@ -95,23 +96,34 @@ impl Worker { pipeline_id: global.pipeline_id(), }; + let browsing_context = global + .downcast::<Window>() + .map(|w| w.window_proxy().browsing_context_id()) + .or_else(|| { + global + .downcast::<DedicatedWorkerGlobalScope>() + .and_then(|w| w.browsing_context()) + }); + let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); let worker_id = WorkerId(Uuid::new_v4()); if let Some(ref chan) = global.devtools_chan() { let pipeline_id = global.pipeline_id(); let title = format!("Worker for {}", worker_url); - let page_info = DevtoolsPageInfo { - title: title, - url: worker_url.clone(), - }; - let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - (pipeline_id, Some(worker_id)), - devtools_sender.clone(), - page_info, - )); + if let Some(browsing_context) = browsing_context { + let page_info = DevtoolsPageInfo { + title: title, + url: worker_url.clone(), + }; + let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( + (browsing_context, pipeline_id, Some(worker_id)), + devtools_sender.clone(), + page_info, + )); + } } - let init = prepare_workerscope_init(global, Some(devtools_sender)); + let init = prepare_workerscope_init(global, Some(devtools_sender), Some(worker_id)); DedicatedWorkerGlobalScope::run_worker_scope( init, @@ -126,6 +138,7 @@ impl Worker { worker_options.type_, closing, global.image_cache(), + browsing_context, ); Ok(worker) diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 15eb79858fe..684760c3197 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -62,6 +62,7 @@ use uuid::Uuid; pub fn prepare_workerscope_init( global: &GlobalScope, devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>, + worker_id: Option<WorkerId>, ) -> WorkerGlobalScopeInit { let init = WorkerGlobalScopeInit { resource_threads: global.resource_threads().clone(), @@ -71,7 +72,7 @@ pub fn prepare_workerscope_init( from_devtools_sender: devtools_sender, script_to_constellation_chan: global.script_to_constellation_chan().clone(), scheduler_chan: global.scheduler_chan().clone(), - worker_id: WorkerId(Uuid::new_v4()), + worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())), pipeline_id: global.pipeline_id(), origin: global.origin().immutable().clone(), is_headless: global.is_headless(), diff --git a/components/script/dom/xrframe.rs b/components/script/dom/xrframe.rs index fdafd932e8a..1526a1c387c 100644 --- a/components/script/dom/xrframe.rs +++ b/components/script/dom/xrframe.rs @@ -10,6 +10,8 @@ use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::globalscope::GlobalScope; use crate::dom::xrhittestresult::XRHitTestResult; use crate::dom::xrhittestsource::XRHitTestSource; +use crate::dom::xrjointpose::XRJointPose; +use crate::dom::xrjointspace::XRJointSpace; use crate::dom::xrpose::XRPose; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrsession::{ApiPose, XRSession}; @@ -112,6 +114,38 @@ impl XRFrameMethods for XRFrame { Ok(Some(XRPose::new(&self.global(), pose))) } + /// https://immersive-web.github.io/webxr/#dom-xrframe-getpose + fn GetJointPose( + &self, + space: &XRJointSpace, + relative_to: &XRSpace, + ) -> Result<Option<DomRoot<XRJointPose>>, Error> { + if self.session != space.upcast::<XRSpace>().session() || + self.session != relative_to.session() + { + return Err(Error::InvalidState); + } + if !self.active.get() { + return Err(Error::InvalidState); + } + let joint_frame = if let Some(frame) = space.frame(&self.data) { + frame + } else { + return Ok(None); + }; + let relative_to = if let Some(r) = self.get_pose(relative_to) { + r + } else { + return Ok(None); + }; + let pose = relative_to.inverse().pre_transform(&joint_frame.pose); + Ok(Some(XRJointPose::new( + &self.global(), + pose.cast_unit(), + Some(joint_frame.radius), + ))) + } + /// https://immersive-web.github.io/hit-test/#dom-xrframe-gethittestresults fn GetHitTestResults(&self, source: &XRHitTestSource) -> Vec<DomRoot<XRHitTestResult>> { self.data diff --git a/components/script/dom/xrhand.rs b/components/script/dom/xrhand.rs new file mode 100644 index 00000000000..a7457526242 --- /dev/null +++ b/components/script/dom/xrhand.rs @@ -0,0 +1,88 @@ +/* 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::XRHandBinding::{XRHandConstants, XRHandMethods}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::xrinputsource::XRInputSource; +use crate::dom::xrjointspace::XRJointSpace; +use dom_struct::dom_struct; +use webxr_api::{FingerJoint, Hand, Joint}; + +#[dom_struct] +pub struct XRHand { + reflector_: Reflector, + #[ignore_malloc_size_of = "defined in webxr"] + source: Dom<XRInputSource>, + #[ignore_malloc_size_of = "partially defind in webxr"] + spaces: Hand<Dom<XRJointSpace>>, +} + +impl XRHand { + fn new_inherited(source: &XRInputSource, spaces: &Hand<DomRoot<XRJointSpace>>) -> XRHand { + XRHand { + reflector_: Reflector::new(), + source: Dom::from_ref(source), + spaces: spaces.map(|j, _| j.as_ref().map(|j| Dom::from_ref(&**j))), + } + } + + pub fn new(global: &GlobalScope, source: &XRInputSource, support: Hand<()>) -> DomRoot<XRHand> { + let id = source.id(); + let session = source.session(); + let spaces = support + .map(|field, joint| field.map(|_| XRJointSpace::new(global, session, id, joint))); + reflect_dom_object(Box::new(XRHand::new_inherited(source, &spaces)), global) + } +} + +impl XRHandMethods for XRHand { + /// https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md + fn Length(&self) -> i32 { + XRHandConstants::LITTLE_PHALANX_TIP as i32 + 1 + } + + /// https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md + fn IndexedGetter(&self, joint_index: u32) -> Option<DomRoot<XRJointSpace>> { + let joint = match joint_index { + XRHandConstants::WRIST => Joint::Wrist, + XRHandConstants::THUMB_METACARPAL => Joint::ThumbMetacarpal, + XRHandConstants::THUMB_PHALANX_PROXIMAL => Joint::ThumbPhalanxProximal, + XRHandConstants::THUMB_PHALANX_DISTAL => Joint::ThumbPhalanxDistal, + XRHandConstants::THUMB_PHALANX_TIP => Joint::ThumbPhalanxTip, + XRHandConstants::INDEX_METACARPAL => Joint::Index(FingerJoint::Metacarpal), + XRHandConstants::INDEX_PHALANX_PROXIMAL => Joint::Index(FingerJoint::PhalanxProximal), + XRHandConstants::INDEX_PHALANX_INTERMEDIATE => { + Joint::Index(FingerJoint::PhalanxIntermediate) + }, + XRHandConstants::INDEX_PHALANX_DISTAL => Joint::Index(FingerJoint::PhalanxDistal), + XRHandConstants::INDEX_PHALANX_TIP => Joint::Index(FingerJoint::PhalanxTip), + XRHandConstants::MIDDLE_METACARPAL => Joint::Middle(FingerJoint::Metacarpal), + XRHandConstants::MIDDLE_PHALANX_PROXIMAL => Joint::Middle(FingerJoint::PhalanxProximal), + XRHandConstants::MIDDLE_PHALANX_INTERMEDIATE => { + Joint::Middle(FingerJoint::PhalanxIntermediate) + }, + XRHandConstants::MIDDLE_PHALANX_DISTAL => Joint::Middle(FingerJoint::PhalanxDistal), + XRHandConstants::MIDDLE_PHALANX_TIP => Joint::Middle(FingerJoint::PhalanxTip), + XRHandConstants::RING_METACARPAL => Joint::Ring(FingerJoint::Metacarpal), + XRHandConstants::RING_PHALANX_PROXIMAL => Joint::Ring(FingerJoint::PhalanxProximal), + XRHandConstants::RING_PHALANX_INTERMEDIATE => { + Joint::Ring(FingerJoint::PhalanxIntermediate) + }, + XRHandConstants::RING_PHALANX_DISTAL => Joint::Ring(FingerJoint::PhalanxDistal), + XRHandConstants::RING_PHALANX_TIP => Joint::Ring(FingerJoint::PhalanxTip), + XRHandConstants::LITTLE_METACARPAL => Joint::Little(FingerJoint::Metacarpal), + XRHandConstants::LITTLE_PHALANX_PROXIMAL => Joint::Little(FingerJoint::PhalanxProximal), + XRHandConstants::LITTLE_PHALANX_INTERMEDIATE => { + Joint::Little(FingerJoint::PhalanxIntermediate) + }, + XRHandConstants::LITTLE_PHALANX_DISTAL => Joint::Little(FingerJoint::PhalanxDistal), + XRHandConstants::LITTLE_PHALANX_TIP => Joint::Little(FingerJoint::PhalanxTip), + // XXXManishearth should this be a TypeError? + _ => return None, + }; + self.spaces.get(joint).map(|j| DomRoot::from_ref(&**j)) + } +} diff --git a/components/script/dom/xrinputsource.rs b/components/script/dom/xrinputsource.rs index ed8a3e6f1a5..248fb6d737a 100644 --- a/components/script/dom/xrinputsource.rs +++ b/components/script/dom/xrinputsource.rs @@ -8,6 +8,7 @@ use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{ 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::xrhand::XRHand; use crate::dom::xrsession::XRSession; use crate::dom::xrspace::XRSpace; use crate::realms::enter_realm; @@ -24,10 +25,9 @@ pub struct XRInputSource { session: Dom<XRSession>, #[ignore_malloc_size_of = "Defined in rust-webxr"] info: InputSource, - #[ignore_malloc_size_of = "Defined in rust-webxr"] target_ray_space: MutNullableDom<XRSpace>, - #[ignore_malloc_size_of = "Defined in rust-webxr"] grip_space: MutNullableDom<XRSpace>, + hand: MutNullableDom<XRHand>, #[ignore_malloc_size_of = "mozjs"] profiles: Heap<JSVal>, } @@ -40,6 +40,7 @@ impl XRInputSource { info, target_ray_space: Default::default(), grip_space: Default::default(), + hand: Default::default(), profiles: Heap::default(), } } @@ -68,6 +69,10 @@ impl XRInputSource { pub fn id(&self) -> InputId { self.info.id } + + pub fn session(&self) -> &XRSession { + &self.session + } } impl XRInputSourceMethods for XRInputSource { @@ -112,4 +117,16 @@ impl XRInputSourceMethods for XRInputSource { fn Profiles(&self, _cx: JSContext) -> JSVal { self.profiles.get() } + + // https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md + fn GetHand(&self) -> Option<DomRoot<XRHand>> { + if let Some(ref hand) = self.info.hand_support { + Some( + self.hand + .or_init(|| XRHand::new(&self.global(), &self, hand.clone())), + ) + } else { + None + } + } } diff --git a/components/script/dom/xrjointpose.rs b/components/script/dom/xrjointpose.rs new file mode 100644 index 00000000000..c4b610b3327 --- /dev/null +++ b/components/script/dom/xrjointpose.rs @@ -0,0 +1,48 @@ +/* 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::XRJointPoseBinding::XRJointPoseMethods; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::dom::xrpose::XRPose; +use crate::dom::xrrigidtransform::XRRigidTransform; +use crate::dom::xrsession::ApiRigidTransform; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct XRJointPose { + pose: XRPose, + radius: Option<f32>, +} + +impl XRJointPose { + fn new_inherited(transform: &XRRigidTransform, radius: Option<f32>) -> XRJointPose { + XRJointPose { + pose: XRPose::new_inherited(transform), + radius, + } + } + + #[allow(unsafe_code)] + pub fn new( + global: &GlobalScope, + pose: ApiRigidTransform, + radius: Option<f32>, + ) -> DomRoot<XRJointPose> { + let transform = XRRigidTransform::new(global, pose); + reflect_dom_object( + Box::new(XRJointPose::new_inherited(&transform, radius)), + global, + ) + } +} + +impl XRJointPoseMethods for XRJointPose { + /// https://immersive-web.github.io/webxr/#dom-XRJointPose-views + fn GetRadius(&self) -> Option<Finite<f32>> { + self.radius.map(Finite::wrap) + } +} diff --git a/components/script/dom/xrjointspace.rs b/components/script/dom/xrjointspace.rs new file mode 100644 index 00000000000..4b31b0bc6a2 --- /dev/null +++ b/components/script/dom/xrjointspace.rs @@ -0,0 +1,60 @@ +/* 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::reflector::reflect_dom_object; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::dom::xrsession::{ApiPose, XRSession}; +use crate::dom::xrspace::XRSpace; +use dom_struct::dom_struct; +use euclid::RigidTransform3D; +use webxr_api::{BaseSpace, Frame, InputId, Joint, JointFrame, Space}; + +#[dom_struct] +pub struct XRJointSpace { + xrspace: XRSpace, + #[ignore_malloc_size_of = "defined in rust-webxr"] + input: InputId, + #[ignore_malloc_size_of = "defined in rust-webxr"] + joint: Joint, +} + +impl XRJointSpace { + pub fn new_inherited(session: &XRSession, input: InputId, joint: Joint) -> XRJointSpace { + XRJointSpace { + xrspace: XRSpace::new_inherited(session), + input, + joint, + } + } + + #[allow(unused)] + pub fn new( + global: &GlobalScope, + session: &XRSession, + input: InputId, + joint: Joint, + ) -> DomRoot<XRJointSpace> { + reflect_dom_object(Box::new(Self::new_inherited(session, input, joint)), global) + } + + pub fn space(&self) -> Space { + let base = BaseSpace::Joint(self.input, self.joint); + let offset = RigidTransform3D::identity(); + Space { base, offset } + } + + pub fn frame<'a>(&self, frame: &'a Frame) -> Option<&'a JointFrame> { + frame + .inputs + .iter() + .find(|i| i.id == self.input) + .and_then(|i| i.hand.as_ref()) + .and_then(|h| h.get(self.joint)) + } + + pub fn get_pose(&self, frame: &Frame) -> Option<ApiPose> { + self.frame(frame).map(|f| f.pose).map(|t| t.cast_unit()) + } +} diff --git a/components/script/dom/xrspace.rs b/components/script/dom/xrspace.rs index b4a26b7b8bb..66dd5b44e70 100644 --- a/components/script/dom/xrspace.rs +++ b/components/script/dom/xrspace.rs @@ -8,6 +8,7 @@ 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::xrjointspace::XRJointSpace; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrsession::{cast_transform, ApiPose, XRSession}; use dom_struct::dom_struct; @@ -61,6 +62,8 @@ impl XRSpace { pub fn space(&self) -> Space { if let Some(rs) = self.downcast::<XRReferenceSpace>() { rs.space() + } else if let Some(j) = self.downcast::<XRJointSpace>() { + j.space() } else if let Some(source) = self.input_source.get() { let base = if self.is_grip_space { BaseSpace::Grip(source.id()) @@ -86,6 +89,8 @@ impl XRSpace { pub fn get_pose(&self, base_pose: &Frame) -> Option<ApiPose> { if let Some(reference) = self.downcast::<XRReferenceSpace>() { reference.get_pose(base_pose) + } else if let Some(joint) = self.downcast::<XRJointSpace>() { + joint.get_pose(base_pose) } else if let Some(source) = self.input_source.get() { // XXXManishearth we should be able to request frame information // for inputs when necessary instead of always loading it diff --git a/components/script/dom/xrsystem.rs b/components/script/dom/xrsystem.rs index 0fc4ab108e2..a2ba7946e62 100644 --- a/components/script/dom/xrsystem.rs +++ b/components/script/dom/xrsystem.rs @@ -267,7 +267,8 @@ impl XRSystem { ) { let session = match response { Ok(session) => session, - Err(_) => { + Err(e) => { + warn!("Error requesting XR session: {:?}", e); if mode != XRSessionMode::Inline { self.pending_immersive_session.set(false); } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index bd65780dc19..16fd866f1b9 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -93,7 +93,7 @@ use canvas_traits::webgl::WebGLPipeline; use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::CSSError; use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo}; -use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; +use devtools_traits::{NavigationState, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{EmbedderMsg, EventLoopWaker}; use euclid::default::{Point2D, Rect}; use euclid::Vector2D; @@ -133,17 +133,15 @@ use script_traits::CompositorEvent::{ CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent, WheelEvent, }; -use script_traits::StructuredSerializedData; -use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{ - DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement, + CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext, DocumentActivity, + EventResult, HistoryEntryReplacement, InitialScriptState, JsEvalResult, LayoutMsg, LoadData, + LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo, Painter, + ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory, ScriptToConstellationChan, + StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId, TransitionEventType, + UntrustedNodeAddress, UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData, + WindowSizeType, }; -use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData, LoadOrigin}; -use script_traits::{MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo}; -use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory}; -use script_traits::{ScriptToConstellationChan, TimerSchedulerMsg}; -use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta}; -use script_traits::{UpdatePipelineIdReason, WebrenderIpcSender, WindowSizeData, WindowSizeType}; use servo_atoms::Atom; use servo_config::opts; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; @@ -949,6 +947,7 @@ impl ScriptThread { /// Step 13 of https://html.spec.whatwg.org/multipage/#navigate pub fn navigate( + browsing_context: BrowsingContextId, pipeline_id: PipelineId, mut load_data: LoadData, replace: HistoryEntryReplacement, @@ -987,6 +986,12 @@ impl ScriptThread { .queue(task, global.upcast()) .expect("Enqueing navigate js task on the DOM manipulation task source failed"); } else { + if let Some(ref sender) = script_thread.devtools_chan { + let _ = sender.send(ScriptToDevtoolsControlMsg::Navigate( + browsing_context, NavigationState::Start(load_data.url.clone()) + )); + } + script_thread .script_sender .send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace))) @@ -1668,46 +1673,43 @@ impl ScriptThread { fn message_to_pipeline(&self, msg: &MixedMessage) -> Option<PipelineId> { use script_traits::ConstellationControlMsg::*; match *msg { - MixedMessage::FromConstellation(ref inner_msg) => { - match *inner_msg { - StopDelayingLoadEventsMode(id) => Some(id), - NavigationResponse(id, _) => Some(id), - AttachLayout(ref new_layout_info) => Some(new_layout_info.new_pipeline_id), - Resize(id, ..) => Some(id), - ResizeInactive(id, ..) => Some(id), - UnloadDocument(id) => Some(id), - ExitPipeline(id, ..) => Some(id), - ExitScriptThread => None, - SendEvent(id, ..) => Some(id), - Viewport(id, ..) => Some(id), - SetScrollState(id, ..) => Some(id), - GetTitle(id) => Some(id), - SetDocumentActivity(id, ..) => Some(id), - ChangeFrameVisibilityStatus(id, ..) => Some(id), - NotifyVisibilityChange(id, ..) => Some(id), - NavigateIframe(id, ..) => Some(id), - PostMessage { target: id, .. } => Some(id), - UpdatePipelineId(_, _, _, id, _) => Some(id), - UpdateHistoryState(id, ..) => Some(id), - RemoveHistoryStates(id, ..) => Some(id), - FocusIFrame(id, ..) => Some(id), - WebDriverScriptCommand(id, ..) => Some(id), - TickAllAnimations(id) => Some(id), - // FIXME https://github.com/servo/servo/issues/15079 - TransitionEnd(..) => None, - WebFontLoaded(id) => Some(id), - DispatchIFrameLoadEvent { - target: _, - parent: id, - child: _, - } => Some(id), - DispatchStorageEvent(id, ..) => Some(id), - ReportCSSError(id, ..) => Some(id), - Reload(id, ..) => Some(id), - PaintMetric(..) => None, - ExitFullScreen(id, ..) => Some(id), - MediaSessionAction(..) => None, - } + MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg { + StopDelayingLoadEventsMode(id) => Some(id), + NavigationResponse(id, _) => Some(id), + AttachLayout(ref new_layout_info) => Some(new_layout_info.new_pipeline_id), + Resize(id, ..) => Some(id), + ResizeInactive(id, ..) => Some(id), + UnloadDocument(id) => Some(id), + ExitPipeline(id, ..) => Some(id), + ExitScriptThread => None, + SendEvent(id, ..) => Some(id), + Viewport(id, ..) => Some(id), + SetScrollState(id, ..) => Some(id), + GetTitle(id) => Some(id), + SetDocumentActivity(id, ..) => Some(id), + ChangeFrameVisibilityStatus(id, ..) => Some(id), + NotifyVisibilityChange(id, ..) => Some(id), + NavigateIframe(id, ..) => Some(id), + PostMessage { target: id, .. } => Some(id), + UpdatePipelineId(_, _, _, id, _) => Some(id), + UpdateHistoryState(id, ..) => Some(id), + RemoveHistoryStates(id, ..) => Some(id), + FocusIFrame(id, ..) => Some(id), + WebDriverScriptCommand(id, ..) => Some(id), + TickAllAnimations(id) => Some(id), + TransitionEvent { .. } => None, + WebFontLoaded(id) => Some(id), + DispatchIFrameLoadEvent { + target: _, + parent: id, + child: _, + } => Some(id), + DispatchStorageEvent(id, ..) => Some(id), + ReportCSSError(id, ..) => Some(id), + Reload(id, ..) => Some(id), + PaintMetric(..) => None, + ExitFullScreen(id, ..) => Some(id), + MediaSessionAction(..) => None, }, MixedMessage::FromDevtools(_) => None, MixedMessage::FromScript(ref inner_msg) => match *inner_msg { @@ -1896,8 +1898,20 @@ impl ScriptThread { ConstellationControlMsg::TickAllAnimations(pipeline_id) => { self.handle_tick_all_animations(pipeline_id) }, - ConstellationControlMsg::TransitionEnd(unsafe_node, name, duration) => { - self.handle_transition_event(unsafe_node, name, duration) + ConstellationControlMsg::TransitionEvent { + pipeline_id, + event_type, + node, + property_name, + elapsed_time, + } => { + self.handle_transition_event( + pipeline_id, + event_type, + node, + property_name, + elapsed_time, + ); }, ConstellationControlMsg::WebFontLoaded(pipeline_id) => { self.handle_web_font_loaded(pipeline_id) @@ -2899,56 +2913,69 @@ impl ScriptThread { document.run_the_animation_frame_callbacks(); } - /// Handles firing of transition events. + /// Handles firing of transition-related events. + /// + /// TODO(mrobinson): Add support for more events. fn handle_transition_event( &self, + pipeline_id: PipelineId, + event_type: TransitionEventType, unsafe_node: UntrustedNodeAddress, - name: String, - duration: f64, + property_name: String, + elapsed_time: f64, ) { let js_runtime = self.js_runtime.rt(); let node = unsafe { from_untrusted_node_address(js_runtime, unsafe_node) }; - let idx = self + let node_index = self .transitioning_nodes .borrow() .iter() .position(|n| &**n as *const _ == &*node as *const _); - match idx { - Some(idx) => { - self.transitioning_nodes.borrow_mut().remove(idx); - }, + let node_index = match node_index { + Some(node_index) => node_index, None => { // If no index is found, we can't know whether this node is safe to use. // It's better not to fire a DOM event than crash. warn!("Ignoring transition end notification for unknown node."); return; }, - } - - let window = window_from_node(&*node); - - // Not quite the right thing - see #13865. - node.dirty(NodeDamage::NodeStyleDamaged); + }; - if let Some(el) = node.downcast::<Element>() { - if !el.has_css_layout_box() { - return; - } + if self.closed_pipelines.borrow().contains(&pipeline_id) { + warn!("Ignoring transition event for closed pipeline."); + return; } - let init = TransitionEventInit { + let event_atom = match event_type { + TransitionEventType::TransitionRun => atom!("transitionrun"), + TransitionEventType::TransitionEnd => { + // Not quite the right thing - see #13865. + node.dirty(NodeDamage::NodeStyleDamaged); + self.transitioning_nodes.borrow_mut().remove(node_index); + atom!("transitionend") + }, + TransitionEventType::TransitionCancel => { + self.transitioning_nodes.borrow_mut().remove(node_index); + atom!("transitioncancel") + }, + }; + + let event_init = TransitionEventInit { parent: EventInit { bubbles: true, cancelable: false, }, - propertyName: DOMString::from(name), - elapsedTime: Finite::new(duration as f32).unwrap(), - // FIXME: Handle pseudo-elements properly + propertyName: DOMString::from(property_name), + elapsedTime: Finite::new(elapsed_time as f32).unwrap(), + // TODO: Handle pseudo-elements properly pseudoElement: DOMString::new(), }; - let transition_event = TransitionEvent::new(&window, atom!("transitionend"), &init); - transition_event.upcast::<Event>().fire(node.upcast()); + + let window = window_from_node(&*node); + TransitionEvent::new(&window, event_atom, &event_init) + .upcast::<Event>() + .fire(node.upcast()); } /// Handles a Web font being loaded. Does nothing if the page no longer exists. @@ -3321,7 +3348,7 @@ impl ScriptThread { self.notify_devtools( document.Title(), final_url.clone(), - (incomplete.pipeline_id, None), + (incomplete.browsing_context_id, incomplete.pipeline_id, None), ); let parse_input = DOMString::new(); @@ -3352,7 +3379,7 @@ impl ScriptThread { &self, title: DOMString, url: ServoUrl, - ids: (PipelineId, Option<WorkerId>), + (bc, p, w): (BrowsingContextId, PipelineId, Option<WorkerId>), ) { if let Some(ref chan) = self.devtools_chan { let page_info = DevtoolsPageInfo { @@ -3360,11 +3387,14 @@ impl ScriptThread { url: url, }; chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - ids, + (bc, p, w), self.devtools_sender.clone(), - page_info, + page_info.clone(), )) .unwrap(); + + let state = NavigationState::Stop(p, page_info); + let _ = chan.send(ScriptToDevtoolsControlMsg::Navigate(bc, state)); } } diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs index 83bf1dbf312..73ead01fe17 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -11,7 +11,6 @@ use crate::dom::abstractworker::WorkerScriptMsg; use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg}; use crate::dom::serviceworkerregistration::longest_prefix_match; use crossbeam_channel::{unbounded, Receiver, RecvError, Sender}; -use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use net_traits::{CoreResourceMsg, CustomResponseMediator}; @@ -79,19 +78,7 @@ impl ServiceWorkerManager { let scope_things = self.registered_workers.get(&scope_url); if let Some(scope_things) = scope_things { let (sender, receiver) = unbounded(); - let (devtools_sender, devtools_receiver) = ipc::channel().unwrap(); - if let Some(ref chan) = scope_things.devtools_chan { - let title = format!("ServiceWorker for {}", scope_things.script_url); - let page_info = DevtoolsPageInfo { - title: title, - url: scope_things.script_url.clone(), - }; - let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - (scope_things.init.pipeline_id, Some(scope_things.worker_id)), - devtools_sender, - page_info, - )); - }; + let (_devtools_sender, devtools_receiver) = ipc::channel().unwrap(); ServiceWorkerGlobalScope::run_serviceworker_scope( scope_things.clone(), sender.clone(), diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 41056f3f6f2..3db883b6536 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -282,6 +282,18 @@ pub enum UpdatePipelineIdReason { Traversal, } +/// The type of transition event to trigger. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum TransitionEventType { + /// The transition has started running. + TransitionRun, + /// The transition has ended by reaching the end of its animation. + TransitionEnd, + /// The transition ended early for some reason, such as the property + /// no longer being transitionable or being replaced by another transition. + TransitionCancel, +} + /// Messages sent from the constellation or layout to the script thread. #[derive(Deserialize, Serialize)] pub enum ConstellationControlMsg { @@ -368,8 +380,19 @@ pub enum ConstellationControlMsg { WebDriverScriptCommand(PipelineId, WebDriverScriptCommand), /// Notifies script thread that all animations are done TickAllAnimations(PipelineId), - /// Notifies the script thread of a transition end - TransitionEnd(UntrustedNodeAddress, String, f64), + /// Notifies the script thread that a transition related event should be sent. + TransitionEvent { + /// The pipeline id of the layout task that sent this message. + pipeline_id: PipelineId, + /// The type of transition event this should trigger. + event_type: TransitionEventType, + /// The address of the node which owns this transition. + node: UntrustedNodeAddress, + /// The property name of the property that is transitioning. + property_name: String, + /// The elapsed time property to send with this transition event. + elapsed_time: f64, + }, /// Notifies the script thread that a new Web font has been loaded, and thus the page should be /// reflowed. WebFontLoaded(PipelineId), @@ -429,7 +452,7 @@ impl fmt::Debug for ConstellationControlMsg { FocusIFrame(..) => "FocusIFrame", WebDriverScriptCommand(..) => "WebDriverScriptCommand", TickAllAnimations(..) => "TickAllAnimations", - TransitionEnd(..) => "TransitionEnd", + TransitionEvent { .. } => "TransitionEvent", WebFontLoaded(..) => "WebFontLoaded", DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent", DispatchStorageEvent(..) => "DispatchStorageEvent", diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 59f231e11b3..f3951107703 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -19,7 +19,7 @@ doctest = false gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"] servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url", - "string_cache", "crossbeam-channel", "to_shmem/servo", + "string_cache", "to_shmem/servo", "servo_arc/servo"] servo-layout-2013 = [] servo-layout-2020 = [] @@ -34,7 +34,6 @@ atomic_refcell = "0.1" bitflags = "1.0" byteorder = "1.0" cssparser = "0.27" -crossbeam-channel = { version = "0.4", optional = true } derive_more = "0.99" new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} diff --git a/components/style/README.md b/components/style/README.md index 70670a5f96b..96457e1b30f 100644 --- a/components/style/README.md +++ b/components/style/README.md @@ -1,6 +1,6 @@ servo-style =========== -Style system for Servo, using [rust-cssparser](https://github.com/mozilla-servo/rust-cssparser) for parsing. +Style system for Servo, using [rust-cssparser](https://github.com/servo/rust-cssparser) for parsing. * [Documentation](https://github.com/servo/servo/blob/master/docs/components/style.md). diff --git a/components/style/animation.rs b/components/style/animation.rs index b82d196fa73..c497bd9414b 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -5,16 +5,16 @@ //! CSS transitions and animations. // NOTE(emilio): This code isn't really executed in Gecko, but we don't want to -// compile it out so that people remember it exists, thus the cfg'd Sender -// import. +// compile it out so that people remember it exists. use crate::bezier::Bezier; use crate::context::SharedStyleContext; use crate::dom::{OpaqueNode, TElement}; use crate::font_metrics::FontMetricsProvider; -use crate::properties::animated_properties::{AnimatedProperty, TransitionPropertyIteration}; +use crate::properties::animated_properties::AnimatedProperty; use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; +use crate::properties::LonghandIdSet; use crate::properties::{self, CascadeMode, ComputedValues, LonghandId}; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; use crate::stylesheets::Origin; @@ -24,12 +24,8 @@ use crate::values::computed::TimingFunction; use crate::values::generics::box_::AnimationIterationCount; use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction}; use crate::Atom; -#[cfg(feature = "servo")] -use crossbeam_channel::Sender; use servo_arc::Arc; use std::fmt; -#[cfg(feature = "gecko")] -use std::sync::mpsc::Sender; /// This structure represents a keyframes animation current iteration state. /// @@ -245,6 +241,17 @@ impl Animation { Animation::Keyframes(..) => false, } } + + /// Whether this animation has the same end value as another one. + #[inline] + pub fn is_transition_with_same_end_value(&self, other_animation: &PropertyAnimation) -> bool { + match *self { + Animation::Transition(_, _, ref animation) => { + animation.has_the_same_end_value_as(other_animation) + }, + Animation::Keyframes(..) => false, + } + } } /// Represents an animation for a given property. @@ -261,6 +268,11 @@ pub struct PropertyAnimation { } impl PropertyAnimation { + /// Returns the given property longhand id. + pub fn property_id(&self) -> LonghandId { + self.property.id() + } + /// Returns the given property name. pub fn property_name(&self) -> &'static str { self.property.name() @@ -351,20 +363,200 @@ impl PropertyAnimation { } } -/// Inserts transitions into the queue of running animations as applicable for -/// the given style difference. This is called from the layout worker threads. -/// Returns true if any animations were kicked off and false otherwise. +/// Holds the animation state for a particular element. +#[derive(Default)] +pub struct ElementAnimationState { + /// The animations running for this element. + pub running_animations: Vec<Animation>, + + /// The animations that have finished for this element, but not canceled. These are cleared + /// upon triggering a DOM event for each finished animation. + pub finished_animations: Vec<Animation>, + + /// The animations that have been cancelled for this element. These are cleared + /// upon triggering a DOM event for each cancelled animation. + pub cancelled_animations: Vec<Animation>, + + /// New animations created for this element. + pub new_animations: Vec<Animation>, +} + +impl ElementAnimationState { + /// Cancel all animations in this `ElementAnimationState`. This is typically called + /// when the element has been removed from the DOM. + pub fn cancel_all_animations(&mut self) { + self.cancelled_animations.extend( + self.finished_animations + .drain(..) + .chain(self.running_animations.drain(..)) + .chain(self.new_animations.drain(..)), + ); + } + + pub(crate) fn cancel_transitions_with_nontransitioning_properties( + &mut self, + properties_that_transition: &LonghandIdSet, + ) { + if self.running_animations.is_empty() { + return; + } + + let previously_running_transitions = + std::mem::replace(&mut self.running_animations, Vec::new()); + for running_animation in previously_running_transitions { + if let Animation::Transition(_, _, ref property_animation) = running_animation { + if !properties_that_transition.contains(property_animation.property_id()) { + self.cancelled_animations.push(running_animation); + continue; + } + } + self.running_animations.push(running_animation); + } + } + + fn has_transition_with_same_end_value(&self, property_animation: &PropertyAnimation) -> bool { + if self + .running_animations + .iter() + .any(|animation| animation.is_transition_with_same_end_value(&property_animation)) + { + debug!( + "Running transition found with the same end value for {:?}", + property_animation, + ); + return true; + } + + if self + .finished_animations + .iter() + .any(|animation| animation.is_transition_with_same_end_value(&property_animation)) + { + debug!( + "Expired transition found with the same end value for {:?}", + property_animation, + ); + return true; + } + + false + } + + pub(crate) fn apply_completed_animations(&mut self, style: &mut Arc<ComputedValues>) { + for animation in self.finished_animations.iter() { + // TODO: Make this a bit more general and add support animation-fill-mode. + // Without support for that property though, animations that have ended should + // not affect property values. This is why we only process transitions here. + if let Animation::Transition(_, _, property_animation) = animation { + property_animation.update(Arc::make_mut(style), 1.0); + } + } + } + + pub(crate) fn apply_running_animations<E>( + &mut self, + context: &SharedStyleContext, + style: &mut Arc<ComputedValues>, + font_metrics: &dyn crate::font_metrics::FontMetricsProvider, + ) where + E: TElement, + { + // Return early so that we don't unnecessarily clone the style when making it mutable. + if self.running_animations.is_empty() { + return; + } + + let style = Arc::make_mut(style); + for animation in self.running_animations.iter_mut() { + let update = update_style_for_animation::<E>(context, animation, style, font_metrics); + + match *animation { + Animation::Transition(..) => {}, + Animation::Keyframes(_, _, _, ref mut state) => match update { + AnimationUpdate::Regular => {}, + AnimationUpdate::AnimationCanceled => { + state.expired = true; + }, + }, + } + } + } + + pub(crate) fn apply_new_animations<E>( + &mut self, + context: &SharedStyleContext, + style: &mut Arc<ComputedValues>, + font_metrics: &dyn crate::font_metrics::FontMetricsProvider, + ) where + E: TElement, + { + // Return early so that we don't unnecessarily clone the style when making it mutable. + if self.new_animations.is_empty() { + return; + } + + let style = Arc::make_mut(style); + for animation in self.new_animations.iter_mut() { + update_style_for_animation::<E>(context, animation, style, font_metrics); + } + } + + /// Whether this `ElementAnimationState` is empty, which means it doesn't + /// hold any animations in any state. + pub fn is_empty(&self) -> bool { + self.running_animations.is_empty() && + self.finished_animations.is_empty() && + self.cancelled_animations.is_empty() && + self.new_animations.is_empty() + } + + fn add_new_animation(&mut self, animation: Animation) { + self.new_animations.push(animation); + } + + fn add_or_update_new_animation(&mut self, timer: &Timer, new_animation: Animation) { + // If the animation was already present in the list for the node, + // just update its state. + if let Animation::Keyframes(_, _, ref new_name, ref new_state) = new_animation { + for existing_animation in self.running_animations.iter_mut() { + match existing_animation { + Animation::Keyframes(_, _, ref name, ref mut state) if *name == *new_name => { + state.update_from_other(&new_state, timer); + return; + }, + _ => {}, + } + } + } + // Otherwise just add the new running animation. + self.add_new_animation(new_animation); + } +} + +/// Kick off any new transitions for this node and return all of the properties that are +/// transitioning. This is at the end of calculating style for a single node. pub fn start_transitions_if_applicable( - new_animations_sender: &Sender<Animation>, + context: &SharedStyleContext, opaque_node: OpaqueNode, old_style: &ComputedValues, new_style: &mut Arc<ComputedValues>, - timer: &Timer, - running_and_expired_transitions: &[PropertyAnimation], -) -> bool { - let mut had_animations = false; - let transitions: Vec<TransitionPropertyIteration> = new_style.transition_properties().collect(); - for transition in &transitions { + animation_state: &mut ElementAnimationState, +) -> LonghandIdSet { + // If the style of this element is display:none, then we don't start any transitions + // and we cancel any currently running transitions by returning an empty LonghandIdSet. + if new_style.get_box().clone_display().is_none() { + return LonghandIdSet::new(); + } + + let mut properties_that_transition = LonghandIdSet::new(); + for transition in new_style.transition_properties() { + let physical_property = transition.longhand_id.to_physical(new_style.writing_mode); + if properties_that_transition.contains(physical_property) { + continue; + } else { + properties_that_transition.insert(physical_property); + } + let property_animation = match PropertyAnimation::from_longhand( transition.longhand_id, new_style @@ -374,56 +566,33 @@ pub fn start_transitions_if_applicable( .get_box() .transition_duration_mod(transition.index), old_style, - Arc::make_mut(new_style), + new_style, ) { Some(property_animation) => property_animation, None => continue, }; - // Set the property to the initial value. - // - // NB: get_mut is guaranteed to succeed since we called make_mut() - // above. - property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0); - // Per [1], don't trigger a new transition if the end state for that - // transition is the same as that of a transition that's already - // running on the same node. - // + // transition is the same as that of a transition that's running or + // completed. // [1]: https://drafts.csswg.org/css-transitions/#starting - debug!( - "checking {:?} for matching end value", - running_and_expired_transitions - ); - if running_and_expired_transitions - .iter() - .any(|animation| animation.has_the_same_end_value_as(&property_animation)) - { - debug!( - "Not initiating transition for {}, other transition \ - found with the same end value", - property_animation.property_name() - ); + if animation_state.has_transition_with_same_end_value(&property_animation) { continue; } // Kick off the animation. debug!("Kicking off transition of {:?}", property_animation); let box_style = new_style.get_box(); - let now = timer.seconds(); + let now = context.timer.seconds(); let start_time = now + (box_style.transition_delay_mod(transition.index).seconds() as f64); - new_animations_sender - .send(Animation::Transition( - opaque_node, - start_time, - property_animation, - )) - .unwrap(); - - had_animations = true; + animation_state.add_new_animation(Animation::Transition( + opaque_node, + start_time, + property_animation, + )); } - had_animations + properties_that_transition } fn compute_style_for_animation_step<E>( @@ -484,15 +653,12 @@ where pub fn maybe_start_animations<E>( element: E, context: &SharedStyleContext, - new_animations_sender: &Sender<Animation>, node: OpaqueNode, new_style: &Arc<ComputedValues>, -) -> bool -where + animation_state: &mut ElementAnimationState, +) where E: TElement, { - let mut had_animations = false; - let box_style = new_style.get_box(); for (i, name) in box_style.animation_name_iter().enumerate() { let name = match name.as_atom() { @@ -546,8 +712,9 @@ where AnimationPlayState::Running => KeyframesRunningState::Running, }; - new_animations_sender - .send(Animation::Keyframes( + animation_state.add_or_update_new_animation( + &context.timer, + Animation::Keyframes( node, anim.clone(), name.clone(), @@ -562,12 +729,9 @@ where expired: false, cascade_style: new_style.clone(), }, - )) - .unwrap(); - had_animations = true; + ), + ); } - - had_animations } /// Returns the kind of animation update that happened. @@ -591,7 +755,7 @@ pub enum AnimationUpdate { pub fn update_style_for_animation<E>( context: &SharedStyleContext, animation: &Animation, - style: &mut Arc<ComputedValues>, + style: &mut ComputedValues, font_metrics_provider: &dyn FontMetricsProvider, ) -> AnimationUpdate where @@ -606,14 +770,8 @@ where let progress = (now - start_time) / (property_animation.duration); let progress = progress.min(1.0); if progress >= 0.0 { - property_animation.update(Arc::make_mut(style), progress); + property_animation.update(style, progress); } - - // FIXME(emilio): Should check before updating the style that the - // transition_property still transitions this, or bail out if not. - // - // Or doing it in process_animations, only if transition_property - // changed somehow (even better). AnimationUpdate::Regular }, Animation::Keyframes(_, ref animation, ref name, ref state) => { @@ -717,7 +875,7 @@ where let from_style = compute_style_for_animation_step::<E>( context, last_keyframe, - &**style, + style, &state.cascade_style, font_metrics_provider, ); @@ -761,7 +919,7 @@ where property ); debug!("update_style_for_animation: {:?}", property_animation); - property_animation.update(Arc::make_mut(&mut new_style), relative_progress); + property_animation.update(&mut new_style, relative_progress); }, None => { debug!( diff --git a/components/style/context.rs b/components/style/context.rs index d07cf6bd94c..5bcae3e6738 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -5,7 +5,7 @@ //! The context within which style is calculated. #[cfg(feature = "servo")] -use crate::animation::Animation; +use crate::animation::ElementAnimationState; use crate::bloom::StyleBloom; use crate::data::{EagerPseudoStyles, ElementData}; #[cfg(feature = "servo")] @@ -29,8 +29,6 @@ use crate::timer::Timer; use crate::traversal::DomTraversal; use crate::traversal_flags::TraversalFlags; use app_units::Au; -#[cfg(feature = "servo")] -use crossbeam_channel::Sender; use euclid::default::Size2D; use euclid::Scale; use fxhash::FxHashMap; @@ -43,8 +41,6 @@ use servo_arc::Arc; use servo_atoms::Atom; use std::fmt; use std::ops; -#[cfg(feature = "servo")] -use std::sync::Mutex; use style_traits::CSSPixel; use style_traits::DevicePixel; #[cfg(feature = "servo")] @@ -54,22 +50,6 @@ use uluru::{Entry, LRUCache}; pub use selectors::matching::QuirksMode; -/// This structure is used to create a local style context from a shared one. -#[cfg(feature = "servo")] -pub struct ThreadLocalStyleContextCreationInfo { - new_animations_sender: Sender<Animation>, -} - -#[cfg(feature = "servo")] -impl ThreadLocalStyleContextCreationInfo { - /// Trivially constructs a `ThreadLocalStyleContextCreationInfo`. - pub fn new(animations_sender: Sender<Animation>) -> Self { - ThreadLocalStyleContextCreationInfo { - new_animations_sender: animations_sender, - } - } -} - /// A global options structure for the style system. We use this instead of /// opts to abstract across Gecko and Servo. #[derive(Clone)] @@ -186,21 +166,13 @@ pub struct SharedStyleContext<'a> { /// A map with our snapshots in order to handle restyle hints. pub snapshot_map: &'a SnapshotMap, - /// The animations that are currently running. - #[cfg(feature = "servo")] - pub running_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>, - - /// The list of animations that have expired since the last style recalculation. + /// The state of all animations for our styled elements. #[cfg(feature = "servo")] - pub expired_animations: Arc<RwLock<FxHashMap<OpaqueNode, Vec<Animation>>>>, + pub animation_states: Arc<RwLock<FxHashMap<OpaqueNode, ElementAnimationState>>>, /// Paint worklets #[cfg(feature = "servo")] pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters, - - /// Data needed to create the thread-local style context from the shared one. - #[cfg(feature = "servo")] - pub local_context_creation_data: Mutex<ThreadLocalStyleContextCreationInfo>, } impl<'a> SharedStyleContext<'a> { @@ -737,10 +709,6 @@ pub struct ThreadLocalStyleContext<E: TElement> { pub rule_cache: RuleCache, /// The bloom filter used to fast-reject selector-matching. pub bloom_filter: StyleBloom<E>, - /// A channel on which new animations that have been triggered by style - /// recalculation can be sent. - #[cfg(feature = "servo")] - pub new_animations_sender: Sender<Animation>, /// A set of tasks to be run (on the parent thread) in sequential mode after /// the rest of the styling is complete. This is useful for /// infrequently-needed non-threadsafe operations. @@ -774,12 +742,6 @@ impl<E: TElement> ThreadLocalStyleContext<E> { sharing_cache: StyleSharingCache::new(), rule_cache: RuleCache::new(), bloom_filter: StyleBloom::new(), - new_animations_sender: shared - .local_context_creation_data - .lock() - .unwrap() - .new_animations_sender - .clone(), tasks: SequentialTaskList(Vec::new()), selector_flags: SelectorFlagsMap::new(), statistics: PerThreadTraversalStatistics::default(), diff --git a/components/style/lib.rs b/components/style/lib.rs index 5096217d75c..1abb740686d 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -32,8 +32,6 @@ extern crate atomic_refcell; extern crate bitflags; #[allow(unused_extern_crates)] extern crate byteorder; -#[cfg(feature = "servo")] -extern crate crossbeam_channel; #[macro_use] extern crate cssparser; #[macro_use] diff --git a/components/style/matching.rs b/components/style/matching.rs index f99f15efc3e..2d6e47f6d1c 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -7,13 +7,15 @@ #![allow(unsafe_code)] #![deny(missing_docs)] +#[cfg(feature = "servo")] +use crate::animation; use crate::computed_value_flags::ComputedValueFlags; use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap}; use crate::context::{SharedStyleContext, StyleContext}; use crate::data::ElementData; use crate::dom::TElement; #[cfg(feature = "servo")] -use crate::dom::{OpaqueNode, TNode}; +use crate::dom::TNode; use crate::invalidation::element::restyle_hints::RestyleHint; use crate::properties::longhands::display::computed_value::T as Display; use crate::properties::ComputedValues; @@ -437,56 +439,61 @@ trait PrivateMatchMethods: TElement { _restyle_hint: RestyleHint, _important_rules_changed: bool, ) { - use crate::animation; - let this_opaque = self.as_node().opaque(); - let mut running_and_expired_transitions = vec![]; let shared_context = context.shared; - if let Some(ref mut old_values) = *old_values { - // We apply the expired transitions and animations to the old style - // here, because we later compare the old style to the new style in - // `start_transitions_if_applicable`. If the styles differ then it will - // cause the expired transition to restart. - // - // TODO(mrobinson): We should really be following spec behavior and calculate - // after-change-style and before-change-style here. - Self::collect_and_update_style_for_expired_transitions( - shared_context, - this_opaque, - old_values, - &mut running_and_expired_transitions, - ); + let mut animation_states = shared_context.animation_states.write(); + let mut animation_state = animation_states.remove(&this_opaque).unwrap_or_default(); - Self::update_style_for_animations_and_collect_running_transitions( + if let Some(ref mut old_values) = *old_values { + // We convert old values into `before-change-style` here. + // https://drafts.csswg.org/css-transitions/#starting + animation_state.apply_completed_animations(old_values); + animation_state.apply_running_animations::<Self>( shared_context, - this_opaque, old_values, - &mut running_and_expired_transitions, &context.thread_local.font_metrics_provider, ); } - let new_animations_sender = &context.thread_local.new_animations_sender; // Trigger any present animations if necessary. animation::maybe_start_animations( *self, &shared_context, - new_animations_sender, this_opaque, &new_values, + &mut animation_state, ); // Trigger transitions if necessary. This will set `new_values` to // the starting value of the transition if it did trigger a transition. - if let Some(ref values) = old_values { - animation::start_transitions_if_applicable( - new_animations_sender, + if let Some(ref old_values) = old_values { + let transitioning_properties = animation::start_transitions_if_applicable( + shared_context, this_opaque, - &values, + old_values, new_values, - &shared_context.timer, - &running_and_expired_transitions, + &mut animation_state, ); + animation_state + .cancel_transitions_with_nontransitioning_properties(&transitioning_properties); + } + + animation_state.apply_running_animations::<Self>( + shared_context, + new_values, + &context.thread_local.font_metrics_provider, + ); + animation_state.apply_new_animations::<Self>( + shared_context, + new_values, + &context.thread_local.font_metrics_provider, + ); + + // If the ElementAnimationState is empty, and don't store it in order to + // save memory and to avoid extra processing later. + animation_state.finished_animations.clear(); + if !animation_state.is_empty() { + animation_states.insert(this_opaque, animation_state); } } @@ -602,70 +609,6 @@ trait PrivateMatchMethods: TElement { // properties, we can stop the cascade. ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle } - - #[cfg(feature = "servo")] - fn collect_and_update_style_for_expired_transitions( - context: &SharedStyleContext, - node: OpaqueNode, - style: &mut Arc<ComputedValues>, - expired_transitions: &mut Vec<crate::animation::PropertyAnimation>, - ) { - use crate::animation::Animation; - - let mut all_expired_animations = context.expired_animations.write(); - if let Some(animations) = all_expired_animations.remove(&node) { - debug!("removing expired animations for {:?}", node); - for animation in animations { - debug!("Updating expired animation {:?}", animation); - // TODO: support animation-fill-mode - if let Animation::Transition(_, _, property_animation) = animation { - property_animation.update(Arc::make_mut(style), 1.0); - expired_transitions.push(property_animation); - } - } - } - } - - #[cfg(feature = "servo")] - fn update_style_for_animations_and_collect_running_transitions( - context: &SharedStyleContext, - node: OpaqueNode, - style: &mut Arc<ComputedValues>, - running_transitions: &mut Vec<crate::animation::PropertyAnimation>, - font_metrics: &dyn crate::font_metrics::FontMetricsProvider, - ) { - use crate::animation::{self, Animation, AnimationUpdate}; - - let had_running_animations = context.running_animations.read().get(&node).is_some(); - if !had_running_animations { - return; - } - - let mut all_running_animations = context.running_animations.write(); - for mut running_animation in all_running_animations.get_mut(&node).unwrap() { - if let Animation::Transition(_, _, ref property_animation) = *running_animation { - running_transitions.push(property_animation.clone()); - continue; - } - - let update = animation::update_style_for_animation::<Self>( - context, - &mut running_animation, - style, - font_metrics, - ); - - match *running_animation { - Animation::Transition(..) => unreachable!(), - Animation::Keyframes(_, _, _, ref mut state) => match update { - AnimationUpdate::Regular => {}, - AnimationUpdate::AnimationCanceled => { - state.expired = true; - }, - }, - } - } - } } impl<E: TElement> PrivateMatchMethods for E {} diff --git a/components/style/rule_tree/core.rs b/components/style/rule_tree/core.rs index d1c8bda5379..ae1ba7bed94 100644 --- a/components/style/rule_tree/core.rs +++ b/components/style/rule_tree/core.rs @@ -202,6 +202,9 @@ impl RuleTree { /// where it likely did not result from a rigorous performance analysis.) const RULE_TREE_GC_INTERVAL: usize = 300; +/// Used for some size assertions. +pub const RULE_NODE_SIZE: usize = std::mem::size_of::<RuleNode>(); + /// A node in the rule tree. struct RuleNode { /// The root node. Only the root has no root pointer, for obvious reasons. diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index ab5273db781..e50382255ca 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -19,7 +19,7 @@ mod map; mod source; mod unsafe_box; -pub use self::core::{RuleTree, StrongRuleNode}; +pub use self::core::{RuleTree, StrongRuleNode, RULE_NODE_SIZE}; pub use self::level::{CascadeLevel, ShadowCascadeOrder}; pub use self::source::StyleSource; diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 6b63ebf0f6a..50fe8d8c867 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -12,8 +12,6 @@ extern crate crossbeam_channel; extern crate log; #[macro_use] extern crate serde; -#[macro_use] -extern crate serde_json; mod actions; mod capabilities; |