diff options
author | Alex Touchet <alextouchet@outlook.com> | 2018-09-11 09:06:42 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-11 09:06:42 -0700 |
commit | 025b5550fc6f1fd74605b09973ffc606dae7432c (patch) | |
tree | 7dfb1026318b7a0135273b667d3f44e3ee8d737d /components/script/dom | |
parent | 9a7e1d17f0e054cb9f7eaafeee943a2ec5bc5e26 (diff) | |
parent | 049eb6887e29d8409b1dfe55bc31803f1c3220da (diff) | |
download | servo-025b5550fc6f1fd74605b09973ffc606dae7432c.tar.gz servo-025b5550fc6f1fd74605b09973ffc606dae7432c.zip |
Merge branch 'master' into tidy
Diffstat (limited to 'components/script/dom')
41 files changed, 1112 insertions, 710 deletions
diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs index f6d868765ed..45c594dbaee 100644 --- a/components/script/dom/abstractworkerglobalscope.rs +++ b/components/script/dom/abstractworkerglobalscope.rs @@ -2,25 +2,31 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use devtools_traits::DevtoolScriptControlMsg; use dom::abstractworker::WorkerScriptMsg; -use dom::bindings::refcounted::Trusted; +use dom::bindings::conversions::DerivedFrom; use dom::bindings::reflector::DomObject; -use dom::bindings::trace::JSTraceable; +use dom::dedicatedworkerglobalscope::{AutoWorkerReset, DedicatedWorkerScriptMsg}; +use dom::globalscope::GlobalScope; +use dom::worker::TrustedWorkerAddress; +use dom::workerglobalscope::WorkerGlobalScope; use script_runtime::{ScriptChan, CommonScriptMsg, ScriptPort}; -use std::sync::mpsc::{Receiver, Sender}; +use std::sync::mpsc::{Receiver, Select, Sender}; +use task_queue::{QueuedTaskConversion, TaskQueue}; /// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with /// common event loop messages. While this SendableWorkerScriptChan is alive, the associated /// Worker object will remain alive. #[derive(Clone, JSTraceable)] -pub struct SendableWorkerScriptChan<T: DomObject> { - pub sender: Sender<(Trusted<T>, CommonScriptMsg)>, - pub worker: Trusted<T>, +pub struct SendableWorkerScriptChan { + pub sender: Sender<DedicatedWorkerScriptMsg>, + pub worker: TrustedWorkerAddress, } -impl<T: JSTraceable + DomObject + 'static> ScriptChan for SendableWorkerScriptChan<T> { +impl ScriptChan for SendableWorkerScriptChan { fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.sender.send((self.worker.clone(), msg)).map_err(|_| ()) + let msg = DedicatedWorkerScriptMsg::CommonWorker(self.worker.clone(), WorkerScriptMsg::Common(msg)); + self.sender.send(msg).map_err(|_| ()) } fn clone(&self) -> Box<ScriptChan + Send> { @@ -35,15 +41,16 @@ impl<T: JSTraceable + DomObject + 'static> ScriptChan for SendableWorkerScriptCh /// worker event loop messages. While this SendableWorkerScriptChan is alive, the associated /// Worker object will remain alive. #[derive(Clone, JSTraceable)] -pub struct WorkerThreadWorkerChan<T: DomObject> { - pub sender: Sender<(Trusted<T>, WorkerScriptMsg)>, - pub worker: Trusted<T>, +pub struct WorkerThreadWorkerChan { + pub sender: Sender<DedicatedWorkerScriptMsg>, + pub worker: TrustedWorkerAddress, } -impl<T: JSTraceable + DomObject + 'static> ScriptChan for WorkerThreadWorkerChan<T> { +impl ScriptChan for WorkerThreadWorkerChan { fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { + let msg = DedicatedWorkerScriptMsg::CommonWorker(self.worker.clone(), WorkerScriptMsg::Common(msg)); self.sender - .send((self.worker.clone(), WorkerScriptMsg::Common(msg))) + .send(msg) .map_err(|_| ()) } @@ -55,12 +62,100 @@ impl<T: JSTraceable + DomObject + 'static> ScriptChan for WorkerThreadWorkerChan } } -impl<T: DomObject> ScriptPort for Receiver<(Trusted<T>, WorkerScriptMsg)> { +impl ScriptPort for Receiver<DedicatedWorkerScriptMsg> { fn recv(&self) -> Result<CommonScriptMsg, ()> { - match self.recv().map(|(_, msg)| msg) { - Ok(WorkerScriptMsg::Common(script_msg)) => Ok(script_msg), - Ok(WorkerScriptMsg::DOMMessage(_)) => panic!("unexpected worker event message!"), - Err(_) => Err(()), + let common_msg = match self.recv() { + Ok(DedicatedWorkerScriptMsg::CommonWorker(_worker, common_msg)) => common_msg, + Err(_) => return Err(()), + Ok(DedicatedWorkerScriptMsg::WakeUp) => panic!("unexpected worker event message!") + }; + match common_msg { + WorkerScriptMsg::Common(script_msg) => Ok(script_msg), + WorkerScriptMsg::DOMMessage(_) => panic!("unexpected worker event message!"), } } } + +pub trait WorkerEventLoopMethods { + type TimerMsg: Send; + type WorkerMsg: QueuedTaskConversion + Send; + type Event; + fn timer_event_port(&self) -> &Receiver<Self::TimerMsg>; + fn task_queue(&self) -> &TaskQueue<Self::WorkerMsg>; + fn handle_event(&self, event: Self::Event); + fn handle_worker_post_event(&self, worker: &TrustedWorkerAddress) -> Option<AutoWorkerReset>; + fn from_worker_msg(&self, msg: Self::WorkerMsg) -> Self::Event; + fn from_timer_msg(&self, msg: Self::TimerMsg) -> Self::Event; + fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> Self::Event; +} + +#[allow(unsafe_code)] +// https://html.spec.whatwg.org/multipage/#worker-event-loop +pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>(worker_scope: &T, + worker: Option<&TrustedWorkerAddress>) +where + TimerMsg: Send, + WorkerMsg: QueuedTaskConversion + Send, + T: WorkerEventLoopMethods<TimerMsg = TimerMsg, WorkerMsg = WorkerMsg, Event = Event> + + DerivedFrom<WorkerGlobalScope> + DerivedFrom<GlobalScope> + + DomObject { + let scope = worker_scope.upcast::<WorkerGlobalScope>(); + let timer_event_port = worker_scope.timer_event_port(); + let devtools_port = scope.from_devtools_receiver(); + let task_queue = worker_scope.task_queue(); + let sel = Select::new(); + let mut worker_handle = sel.handle(task_queue.select()); + let mut timer_event_handle = sel.handle(timer_event_port); + let mut devtools_handle = sel.handle(devtools_port); + unsafe { + worker_handle.add(); + timer_event_handle.add(); + if scope.from_devtools_sender().is_some() { + devtools_handle.add(); + } + } + let ret = sel.wait(); + let event = { + if ret == worker_handle.id() { + task_queue.take_tasks(); + worker_scope.from_worker_msg(task_queue.recv().unwrap()) + } else if ret == timer_event_handle.id() { + worker_scope.from_timer_msg(timer_event_port.recv().unwrap()) + } else if ret == devtools_handle.id() { + worker_scope.from_devtools_msg(devtools_port.recv().unwrap()) + } else { + panic!("unexpected select result!") + } + }; + let mut sequential = vec![]; + sequential.push(event); + // https://html.spec.whatwg.org/multipage/#worker-event-loop + // Once the WorkerGlobalScope's closing flag is set to true, + // the event loop's task queues must discard any further tasks + // that would be added to them + // (tasks already on the queue are unaffected except where otherwise specified). + while !scope.is_closing() { + // Batch all events that are ready. + // The task queue will throttle non-priority tasks if necessary. + match task_queue.try_recv() { + Err(_) => match timer_event_port.try_recv() { + Err(_) => match devtools_port.try_recv() { + Err(_) => break, + Ok(ev) => sequential.push(worker_scope.from_devtools_msg(ev)), + }, + Ok(ev) => sequential.push(worker_scope.from_timer_msg(ev)), + }, + Ok(ev) => sequential.push(worker_scope.from_worker_msg(ev)), + } + } + // Step 3 + for event in sequential { + worker_scope.handle_event(event); + // Step 6 + let _ar = match worker { + Some(worker) => worker_scope.handle_worker_post_event(worker), + None => None + }; + worker_scope.upcast::<GlobalScope>().perform_a_microtask_checkpoint(); + } +} diff --git a/components/script/dom/audiobuffer.rs b/components/script/dom/audiobuffer.rs index 8cf3bb6648f..761bcde404b 100644 --- a/components/script/dom/audiobuffer.rs +++ b/components/script/dom/audiobuffer.rs @@ -64,7 +64,7 @@ impl AudioBuffer { number_of_channels: u32, length: u32, sample_rate: f32, - initial_data: Option<&[f32]>, + initial_data: Option<&[Vec<f32>]>, ) -> DomRoot<AudioBuffer> { let buffer = AudioBuffer::new_inherited(number_of_channels, length, sample_rate); let buffer = reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap); @@ -93,20 +93,19 @@ impl AudioBuffer { } #[allow(unsafe_code)] - pub fn set_channels(&self, initial_data: Option<&[f32]>) { + pub fn set_channels(&self, initial_data: Option<&[Vec<f32>]>) { let global = self.global(); let cx = global.get_cx(); let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get()); let chans = self.js_channels.borrow_mut(); for channel in 0..self.number_of_channels { rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>()); - let offset = (channel * self.length) as usize; match initial_data { Some(data) => { let _ = unsafe { Float32Array::create( cx, - CreateWith::Slice(&data[offset..offset + (self.length as usize) - 1]), + CreateWith::Slice(data[channel as usize].as_slice()), array.handle_mut(), ) }; diff --git a/components/script/dom/audiobuffersourcenode.rs b/components/script/dom/audiobuffersourcenode.rs index 79841e38e22..a3ddf4121a9 100644 --- a/components/script/dom/audiobuffersourcenode.rs +++ b/components/script/dom/audiobuffersourcenode.rs @@ -44,7 +44,7 @@ impl AudioBufferSourceNode { window: &Window, context: &BaseAudioContext, options: &AudioBufferSourceOptions, - ) -> AudioBufferSourceNode { + ) -> Fallible<AudioBufferSourceNode> { let mut node_options = AudioNodeOptions::empty(); node_options.channelCount = Some(2); node_options.channelCountMode = Some(ChannelCountMode::Max); @@ -55,7 +55,7 @@ impl AudioBufferSourceNode { &node_options, 0, /* inputs */ 1, /* outputs */ - ); + )?; let node_id = source_node.node().node_id(); let playback_rate = AudioParam::new( &window, @@ -77,7 +77,7 @@ impl AudioBufferSourceNode { f32::MIN, f32::MAX, ); - AudioBufferSourceNode { + Ok(AudioBufferSourceNode { source_node, buffer: Default::default(), playback_rate: Dom::from_ref(&playback_rate), @@ -85,7 +85,7 @@ impl AudioBufferSourceNode { loop_enabled: Cell::new(options.loop_), loop_start: Cell::new(*options.loopStart), loop_end: Cell::new(*options.loopEnd), - } + }) } #[allow(unrooted_must_root)] @@ -93,9 +93,9 @@ impl AudioBufferSourceNode { window: &Window, context: &BaseAudioContext, options: &AudioBufferSourceOptions, - ) -> DomRoot<AudioBufferSourceNode> { - let node = AudioBufferSourceNode::new_inherited(window, context, options); - reflect_dom_object(Box::new(node), window, AudioBufferSourceNodeBinding::Wrap) + ) -> Fallible<DomRoot<AudioBufferSourceNode>> { + let node = AudioBufferSourceNode::new_inherited(window, context, options)?; + Ok(reflect_dom_object(Box::new(node), window, AudioBufferSourceNodeBinding::Wrap)) } pub fn Constructor( @@ -103,7 +103,7 @@ impl AudioBufferSourceNode { context: &BaseAudioContext, options: &AudioBufferSourceOptions, ) -> Fallible<DomRoot<AudioBufferSourceNode>> { - Ok(AudioBufferSourceNode::new(window, context, options)) + AudioBufferSourceNode::new(window, context, options) } } diff --git a/components/script/dom/audionode.rs b/components/script/dom/audionode.rs index 4690f74245e..5a149dd814f 100644 --- a/components/script/dom/audionode.rs +++ b/components/script/dom/audionode.rs @@ -44,9 +44,14 @@ impl AudioNode { options: &AudioNodeOptions, number_of_inputs: u32, number_of_outputs: u32, - ) -> AudioNode { + ) -> Fallible<AudioNode> { + if let Some(c) = options.channelCount { + if c == 0 || c > MAX_CHANNEL_COUNT { + return Err(Error::NotSupported); + } + } let node_id = context.audio_context_impl().create_node(node_type); - AudioNode::new_inherited_for_id(node_id, context, options, number_of_inputs, number_of_outputs) + Ok(AudioNode::new_inherited_for_id(node_id, context, options, number_of_inputs, number_of_outputs)) } pub fn new_inherited_for_id( @@ -218,6 +223,11 @@ impl AudioNodeMethods for AudioNode { return Err(Error::NotSupported) } } + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelMergerNode) => { + if value != 1 { + return Err(Error::InvalidState) + } + } // XXX We do not support any of the other AudioNodes with // constraints yet. Add more cases here as we add support // for new AudioNodes. @@ -256,6 +266,11 @@ impl AudioNodeMethods for AudioNode { return Err(Error::NotSupported) } } + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelMergerNode) => { + if value != ChannelCountMode::Explicit { + return Err(Error::InvalidState) + } + } // XXX We do not support any of the other AudioNodes with // constraints yet. Add more cases here as we add support // for new AudioNodes. diff --git a/components/script/dom/audioscheduledsourcenode.rs b/components/script/dom/audioscheduledsourcenode.rs index 03f1f952102..871a793f6ec 100644 --- a/components/script/dom/audioscheduledsourcenode.rs +++ b/components/script/dom/audioscheduledsourcenode.rs @@ -24,24 +24,25 @@ pub struct AudioScheduledSourceNode { } impl AudioScheduledSourceNode { + #[allow(unrooted_must_root)] pub fn new_inherited( node_type: AudioNodeInit, context: &BaseAudioContext, options: &AudioNodeOptions, number_of_inputs: u32, number_of_outputs: u32, - ) -> AudioScheduledSourceNode { - AudioScheduledSourceNode { + ) -> Fallible<AudioScheduledSourceNode> { + Ok(AudioScheduledSourceNode { node: AudioNode::new_inherited( node_type, context, options, number_of_inputs, number_of_outputs, - ), + )?, started: Cell::new(false), stopped: Cell::new(false), - } + }) } pub fn node(&self) -> &AudioNode { diff --git a/components/script/dom/baseaudiocontext.rs b/components/script/dom/baseaudiocontext.rs index 9513a0b22d1..dbd821b64fe 100644 --- a/components/script/dom/baseaudiocontext.rs +++ b/components/script/dom/baseaudiocontext.rs @@ -16,6 +16,7 @@ use dom::bindings::codegen::Bindings::BaseAudioContextBinding::AudioContextState use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextMethods; use dom::bindings::codegen::Bindings::BaseAudioContextBinding::DecodeErrorCallback; use dom::bindings::codegen::Bindings::BaseAudioContextBinding::DecodeSuccessCallback; +use dom::bindings::codegen::Bindings::ChannelMergerNodeBinding::ChannelMergerOptions; use dom::bindings::codegen::Bindings::GainNodeBinding::GainOptions; use dom::bindings::codegen::Bindings::OscillatorNodeBinding::OscillatorOptions; use dom::bindings::codegen::Bindings::PannerNodeBinding::PannerOptions; @@ -25,6 +26,7 @@ use dom::bindings::num::Finite; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::DomObject; use dom::bindings::root::{DomRoot, MutNullableDom}; +use dom::channelmergernode::ChannelMergerNode; use dom::domexception::{DOMErrorName, DOMException}; use dom::eventtarget::EventTarget; use dom::gainnode::GainNode; @@ -317,7 +319,7 @@ impl BaseAudioContextMethods for BaseAudioContext { event_handler!(statechange, GetOnstatechange, SetOnstatechange); /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createoscillator - fn CreateOscillator(&self) -> DomRoot<OscillatorNode> { + fn CreateOscillator(&self) -> Fallible<DomRoot<OscillatorNode>> { OscillatorNode::new( &self.global().as_window(), &self, @@ -326,7 +328,7 @@ impl BaseAudioContextMethods for BaseAudioContext { } /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-creategain - fn CreateGain(&self) -> DomRoot<GainNode> { + fn CreateGain(&self) -> Fallible<DomRoot<GainNode>> { GainNode::new(&self.global().as_window(), &self, &GainOptions::empty()) } @@ -335,6 +337,12 @@ impl BaseAudioContextMethods for BaseAudioContext { PannerNode::new(&self.global().as_window(), &self, &PannerOptions::empty()) } + /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createchannelmerger + fn CreateChannelMerger(&self, count: u32) -> Fallible<DomRoot<ChannelMergerNode>> { + let mut opts = ChannelMergerOptions::empty(); + opts.numberOfInputs = count; + ChannelMergerNode::new(&self.global().as_window(), &self, &opts) + } /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer fn CreateBuffer( @@ -360,7 +368,7 @@ impl BaseAudioContextMethods for BaseAudioContext { } // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffersource - fn CreateBufferSource(&self) -> DomRoot<AudioBufferSourceNode> { + fn CreateBufferSource(&self) -> Fallible<DomRoot<AudioBufferSourceNode>> { AudioBufferSourceNode::new( &self.global().as_window(), &self, @@ -397,6 +405,7 @@ impl BaseAudioContextMethods for BaseAudioContext { let audio_data = audio_data.to_vec(); let decoded_audio = Arc::new(Mutex::new(Vec::new())); let decoded_audio_ = decoded_audio.clone(); + let decoded_audio__ = decoded_audio.clone(); let this = Trusted::new(self); let this_ = this.clone(); let task_source = window.dom_manipulation_task_source(); @@ -404,15 +413,30 @@ impl BaseAudioContextMethods for BaseAudioContext { let canceller = window.task_canceller(TaskSourceName::DOMManipulation); let canceller_ = window.task_canceller(TaskSourceName::DOMManipulation); let callbacks = AudioDecoderCallbacks::new() + .ready(move |channel_count| { + decoded_audio + .lock() + .unwrap() + .resize(channel_count as usize, Vec::new()); + }) + .progress(move |buffer, channel| { + let mut decoded_audio = decoded_audio_.lock().unwrap(); + decoded_audio[(channel - 1) as usize].extend_from_slice((*buffer).as_ref()); + }) .eos(move || { let _ = task_source.queue_with_canceller( task!(audio_decode_eos: move || { let this = this.root(); - let decoded_audio = decoded_audio.lock().unwrap(); + let decoded_audio = decoded_audio__.lock().unwrap(); + let length = if decoded_audio.len() >= 1 { + decoded_audio[0].len() + } else { + 0 + }; let buffer = AudioBuffer::new( &this.global().as_window(), - 1, // XXX servo-media should provide this info - decoded_audio.len() as u32, + decoded_audio.len() as u32 /* number of channels */, + length as u32, this.sample_rate, Some(decoded_audio.as_slice())); let mut resolvers = this.decode_resolvers.borrow_mut(); @@ -443,12 +467,6 @@ impl BaseAudioContextMethods for BaseAudioContext { &canceller_, ); }) - .progress(move |buffer| { - decoded_audio_ - .lock() - .unwrap() - .extend_from_slice((*buffer).as_ref()); - }) .build(); self.audio_context_impl .decode_audio_data(audio_data, callbacks); diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 449fda3b7ea..39409e7deaa 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -344,12 +344,17 @@ class CGMethodCall(CGThing): distinguishingIndex = method.distinguishingIndexForArgCount(argCount) - # We can't handle unions at the distinguishing index. + # We can't handle unions of non-object values at the distinguishing index. for (returnType, args) in possibleSignatures: - if args[distinguishingIndex].type.isUnion(): - raise TypeError("No support for unions as distinguishing " - "arguments yet: %s", - args[distinguishingIndex].location) + type = args[distinguishingIndex].type + if type.isUnion(): + if type.nullable(): + type = type.inner + for type in type.flatMemberTypes: + if not (type.isObject() or type.isNonCallbackInterface()): + raise TypeError("No support for unions with non-object variants " + "as distinguishing arguments yet: %s", + args[distinguishingIndex].location) # Convert all our arguments up to the distinguishing index. # Doesn't matter which of the possible signatures we use, since @@ -388,6 +393,7 @@ class CGMethodCall(CGThing): interfacesSigs = [ s for s in possibleSignatures if (s[1][distinguishingIndex].type.isObject() or + s[1][distinguishingIndex].type.isUnion() or s[1][distinguishingIndex].type.isNonCallbackInterface())] # There might be more than one of these; we need to check # which ones we unwrap to. @@ -2366,7 +2372,6 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 'dom::bindings::conversions::ConversionBehavior', 'dom::bindings::conversions::StringificationBehavior', 'dom::bindings::conversions::root_from_handlevalue', - 'dom::bindings::error::throw_not_in_union', 'std::ptr::NonNull', 'dom::bindings::mozmap::MozMap', 'dom::bindings::root::DomRoot', @@ -4450,8 +4455,8 @@ class CGUnionConversionStruct(CGThing): other.append(booleanConversion[0]) conversions.append(CGList(other, "\n\n")) conversions.append(CGGeneric( - "throw_not_in_union(cx, \"%s\");\n" - "Err(())" % ", ".join(names))) + "Ok(ConversionResult::Failure(\"argument could not be converted to any of: %s\".into()))" % ", ".join(names) + )) method = CGWrapper( CGIndenter(CGList(conversions, "\n\n")), pre="unsafe fn from_jsval(cx: *mut JSContext,\n" @@ -4977,7 +4982,7 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): def __init__(self, descriptor): args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'), Argument('RawHandleId', 'id'), - Argument('RawMutableHandle<PropertyDescriptor>', 'desc')] + Argument('RawMutableHandle<PropertyDescriptor>', 'mut desc')] CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor", "bool", args) self.descriptor = descriptor @@ -5050,7 +5055,6 @@ if %s { else: namedGet = "" - # FIXME(#11868) Should assign to desc.obj, desc.get() is a copy. return get + """\ rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); get_expando_object(proxy, expando.handle_mut()); @@ -5063,7 +5067,7 @@ if !expando.is_null() { } if !desc.obj.is_null() { // Pretend the property lives on the wrapper. - desc.get().obj = proxy.get(); + desc.obj = proxy.get(); return true; } } diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index c953df65c1d..7c68de0df18 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -255,14 +255,6 @@ pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool) } } -/// Throw an exception to signal that a `JSVal` can not be converted to any of -/// the types in an IDL union type. -pub unsafe fn throw_not_in_union(cx: *mut JSContext, names: &'static str) { - assert!(!JS_IsExceptionPending(cx)); - let error = format!("argument could not be converted to any of: {}", names); - throw_type_error(cx, &error); -} - /// Throw an exception to signal that a `JSObject` can not be converted to a /// given DOM type. pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) { diff --git a/components/script/dom/channelmergernode.rs b/components/script/dom/channelmergernode.rs new file mode 100644 index 00000000000..d36c481a158 --- /dev/null +++ b/components/script/dom/channelmergernode.rs @@ -0,0 +1,83 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use dom::audionode::{AudioNode, MAX_CHANNEL_COUNT}; +use dom::baseaudiocontext::BaseAudioContext; +use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation}; +use dom::bindings::codegen::Bindings::AudioNodeBinding::AudioNodeOptions; +use dom::bindings::codegen::Bindings::ChannelMergerNodeBinding::{self, ChannelMergerOptions}; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::root::DomRoot; +use dom::window::Window; +use dom_struct::dom_struct; +use servo_media::audio::channel_node::ChannelNodeOptions; +use servo_media::audio::node::AudioNodeInit; + +#[dom_struct] +pub struct ChannelMergerNode { + node: AudioNode, +} + +impl ChannelMergerNode { + #[allow(unrooted_must_root)] + pub fn new_inherited( + _: &Window, + context: &BaseAudioContext, + options: &ChannelMergerOptions, + ) -> Fallible<ChannelMergerNode> { + let mut node_options = AudioNodeOptions::empty(); + let count = options.parent.channelCount.unwrap_or(1); + let mode = options.parent.channelCountMode.unwrap_or(ChannelCountMode::Explicit); + let interpretation = options.parent.channelInterpretation.unwrap_or(ChannelInterpretation::Speakers); + + if count != 1 || mode != ChannelCountMode::Explicit { + return Err(Error::InvalidState) + } + + if options.numberOfInputs < 1 || options.numberOfInputs > MAX_CHANNEL_COUNT { + return Err(Error::IndexSize) + } + + node_options.channelCount = Some(count); + node_options.channelCountMode = Some(mode); + node_options.channelInterpretation = Some(interpretation); + let node = AudioNode::new_inherited( + AudioNodeInit::ChannelMergerNode(options.into()), + context, + &node_options, + options.numberOfInputs, // inputs + 1, // outputs + )?; + Ok(ChannelMergerNode { + node, + }) + } + + #[allow(unrooted_must_root)] + pub fn new( + window: &Window, + context: &BaseAudioContext, + options: &ChannelMergerOptions, + ) -> Fallible<DomRoot<ChannelMergerNode>> { + let node = ChannelMergerNode::new_inherited(window, context, options)?; + Ok(reflect_dom_object(Box::new(node), window, ChannelMergerNodeBinding::Wrap)) + } + + pub fn Constructor( + window: &Window, + context: &BaseAudioContext, + options: &ChannelMergerOptions, + ) -> Fallible<DomRoot<ChannelMergerNode>> { + ChannelMergerNode::new(window, context, options) + } +} + +impl<'a> From<&'a ChannelMergerOptions> for ChannelNodeOptions { + fn from(options: &'a ChannelMergerOptions) -> Self { + Self { + channels: options.numberOfInputs as u8, + } + } +} diff --git a/components/script/dom/css.rs b/components/script/dom/css.rs index d642b44a19f..7a42d6b6e68 100644 --- a/components/script/dom/css.rs +++ b/components/script/dom/css.rs @@ -44,6 +44,7 @@ impl CSS { ParsingMode::DEFAULT, QuirksMode::NoQuirks, None, + None, ); decl.eval(&context) } @@ -61,6 +62,7 @@ impl CSS { ParsingMode::DEFAULT, QuirksMode::NoQuirks, None, + None, ); cond.eval(&context) } else { diff --git a/components/script/dom/cssmediarule.rs b/components/script/dom/cssmediarule.rs index 23f3c46a251..ccc803e4b42 100644 --- a/components/script/dom/cssmediarule.rs +++ b/components/script/dom/cssmediarule.rs @@ -81,6 +81,7 @@ impl CSSMediaRule { ParsingMode::DEFAULT, quirks_mode, window.css_error_reporter(), + None, ); let new_medialist = StyleMediaList::parse(&context, &mut input); diff --git a/components/script/dom/csssupportsrule.rs b/components/script/dom/csssupportsrule.rs index a96cd706b86..8b66fbe76cf 100644 --- a/components/script/dom/csssupportsrule.rs +++ b/components/script/dom/csssupportsrule.rs @@ -69,6 +69,7 @@ impl CSSSupportsRule { ParsingMode::DEFAULT, quirks_mode, None, + None, ); let enabled = cond.eval(&context); let mut guard = self.cssconditionrule.shared_lock().write(); diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 1c8c2316dde..c582177f42e 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -6,6 +6,7 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; use dom::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg}; use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; +use dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop}; use dom::bindings::cell::DomRefCell; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; @@ -40,15 +41,17 @@ use servo_url::ServoUrl; use std::mem::replace; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; +use std::sync::mpsc::{Receiver, Sender, channel}; use std::thread; use style::thread_state::{self, ThreadState}; +use task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; +use task_source::TaskSourceName; /// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular /// value for the duration of this object's lifetime. This ensures that the related Worker /// object only lives as long as necessary (ie. while events are being executed), while /// providing a reference that can be cloned freely. -struct AutoWorkerReset<'a> { +pub struct AutoWorkerReset<'a> { workerscope: &'a DedicatedWorkerGlobalScope, old_worker: Option<TrustedWorkerAddress>, } @@ -70,20 +73,85 @@ impl<'a> Drop for AutoWorkerReset<'a> { } } -enum MixedMessage { - FromWorker((TrustedWorkerAddress, WorkerScriptMsg)), +pub enum DedicatedWorkerScriptMsg { + /// Standard message from a worker. + CommonWorker(TrustedWorkerAddress, WorkerScriptMsg), + /// Wake-up call from the task queue. + WakeUp, +} + +pub enum MixedMessage { + FromWorker(DedicatedWorkerScriptMsg), FromScheduler((TrustedWorkerAddress, TimerEvent)), FromDevtools(DevtoolScriptControlMsg) } +impl QueuedTaskConversion for DedicatedWorkerScriptMsg { + fn task_source_name(&self) -> Option<&TaskSourceName> { + let common_worker_msg = match self { + DedicatedWorkerScriptMsg::CommonWorker(_, common_worker_msg) => common_worker_msg, + _ => return None, + }; + let script_msg = match common_worker_msg { + WorkerScriptMsg::Common(ref script_msg) => script_msg, + _ => return None, + }; + match script_msg { + CommonScriptMsg::Task(_category, _boxed, _pipeline_id, source_name) => Some(&source_name), + _ => return None, + } + } + + fn into_queued_task(self) -> Option<QueuedTask> { + let (worker, common_worker_msg) = match self { + DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => (worker, common_worker_msg), + _ => return None, + }; + let script_msg = match common_worker_msg { + WorkerScriptMsg::Common(script_msg) => script_msg, + _ => return None, + }; + let (category, boxed, pipeline_id, task_source) = match script_msg { + CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => + (category, boxed, pipeline_id, task_source), + _ => return None, + }; + Some((Some(worker), category, boxed, pipeline_id, task_source)) + } + + fn from_queued_task(queued_task: QueuedTask) -> Self { + let (worker, category, boxed, pipeline_id, task_source) = queued_task; + let script_msg = CommonScriptMsg::Task( + category, + boxed, + pipeline_id, + task_source + ); + DedicatedWorkerScriptMsg::CommonWorker(worker.unwrap(), WorkerScriptMsg::Common(script_msg)) + } + + fn wake_up_msg() -> Self { + DedicatedWorkerScriptMsg::WakeUp + } + + fn is_wake_up(&self) -> bool { + match self { + DedicatedWorkerScriptMsg::WakeUp => true, + _ => false, + } + } +} + +unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>); + // https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope #[dom_struct] pub struct DedicatedWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, #[ignore_malloc_size_of = "Defined in std"] - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, + task_queue: TaskQueue<DedicatedWorkerScriptMsg>, #[ignore_malloc_size_of = "Defined in std"] - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, + own_sender: Sender<DedicatedWorkerScriptMsg>, #[ignore_malloc_size_of = "Defined in std"] timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, #[ignore_malloc_size_of = "Trusted<T> has unclear ownership like Dom<T>"] @@ -93,14 +161,49 @@ pub struct DedicatedWorkerGlobalScope { parent_sender: Box<ScriptChan + Send>, } +impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope { + type TimerMsg = (TrustedWorkerAddress, TimerEvent); + type WorkerMsg = DedicatedWorkerScriptMsg; + type Event = MixedMessage; + + fn timer_event_port(&self) -> &Receiver<(TrustedWorkerAddress, TimerEvent)> { + &self.timer_event_port + } + + fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> { + &self.task_queue + } + + fn handle_event(&self, event: MixedMessage) { + self.handle_mixed_message(event); + } + + fn handle_worker_post_event(&self, worker: &TrustedWorkerAddress) -> Option<AutoWorkerReset> { + let ar = AutoWorkerReset::new(&self, worker.clone()); + Some(ar) + } + + fn from_worker_msg(&self, msg: DedicatedWorkerScriptMsg) -> MixedMessage { + MixedMessage::FromWorker(msg) + } + + fn from_timer_msg(&self, msg: (TrustedWorkerAddress, TimerEvent)) -> MixedMessage { + MixedMessage::FromScheduler(msg) + } + + fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> MixedMessage { + MixedMessage::FromDevtools(msg) + } +} + impl DedicatedWorkerGlobalScope { fn new_inherited(init: WorkerGlobalScopeInit, worker_url: ServoUrl, from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, runtime: Runtime, parent_sender: Box<ScriptChan + Send>, - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, + own_sender: Sender<DedicatedWorkerScriptMsg>, + receiver: Receiver<DedicatedWorkerScriptMsg>, timer_event_chan: IpcSender<TimerEvent>, timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, closing: Arc<AtomicBool>) @@ -112,7 +215,7 @@ impl DedicatedWorkerGlobalScope { from_devtools_receiver, timer_event_chan, Some(closing)), - receiver: receiver, + task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender: own_sender, timer_event_port: timer_event_port, parent_sender: parent_sender, @@ -126,8 +229,8 @@ impl DedicatedWorkerGlobalScope { from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, runtime: Runtime, parent_sender: Box<ScriptChan + Send>, - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, + own_sender: Sender<DedicatedWorkerScriptMsg>, + receiver: Receiver<DedicatedWorkerScriptMsg>, timer_event_chan: IpcSender<TimerEvent>, timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, closing: Arc<AtomicBool>) @@ -151,13 +254,14 @@ impl DedicatedWorkerGlobalScope { } #[allow(unsafe_code)] + // https://html.spec.whatwg.org/multipage/#run-a-worker pub fn run_worker_scope(init: WorkerGlobalScopeInit, worker_url: ServoUrl, from_devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>, worker: TrustedWorkerAddress, parent_sender: Box<ScriptChan + Send>, - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, + own_sender: Sender<DedicatedWorkerScriptMsg>, + receiver: Receiver<DedicatedWorkerScriptMsg>, worker_load_origin: WorkerScriptLoadOrigin, closing: Arc<AtomicBool>) { let serialized_worker_url = worker_url.to_string(); @@ -196,7 +300,8 @@ impl DedicatedWorkerGlobalScope { parent_sender.send(CommonScriptMsg::Task( WorkerEvent, Box::new(SimpleWorkerErrorHandler::new(worker)), - pipeline_id + pipeline_id, + TaskSourceName::DOMManipulation, )).unwrap(); return; } @@ -242,17 +347,11 @@ impl DedicatedWorkerGlobalScope { let reporter_name = format!("dedicated-worker-reporter-{}", random::<u64>()); scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| { - // https://html.spec.whatwg.org/multipage/#event-loop-processing-model - // Step 1 - while let Ok(event) = global.receive_event() { - if scope.is_closing() { - break; - } - // Step 3 - global.handle_event(event); - // Step 6 - let _ar = AutoWorkerReset::new(&global, worker.clone()); - global.upcast::<GlobalScope>().perform_a_microtask_checkpoint(); + // Step 29, Run the responsible event loop specified by inside settings until it is destroyed. + // The worker processing model remains on this step until the event loop is destroyed, + // which happens after the closing flag is set to true. + while !scope.is_closing() { + run_worker_event_loop(&*global, Some(&worker)); } }, reporter_name, parent_sender, CommonScriptMsg::CollectReports); }).expect("Thread spawning failed"); @@ -274,36 +373,6 @@ impl DedicatedWorkerGlobalScope { (chan, Box::new(rx)) } - #[allow(unsafe_code)] - fn receive_event(&self) -> Result<MixedMessage, RecvError> { - let scope = self.upcast::<WorkerGlobalScope>(); - let worker_port = &self.receiver; - let timer_event_port = &self.timer_event_port; - let devtools_port = scope.from_devtools_receiver(); - - let sel = Select::new(); - let mut worker_handle = sel.handle(worker_port); - let mut timer_event_handle = sel.handle(timer_event_port); - let mut devtools_handle = sel.handle(devtools_port); - unsafe { - worker_handle.add(); - timer_event_handle.add(); - if scope.from_devtools_sender().is_some() { - devtools_handle.add(); - } - } - let ret = sel.wait(); - if ret == worker_handle.id() { - Ok(MixedMessage::FromWorker(worker_port.recv()?)) - } else if ret == timer_event_handle.id() { - Ok(MixedMessage::FromScheduler(timer_event_port.recv()?)) - } else if ret == devtools_handle.id() { - Ok(MixedMessage::FromDevtools(devtools_port.recv()?)) - } else { - panic!("unexpected select result!") - } - } - fn handle_script_event(&self, msg: WorkerScriptMsg) { match msg { WorkerScriptMsg::DOMMessage(data) => { @@ -321,8 +390,8 @@ impl DedicatedWorkerGlobalScope { } } - fn handle_event(&self, event: MixedMessage) { - match event { + fn handle_mixed_message(&self, msg: MixedMessage) { + match msg { MixedMessage::FromDevtools(msg) => { match msg { DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => @@ -346,10 +415,11 @@ impl DedicatedWorkerGlobalScope { } } } - MixedMessage::FromWorker((linked_worker, msg)) => { + MixedMessage::FromWorker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => { let _ar = AutoWorkerReset::new(self, linked_worker); self.handle_script_event(msg); } + MixedMessage::FromWorker(DedicatedWorkerScriptMsg::WakeUp) => {}, } } @@ -382,8 +452,12 @@ impl DedicatedWorkerGlobalScope { global.report_an_error(error_info, HandleValue::null()); } })); - // TODO: Should use the DOM manipulation task source. - self.parent_sender.send(CommonScriptMsg::Task(WorkerEvent, task, Some(pipeline_id))).unwrap(); + self.parent_sender.send(CommonScriptMsg::Task( + WorkerEvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )).unwrap(); } } @@ -408,7 +482,13 @@ impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope { let task = Box::new(task!(post_worker_message: move || { Worker::handle_message(worker, data); })); - self.parent_sender.send(CommonScriptMsg::Task(WorkerEvent, task, Some(pipeline_id))).unwrap(); + // TODO: Change this task source to a new `unshipped-port-message-queue` task source + self.parent_sender.send(CommonScriptMsg::Task( + WorkerEvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )).unwrap(); Ok(()) } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 5394089d220..cc1a82d04ef 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -2837,7 +2837,14 @@ impl Document { let trusted_pending = Trusted::new(pending); let trusted_promise = TrustedPromise::new(promise.clone()); let handler = ElementPerformFullscreenEnter::new(trusted_pending, trusted_promise, error); - let script_msg = CommonScriptMsg::Task(ScriptThreadEventCategory::EnterFullscreen, handler, pipeline_id); + // NOTE: This steps should be running in parallel + // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen + let script_msg = CommonScriptMsg::Task( + ScriptThreadEventCategory::EnterFullscreen, + handler, + pipeline_id, + TaskSourceName::DOMManipulation, + ); let msg = MainThreadScriptMsg::Common(script_msg); window.main_thread_script_chan().send(msg).unwrap(); @@ -2870,7 +2877,14 @@ impl Document { let trusted_promise = TrustedPromise::new(promise.clone()); let handler = ElementPerformFullscreenExit::new(trusted_element, trusted_promise); let pipeline_id = Some(global.pipeline_id()); - let script_msg = CommonScriptMsg::Task(ScriptThreadEventCategory::ExitFullscreen, handler, pipeline_id); + // NOTE: This steps should be running in parallel + // https://fullscreen.spec.whatwg.org/#exit-fullscreen + let script_msg = CommonScriptMsg::Task( + ScriptThreadEventCategory::ExitFullscreen, + handler, + pipeline_id, + TaskSourceName::DOMManipulation, + ); let msg = MainThreadScriptMsg::Common(script_msg); window.main_thread_script_chan().send(msg).unwrap(); diff --git a/components/script/dom/gainnode.rs b/components/script/dom/gainnode.rs index 5cdbd8a89c9..a48cdc7c86b 100644 --- a/components/script/dom/gainnode.rs +++ b/components/script/dom/gainnode.rs @@ -30,19 +30,22 @@ impl GainNode { pub fn new_inherited( window: &Window, context: &BaseAudioContext, - gain_options: &GainOptions, - ) -> GainNode { + options: &GainOptions, + ) -> Fallible<GainNode> { let mut node_options = AudioNodeOptions::empty(); - node_options.channelCount = Some(2); - node_options.channelCountMode = Some(ChannelCountMode::Max); - node_options.channelInterpretation = Some(ChannelInterpretation::Speakers); + let count = options.parent.channelCount.unwrap_or(2); + let mode = options.parent.channelCountMode.unwrap_or(ChannelCountMode::Max); + let interpretation = options.parent.channelInterpretation.unwrap_or(ChannelInterpretation::Speakers); + node_options.channelCount = Some(count); + node_options.channelCountMode = Some(mode); + node_options.channelInterpretation = Some(interpretation); let node = AudioNode::new_inherited( - AudioNodeInit::GainNode(gain_options.into()), + AudioNodeInit::GainNode(options.into()), context, &node_options, 1, // inputs 1, // outputs - ); + )?; let gain = AudioParam::new( window, context, @@ -53,10 +56,10 @@ impl GainNode { f32::MIN, // min value f32::MAX, // max value ); - GainNode { + Ok(GainNode { node, gain: Dom::from_ref(&gain), - } + }) } #[allow(unrooted_must_root)] @@ -64,9 +67,9 @@ impl GainNode { window: &Window, context: &BaseAudioContext, options: &GainOptions, - ) -> DomRoot<GainNode> { - let node = GainNode::new_inherited(window, context, options); - reflect_dom_object(Box::new(node), window, GainNodeBinding::Wrap) + ) -> Fallible<DomRoot<GainNode>> { + let node = GainNode::new_inherited(window, context, options)?; + Ok(reflect_dom_object(Box::new(node), window, GainNodeBinding::Wrap)) } pub fn Constructor( @@ -74,7 +77,7 @@ impl GainNode { context: &BaseAudioContext, options: &GainOptions, ) -> Fallible<DomRoot<GainNode>> { - Ok(GainNode::new(window, context, options)) + GainNode::new(window, context, options) } } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 596530058ec..b2429948874 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -59,6 +59,7 @@ use task_source::file_reading::FileReadingTaskSource; use task_source::networking::NetworkingTaskSource; use task_source::performance_timeline::PerformanceTimelineTaskSource; use task_source::remote_event::RemoteEventTaskSource; +use task_source::websocket::WebsocketTaskSource; use time::{Timespec, get_time}; use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle}; use timers::{OneshotTimers, TimerCallback}; @@ -430,6 +431,18 @@ impl GlobalScope { unreachable!(); } + /// `ScriptChan` to send messages to the websocket task source of + /// this global scope. + pub fn websocket_task_source(&self) -> WebsocketTaskSource { + if let Some(window) = self.downcast::<Window>() { + return window.websocket_task_source(); + } + if let Some(worker) = self.downcast::<WorkerGlobalScope>() { + return worker.websocket_task_source(); + } + unreachable!(); + } + /// Evaluate JS code on this global scope. pub fn evaluate_js_on_global_with_result( &self, code: &str, rval: MutableHandleValue) -> bool { diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 7261f34b8ca..3aaa7c579cb 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -538,12 +538,17 @@ impl HTMLImageElement { /// https://html.spec.whatwg.org/multipage/#matches-the-environment fn matches_environment(&self, media_query: String) -> bool { let document = document_from_node(self); - let device = document.device(); - if !device.is_some() { - return false; - } + let device = match document.device() { + Some(device) => device, + None => return false, + }; let quirks_mode = document.quirks_mode(); let document_url = &document.url(); + // FIXME(emilio): This should do the same that we do for other media + // lists regarding the rule type and such, though it doesn't really + // matter right now... + // + // Also, ParsingMode::all() is wrong, and should be DEFAULT. let context = ParserContext::new( Origin::Author, document_url, @@ -551,11 +556,12 @@ impl HTMLImageElement { ParsingMode::all(), quirks_mode, None, + None, ); let mut parserInput = ParserInput::new(&media_query); let mut parser = Parser::new(&mut parserInput); let media_list = MediaList::parse(&context, &mut parser); - media_list.evaluate(&device.unwrap(), quirks_mode) + media_list.evaluate(&device, quirks_mode) } /// <https://html.spec.whatwg.org/multipage/#normalise-the-source-densities> @@ -1039,9 +1045,12 @@ pub fn parse_a_sizes_attribute(value: DOMString) -> SourceSizeList { Origin::Author, &url, Some(CssRuleType::Style), + // FIXME(emilio): why ::empty() instead of ::DEFAULT? Also, what do + // browsers do regarding quirks-mode in a media list? ParsingMode::empty(), QuirksMode::NoQuirks, None, + None, ); SourceSizeList::parse(&context, &mut parser) } @@ -1147,10 +1156,16 @@ impl HTMLImageElementMethods for HTMLImageElement { // https://html.spec.whatwg.org/multipage/#dom-img-currentsrc fn CurrentSrc(&self) -> DOMString { - let ref url = self.current_request.borrow().source_url; + let ref url = self.current_request.borrow().parsed_url; match *url { - Some(ref url) => url.clone(), - None => DOMString::from(""), + Some(ref url) => DOMString::from_string(url.clone().into_string()), + None => { + let ref unparsed_url = self.current_request.borrow().source_url; + match *unparsed_url { + Some(ref url) => url.clone(), + None => DOMString::from("") + } + }, } } diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index a86789dcd71..be7b2785ee9 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -287,6 +287,7 @@ impl HTMLLinkElement { ParsingMode::DEFAULT, document.quirks_mode(), window.css_error_reporter(), + None, ); let media = MediaList::parse(&context, &mut css_parser); diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 82da9ba6180..bcecab9088b 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -91,6 +91,7 @@ impl HTMLStyleElement { ParsingMode::DEFAULT, doc.quirks_mode(), css_error_reporter, + None, ); let shared_lock = node.owner_doc().style_shared_lock().clone(); let mut input = ParserInput::new(&mq_str); diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs index d43f6d3a710..99800f1beb1 100644 --- a/components/script/dom/medialist.rs +++ b/components/script/dom/medialist.rs @@ -83,6 +83,7 @@ impl MediaListMethods for MediaList { ParsingMode::DEFAULT, quirks_mode, window.css_error_reporter(), + None, ); *media_queries = StyleMediaList::parse(&context, &mut parser); } @@ -123,6 +124,7 @@ impl MediaListMethods for MediaList { ParsingMode::DEFAULT, quirks_mode, win.css_error_reporter(), + None, ); let m = MediaQuery::parse(&context, &mut parser); // Step 2 @@ -156,6 +158,7 @@ impl MediaListMethods for MediaList { ParsingMode::DEFAULT, quirks_mode, win.css_error_reporter(), + None, ); let m = MediaQuery::parse(&context, &mut parser); // Step 2 diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 758c4284979..04a234a80d3 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -241,6 +241,7 @@ pub mod bluetoothuuid; pub mod canvasgradient; pub mod canvaspattern; pub mod canvasrenderingcontext2d; +pub mod channelmergernode; pub mod characterdata; pub mod client; pub mod closeevent; diff --git a/components/script/dom/offlineaudiocontext.rs b/components/script/dom/offlineaudiocontext.rs index 4490d5df6f9..21376c11a4d 100644 --- a/components/script/dom/offlineaudiocontext.rs +++ b/components/script/dom/offlineaudiocontext.rs @@ -144,6 +144,10 @@ impl OfflineAudioContextMethods for OfflineAudioContext { task!(resolve: move || { let this = this.root(); let processed_audio = processed_audio.lock().unwrap(); + let processed_audio: Vec<_> = processed_audio + .chunks(this.length as usize) + .map(|channel| channel.to_vec()) + .collect(); let buffer = AudioBuffer::new( &this.global().as_window(), this.channel_count, diff --git a/components/script/dom/oscillatornode.rs b/components/script/dom/oscillatornode.rs index f44fa747f7d..662724c4438 100644 --- a/components/script/dom/oscillatornode.rs +++ b/components/script/dom/oscillatornode.rs @@ -35,7 +35,7 @@ impl OscillatorNode { window: &Window, context: &BaseAudioContext, oscillator_options: &OscillatorOptions, - ) -> OscillatorNode { + ) -> Fallible<OscillatorNode> { let mut node_options = AudioNodeOptions::empty(); node_options.channelCount = Some(2); node_options.channelCountMode = Some(ChannelCountMode::Max); @@ -46,7 +46,7 @@ impl OscillatorNode { &node_options, 0, /* inputs */ 1, /* outputs */ - ); + )?; let node_id = source_node.node().node_id(); let frequency = AudioParam::new( window, @@ -69,12 +69,12 @@ impl OscillatorNode { 440. / 2., ); - OscillatorNode { + Ok(OscillatorNode { source_node, oscillator_type: oscillator_options.type_, frequency: Dom::from_ref(&frequency), detune: Dom::from_ref(&detune), - } + }) } #[allow(unrooted_must_root)] @@ -82,9 +82,9 @@ impl OscillatorNode { window: &Window, context: &BaseAudioContext, options: &OscillatorOptions, - ) -> DomRoot<OscillatorNode> { - let node = OscillatorNode::new_inherited(window, context, options); - reflect_dom_object(Box::new(node), window, OscillatorNodeBinding::Wrap) + ) -> Fallible<DomRoot<OscillatorNode>> { + let node = OscillatorNode::new_inherited(window, context, options)?; + Ok(reflect_dom_object(Box::new(node), window, OscillatorNodeBinding::Wrap)) } pub fn Constructor( @@ -92,7 +92,7 @@ impl OscillatorNode { context: &BaseAudioContext, options: &OscillatorOptions, ) -> Fallible<DomRoot<OscillatorNode>> { - Ok(OscillatorNode::new(window, context, options)) + OscillatorNode::new(window, context, options) } } diff --git a/components/script/dom/pannernode.rs b/components/script/dom/pannernode.rs index 7f1909cdb50..a27388f2cab 100644 --- a/components/script/dom/pannernode.rs +++ b/components/script/dom/pannernode.rs @@ -54,16 +54,29 @@ impl PannerNode { ) -> Fallible<PannerNode> { let count = options.parent.channelCount.unwrap_or(2); let mode = options.parent.channelCountMode.unwrap_or(ChannelCountMode::Clamped_max); + let interpretation = options.parent.channelInterpretation.unwrap_or(ChannelInterpretation::Speakers); if mode == ChannelCountMode::Max { return Err(Error::NotSupported) } - if count > 2 { + if count > 2 || count == 0 { return Err(Error::NotSupported) } + if *options.maxDistance <= 0. { + return Err(Error::Range("maxDistance should be positive".into())) + } + if *options.refDistance < 0. { + return Err(Error::Range("refDistance should be non-negative".into())) + } + if *options.rolloffFactor < 0. { + return Err(Error::Range("rolloffFactor should be non-negative".into())) + } + if *options.coneOuterGain < 0. || *options.coneOuterGain > 1. { + return Err(Error::InvalidState) + } let mut node_options = AudioNodeOptions::empty(); node_options.channelCount = Some(count); node_options.channelCountMode = Some(mode); - node_options.channelInterpretation = Some(ChannelInterpretation::Speakers); + node_options.channelInterpretation = Some(interpretation); let options = options.into(); let node = AudioNode::new_inherited( AudioNodeInit::PannerNode(options), @@ -71,7 +84,7 @@ impl PannerNode { &node_options, 1, // inputs 1, // outputs - ); + )?; let id = node.node_id(); let position_x = AudioParam::new( window, @@ -230,10 +243,14 @@ impl PannerNodeMethods for PannerNode { Finite::wrap(self.ref_distance.get()) } // https://webaudio.github.io/web-audio-api/#dom-pannernode-refdistance - fn SetRefDistance(&self, val: Finite<f64>) { + fn SetRefDistance(&self, val: Finite<f64>) -> Fallible<()> { + if *val < 0. { + return Err(Error::Range("value should be non-negative".into())) + } self.ref_distance.set(*val); let msg = PannerNodeMessage::SetRefDistance(self.ref_distance.get()); self.upcast::<AudioNode>().message(AudioNodeMessage::PannerNode(msg)); + Ok(()) } // https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance fn MaxDistance(&self) -> Finite<f64> { @@ -241,8 +258,8 @@ impl PannerNodeMethods for PannerNode { } // https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance fn SetMaxDistance(&self, val: Finite<f64>) -> Fallible<()> { - if *val < 0. { - return Err(Error::NotSupported) + if *val <= 0. { + return Err(Error::Range("value should be positive".into())) } self.max_distance.set(*val); let msg = PannerNodeMessage::SetMaxDistance(self.max_distance.get()); @@ -256,7 +273,7 @@ impl PannerNodeMethods for PannerNode { // https://webaudio.github.io/web-audio-api/#dom-pannernode-rollofffactor fn SetRolloffFactor(&self, val: Finite<f64>) -> Fallible<()> { if *val < 0. { - return Err(Error::Range("value should be positive".into())) + return Err(Error::Range("value should be non-negative".into())) } self.rolloff_factor.set(*val); let msg = PannerNodeMessage::SetRolloff(self.rolloff_factor.get()); @@ -289,7 +306,7 @@ impl PannerNodeMethods for PannerNode { } // https://webaudio.github.io/web-audio-api/#dom-pannernode-coneoutergain fn SetConeOuterGain(&self, val: Finite<f64>) -> Fallible<()> { - if *val < 0. || *val > 360. { + if *val < 0. || *val > 1. { return Err(Error::InvalidState) } self.cone_outer_gain.set(*val); diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 7e737b8d3d0..50080f9925b 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -5,17 +5,20 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; use dom::abstractworker::WorkerScriptMsg; +use dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop}; use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods; use dom::bindings::inheritance::Castable; use dom::bindings::reflector::DomObject; use dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use dom::bindings::str::DOMString; +use dom::dedicatedworkerglobalscope::AutoWorkerReset; use dom::event::Event; use dom::eventtarget::EventTarget; use dom::extendableevent::ExtendableEvent; use dom::extendablemessageevent::ExtendableMessageEvent; use dom::globalscope::GlobalScope; +use dom::worker::TrustedWorkerAddress; use dom::workerglobalscope::WorkerGlobalScope; use dom_struct::dom_struct; use ipc_channel::ipc::{self, IpcSender, IpcReceiver}; @@ -29,17 +32,64 @@ use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorke use servo_config::prefs::PREFS; use servo_rand::random; use servo_url::ServoUrl; -use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; +use std::sync::mpsc::{Receiver, Sender, channel}; use std::thread; use std::time::Duration; use style::thread_state::{self, ThreadState}; +use task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; +use task_source::TaskSourceName; /// Messages used to control service worker event loop pub enum ServiceWorkerScriptMsg { /// Message common to all workers CommonWorker(WorkerScriptMsg), - // Message to request a custom response by the service worker - Response(CustomResponseMediator) + /// Message to request a custom response by the service worker + Response(CustomResponseMediator), + /// Wake-up call from the task queue. + WakeUp, +} + +impl QueuedTaskConversion for ServiceWorkerScriptMsg { + fn task_source_name(&self) -> Option<&TaskSourceName> { + let script_msg = match self { + ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) => script_msg, + _ => return None, + }; + match script_msg { + CommonScriptMsg::Task(_category, _boxed, _pipeline_id, task_source) => Some(&task_source), + _ => return None, + } + } + + fn into_queued_task(self) -> Option<QueuedTask> { + let script_msg = match self { + ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) => script_msg, + _ => return None, + }; + let (category, boxed, pipeline_id, task_source) = match script_msg { + CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => + (category, boxed, pipeline_id, task_source), + _ => return None, + }; + Some((None, category, boxed, pipeline_id, task_source)) + } + + fn from_queued_task(queued_task: QueuedTask) -> Self { + let (_worker, category, boxed, pipeline_id, task_source) = queued_task; + let script_msg = CommonScriptMsg::Task(category, boxed, pipeline_id, task_source); + ServiceWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) + } + + fn wake_up_msg() -> Self { + ServiceWorkerScriptMsg::WakeUp + } + + fn is_wake_up(&self) -> bool { + match self { + ServiceWorkerScriptMsg::WakeUp => true, + _ => false, + } + } } pub enum MixedMessage { @@ -67,11 +117,13 @@ impl ScriptChan for ServiceWorkerChan { } } +unsafe_no_jsmanaged_fields!(TaskQueue<ServiceWorkerScriptMsg>); + #[dom_struct] pub struct ServiceWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, #[ignore_malloc_size_of = "Defined in std"] - receiver: Receiver<ServiceWorkerScriptMsg>, + task_queue: TaskQueue<ServiceWorkerScriptMsg>, #[ignore_malloc_size_of = "Defined in std"] own_sender: Sender<ServiceWorkerScriptMsg>, #[ignore_malloc_size_of = "Defined in std"] @@ -81,6 +133,40 @@ pub struct ServiceWorkerGlobalScope { scope_url: ServoUrl, } +impl WorkerEventLoopMethods for ServiceWorkerGlobalScope { + type TimerMsg = (); + type WorkerMsg = ServiceWorkerScriptMsg; + type Event = MixedMessage; + + fn timer_event_port(&self) -> &Receiver<()> { + &self.timer_event_port + } + + fn task_queue(&self) -> &TaskQueue<ServiceWorkerScriptMsg> { + &self.task_queue + } + + fn handle_event(&self, event: MixedMessage) { + self.handle_mixed_message(event); + } + + fn handle_worker_post_event(&self, _worker: &TrustedWorkerAddress) -> Option<AutoWorkerReset> { + None + } + + fn from_worker_msg(&self, msg: ServiceWorkerScriptMsg) -> MixedMessage { + MixedMessage::FromServiceWorker(msg) + } + + fn from_timer_msg(&self, msg: ()) -> MixedMessage { + MixedMessage::FromTimeoutThread(msg) + } + + fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> MixedMessage { + MixedMessage::FromDevtools(msg) + } +} + impl ServiceWorkerGlobalScope { fn new_inherited(init: WorkerGlobalScopeInit, worker_url: ServoUrl, @@ -100,7 +186,7 @@ impl ServiceWorkerGlobalScope { from_devtools_receiver, timer_event_chan, None), - receiver: receiver, + task_queue: TaskQueue::new(receiver, own_sender.clone()), timer_event_port: timer_event_port, own_sender: own_sender, swmanager_sender: swmanager_sender, @@ -139,6 +225,7 @@ impl ServiceWorkerGlobalScope { } #[allow(unsafe_code)] + // https://html.spec.whatwg.org/multipage/#run-a-worker pub fn run_serviceworker_scope(scope_things: ScopeThings, own_sender: Sender<ServiceWorkerScriptMsg>, receiver: Receiver<ServiceWorkerScriptMsg>, @@ -211,22 +298,18 @@ impl ServiceWorkerGlobalScope { global.dispatch_activate(); let reporter_name = format!("service-worker-reporter-{}", random::<u64>()); scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| { - // https://html.spec.whatwg.org/multipage/#event-loop-processing-model - // Step 1 - while let Ok(event) = global.receive_event() { - // Step 3 - if !global.handle_event(event) { - break; - } - // Step 6 - global.upcast::<GlobalScope>().perform_a_microtask_checkpoint(); + // Step 29, Run the responsible event loop specified by inside settings until it is destroyed. + // The worker processing model remains on this step until the event loop is destroyed, + // which happens after the closing flag is set to true. + while !scope.is_closing() { + run_worker_event_loop(&*global, None); } }, reporter_name, scope.script_chan(), CommonScriptMsg::CollectReports); }).expect("Thread spawning failed"); } - fn handle_event(&self, event: MixedMessage) -> bool { - match event { + fn handle_mixed_message(&self, msg: MixedMessage) -> bool { + match msg { MixedMessage::FromDevtools(msg) => { match msg { DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => @@ -271,38 +354,8 @@ impl ServiceWorkerGlobalScope { // https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/index.html#fetch-event-section self.upcast::<EventTarget>().fire_event(atom!("fetch")); let _ = mediator.response_chan.send(None); - } - } - } - - #[allow(unsafe_code)] - fn receive_event(&self) -> Result<MixedMessage, RecvError> { - let scope = self.upcast::<WorkerGlobalScope>(); - let worker_port = &self.receiver; - let devtools_port = scope.from_devtools_receiver(); - let timer_event_port = &self.timer_event_port; - - let sel = Select::new(); - let mut worker_handle = sel.handle(worker_port); - let mut devtools_handle = sel.handle(devtools_port); - let mut timer_port_handle = sel.handle(timer_event_port); - unsafe { - worker_handle.add(); - if scope.from_devtools_sender().is_some() { - devtools_handle.add(); - } - timer_port_handle.add(); - } - - let ret = sel.wait(); - if ret == worker_handle.id() { - Ok(MixedMessage::FromServiceWorker(worker_port.recv()?)) - }else if ret == devtools_handle.id() { - Ok(MixedMessage::FromDevtools(devtools_port.recv()?)) - } else if ret == timer_port_handle.id() { - Ok(MixedMessage::FromTimeoutThread(timer_event_port.recv()?)) - } else { - panic!("unexpected select result!") + }, + WakeUp => {}, } } diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index c9f24d04ffa..397ec725a4d 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -42,6 +42,7 @@ use std::ops::Deref; use std::rc::Rc; use std::sync::mpsc; use std::thread; +use task_source::TaskSourceName; use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRLayer, WebVRMsg}; #[dom_struct] @@ -517,7 +518,14 @@ impl VRDisplay { let task = Box::new(task!(handle_vrdisplay_raf: move || { this.root().handle_raf(&sender); })); - js_sender.send(CommonScriptMsg::Task(WebVREvent, task, Some(pipeline_id))).unwrap(); + // NOTE: WebVR spec doesn't specify what task source we should use. Is + // dom-manipulation a good choice long term? + js_sender.send(CommonScriptMsg::Task( + WebVREvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )).unwrap(); // Run Sync Poses in parallell on Render thread let msg = WebVRCommand::SyncPoses(display_id, near, far, sync_sender.clone()); diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 811fc100e6b..da50ed556df 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -240,14 +240,18 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.GenerateMipmap(target) } - #[allow(unsafe_code)] /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - unsafe fn BufferData(&self, cx: *mut JSContext, target: u32, data: *mut JSObject, usage: u32) -> Fallible<()> { - self.base.BufferData(cx, target, data, usage) + fn BufferData( + &self, + target: u32, + data: Option<ArrayBufferViewOrArrayBuffer>, + usage: u32, + ) { + self.base.BufferData(target, data, usage) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn BufferData_(&self, target: u32, size: i64, usage: u32) -> Fallible<()> { + fn BufferData_(&self, target: u32, size: i64, usage: u32) { self.base.BufferData_(target, size, usage) } @@ -957,7 +961,10 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { count: i32, primcount: i32, ) { - self.base.draw_arrays_instanced(mode, first, count, primcount); + handle_potential_webgl_error!( + self.base, + self.base.draw_arrays_instanced(mode, first, count, primcount) + ) } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9 @@ -969,7 +976,10 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { offset: i64, primcount: i32, ) { - self.base.draw_elements_instanced(mode, count, type_, offset, primcount); + handle_potential_webgl_error!( + self.base, + self.base.draw_elements_instanced(mode, count, type_, offset, primcount) + ) } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9 diff --git a/components/script/dom/webgl_extensions/ext/angleinstancedarrays.rs b/components/script/dom/webgl_extensions/ext/angleinstancedarrays.rs index f35abaf15af..7c93c8c6567 100644 --- a/components/script/dom/webgl_extensions/ext/angleinstancedarrays.rs +++ b/components/script/dom/webgl_extensions/ext/angleinstancedarrays.rs @@ -71,7 +71,10 @@ impl ANGLEInstancedArraysMethods for ANGLEInstancedArrays { count: i32, primcount: i32, ) { - self.ctx.draw_arrays_instanced(mode, first, count, primcount); + handle_potential_webgl_error!( + self.ctx, + self.ctx.draw_arrays_instanced(mode, first, count, primcount) + ) } // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/ @@ -83,7 +86,10 @@ impl ANGLEInstancedArraysMethods for ANGLEInstancedArrays { offset: i64, primcount: i32, ) { - self.ctx.draw_elements_instanced(mode, count, type_, offset, primcount); + handle_potential_webgl_error!( + self.ctx, + self.ctx.draw_elements_instanced(mode, count, type_, offset, primcount) + ) } fn VertexAttribDivisorANGLE(&self, index: u32, divisor: u32) { diff --git a/components/script/dom/webglbuffer.rs b/components/script/dom/webglbuffer.rs index 66a3ce0af40..c49a3852709 100644 --- a/components/script/dom/webglbuffer.rs +++ b/components/script/dom/webglbuffer.rs @@ -13,6 +13,7 @@ use dom::bindings::root::DomRoot; use dom::webglobject::WebGLObject; use dom::webglrenderingcontext::WebGLRenderingContext; use dom_struct::dom_struct; +use ipc_channel::ipc; use std::cell::Cell; #[dom_struct] @@ -62,10 +63,7 @@ impl WebGLBuffer { self.id } - pub fn buffer_data<T>(&self, target: u32, data: T, usage: u32) -> WebGLResult<()> - where - T: Into<Vec<u8>>, - { + pub fn buffer_data(&self, data: &[u8], usage: u32) -> WebGLResult<()> { match usage { WebGLRenderingContextConstants::STREAM_DRAW | WebGLRenderingContextConstants::STATIC_DRAW | @@ -73,17 +71,13 @@ impl WebGLBuffer { _ => return Err(WebGLError::InvalidEnum), } - if let Some(previous_target) = self.target.get() { - if target != previous_target { - return Err(WebGLError::InvalidOperation); - } - } - let data = data.into(); self.capacity.set(data.len()); self.usage.set(usage); + let (sender, receiver) = ipc::bytes_channel().unwrap(); self.upcast::<WebGLObject>() .context() - .send_command(WebGLCommand::BufferData(target, data.into(), usage)); + .send_command(WebGLCommand::BufferData(self.target.get().unwrap(), receiver, usage)); + sender.send(data).unwrap(); Ok(()) } @@ -154,6 +148,7 @@ impl WebGLBuffer { impl Drop for WebGLBuffer { fn drop(&mut self) { - self.delete(); + self.mark_for_deletion(); + assert!(self.is_deleted()); } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 4aa6c047a1b..ab7178ffa16 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; use canvas_traits::canvas::{byte_swap, multiply_u8_pixel}; use canvas_traits::webgl::{DOMToTextureCommand, Parameter}; use canvas_traits::webgl::{TexParameter, WebGLCommand, WebGLContextShareMode, WebGLError}; @@ -52,6 +52,7 @@ use dom::window::Window; use dom_struct::dom_struct; use euclid::Size2D; use half::f16; +use ipc_channel::ipc; use js::jsapi::{JSContext, JSObject, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value, JSVal}; use js::jsval::{ObjectValue, NullValue, UndefinedValue}; @@ -336,17 +337,12 @@ impl WebGLRenderingContext { // // The WebGL spec mentions a couple more operations that trigger // this: clear() and getParameter(IMPLEMENTATION_COLOR_READ_*). - fn validate_framebuffer_complete(&self) -> bool { + fn validate_framebuffer(&self) -> WebGLResult<()> { match self.bound_framebuffer.get() { - Some(fb) => match fb.check_status() { - constants::FRAMEBUFFER_COMPLETE => return true, - _ => { - self.webgl_error(InvalidFramebufferOperation); - return false; - } + Some(ref fb) if fb.check_status() != constants::FRAMEBUFFER_COMPLETE => { + Err(InvalidFramebufferOperation) }, - // The default framebuffer is always complete. - None => return true, + _ => Ok(()), } } @@ -485,206 +481,6 @@ impl WebGLRenderingContext { } } - // https://en.wikipedia.org/wiki/Relative_luminance - #[inline] - fn luminance(r: u8, g: u8, b: u8) -> u8 { - (0.2126 * (r as f32) + - 0.7152 * (g as f32) + - 0.0722 * (b as f32)) as u8 - } - - /// Translates an image in rgba8 (red in the first byte) format to - /// the format that was requested of TexImage. - /// - /// From the WebGL 1.0 spec, 5.14.8: - /// - /// "The source image data is conceptually first converted to - /// the data type and format specified by the format and type - /// arguments, and then transferred to the WebGL - /// implementation. If a packed pixel format is specified - /// which would imply loss of bits of precision from the image - /// data, this loss of precision must occur." - fn rgba8_image_to_tex_image_data(&self, - format: TexFormat, - data_type: TexDataType, - pixels: Vec<u8>) -> Vec<u8> { - // hint for vector allocation sizing. - let pixel_count = pixels.len() / 4; - - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels, - (TexFormat::RGB, TexDataType::UnsignedByte) => { - // Remove alpha channel - let mut rgb8 = Vec::<u8>::with_capacity(pixel_count * 3); - for rgba8 in pixels.chunks(4) { - rgb8.push(rgba8[0]); - rgb8.push(rgba8[1]); - rgb8.push(rgba8[2]); - } - rgb8 - }, - - (TexFormat::Alpha, TexDataType::UnsignedByte) => { - let mut alpha = Vec::<u8>::with_capacity(pixel_count); - for rgba8 in pixels.chunks(4) { - alpha.push(rgba8[3]); - } - alpha - }, - - (TexFormat::Luminance, TexDataType::UnsignedByte) => { - let mut luminance = Vec::<u8>::with_capacity(pixel_count); - for rgba8 in pixels.chunks(4) { - luminance.push(Self::luminance(rgba8[0], rgba8[1], rgba8[2])); - } - luminance - }, - - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - let mut data = Vec::<u8>::with_capacity(pixel_count * 2); - for rgba8 in pixels.chunks(4) { - data.push(Self::luminance(rgba8[0], rgba8[1], rgba8[2])); - data.push(rgba8[3]); - } - data - }, - - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - let mut rgba4 = Vec::<u8>::with_capacity(pixel_count * 2); - for rgba8 in pixels.chunks(4) { - rgba4.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf0) << 8 | - (rgba8[1] as u16 & 0xf0) << 4 | - (rgba8[2] as u16 & 0xf0) | - (rgba8[3] as u16 & 0xf0) >> 4).unwrap(); - } - rgba4 - } - - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - let mut rgba5551 = Vec::<u8>::with_capacity(pixel_count * 2); - for rgba8 in pixels.chunks(4) { - rgba5551.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf8) << 8 | - (rgba8[1] as u16 & 0xf8) << 3 | - (rgba8[2] as u16 & 0xf8) >> 2 | - (rgba8[3] as u16) >> 7).unwrap(); - } - rgba5551 - } - - (TexFormat::RGB, TexDataType::UnsignedShort565) => { - let mut rgb565 = Vec::<u8>::with_capacity(pixel_count * 2); - for rgba8 in pixels.chunks(4) { - rgb565.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf8) << 8 | - (rgba8[1] as u16 & 0xfc) << 3 | - (rgba8[2] as u16 & 0xf8) >> 3).unwrap(); - } - rgb565 - } - - - (TexFormat::RGBA, TexDataType::Float) => { - let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16); - for rgba8 in pixels.chunks(4) { - rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); - } - rgbaf32 - } - - (TexFormat::RGB, TexDataType::Float) => { - let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12); - for rgba8 in pixels.chunks(4) { - rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); - rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); - } - rgbf32 - } - - (TexFormat::Alpha, TexDataType::Float) => { - let mut alpha = Vec::<u8>::with_capacity(pixel_count * 4); - for rgba8 in pixels.chunks(4) { - alpha.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - } - alpha - }, - - (TexFormat::Luminance, TexDataType::Float) => { - let mut luminance = Vec::<u8>::with_capacity(pixel_count * 4); - for rgba8 in pixels.chunks(4) { - let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]); - luminance.write_f32::<NativeEndian>(p as f32).unwrap(); - } - luminance - }, - - (TexFormat::LuminanceAlpha, TexDataType::Float) => { - let mut data = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]); - data.write_f32::<NativeEndian>(p as f32).unwrap(); - data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); - } - data - }, - - (TexFormat::RGBA, TexDataType::HalfFloat) => { - let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap(); - rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap(); - rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap(); - rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap(); - } - rgbaf16 - }, - - (TexFormat::RGB, TexDataType::HalfFloat) => { - let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6); - for rgba8 in pixels.chunks(4) { - rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap(); - rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap(); - rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap(); - } - rgbf16 - }, - - (TexFormat::Alpha, TexDataType::HalfFloat) => { - let mut alpha = Vec::<u8>::with_capacity(pixel_count * 2); - for rgba8 in pixels.chunks(4) { - alpha.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap(); - } - alpha - }, - - (TexFormat::Luminance, TexDataType::HalfFloat) => { - let mut luminance = Vec::<u8>::with_capacity(pixel_count * 4); - for rgba8 in pixels.chunks(4) { - let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]); - luminance.write_u16::<NativeEndian>(f16::from_f32(p as f32).as_bits()).unwrap(); - } - luminance - }, - - (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) => { - let mut data = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - let p = Self::luminance(rgba8[0], rgba8[1], rgba8[2]); - data.write_u16::<NativeEndian>(f16::from_f32(p as f32).as_bits()).unwrap(); - data.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap(); - } - data - }, - - // Validation should have ensured that we only hit the - // above cases, but we haven't turned the (format, type) - // into an enum yet so there's a default case here. - _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type) - } - } - fn get_image_pixels( &self, source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement, @@ -829,83 +625,51 @@ impl WebGLRenderingContext { /// Performs premultiplication of the pixels if /// UNPACK_PREMULTIPLY_ALPHA_WEBGL is currently enabled. - fn premultiply_pixels(&self, - format: TexFormat, - data_type: TexDataType, - pixels: Vec<u8>) -> Vec<u8> { + fn premultiply_pixels(&self, format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) { if !self.texture_unpacking_settings.get().contains(TextureUnpacking::PREMULTIPLY_ALPHA) { - return pixels; + return; } match (format, data_type) { (TexFormat::RGBA, TexDataType::UnsignedByte) => { - let mut premul = Vec::<u8>::with_capacity(pixels.len()); - for rgba in pixels.chunks(4) { - premul.push(multiply_u8_pixel(rgba[0], rgba[3])); - premul.push(multiply_u8_pixel(rgba[1], rgba[3])); - premul.push(multiply_u8_pixel(rgba[2], rgba[3])); - premul.push(rgba[3]); + for rgba in pixels.chunks_mut(4) { + rgba[0] = multiply_u8_pixel(rgba[0], rgba[3]); + rgba[1] = multiply_u8_pixel(rgba[1], rgba[3]); + rgba[2] = multiply_u8_pixel(rgba[2], rgba[3]); } - premul - } + }, (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - let mut premul = Vec::<u8>::with_capacity(pixels.len()); - for la in pixels.chunks(2) { - premul.push(multiply_u8_pixel(la[0], la[1])); - premul.push(la[1]); + for la in pixels.chunks_mut(2) { + la[0] = multiply_u8_pixel(la[0], la[1]); } - premul - } - + }, (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - let mut premul = Vec::<u8>::with_capacity(pixels.len()); - for mut rgba in pixels.chunks(2) { - let pix = rgba.read_u16::<NativeEndian>().unwrap(); - if pix & (1 << 15) != 0 { - premul.write_u16::<NativeEndian>(pix).unwrap(); - } else { - premul.write_u16::<NativeEndian>(0).unwrap(); + for rgba in pixels.chunks_mut(2) { + if NativeEndian::read_u16(rgba) & 1 == 0 { + NativeEndian::write_u16(rgba, 0); } } - premul - } - + }, (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - let mut premul = Vec::<u8>::with_capacity(pixels.len()); - for mut rgba in pixels.chunks(2) { - let pix = rgba.read_u16::<NativeEndian>().unwrap(); - let extend_to_8_bits = |val| { (val | val << 4) as u8 }; - let r = extend_to_8_bits(pix & 0x000f); - let g = extend_to_8_bits((pix & 0x00f0) >> 4); - let b = extend_to_8_bits((pix & 0x0f00) >> 8); - let a = extend_to_8_bits((pix & 0xf000) >> 12); - - premul.write_u16::<NativeEndian>((multiply_u8_pixel(r, a) & 0xf0) as u16 >> 4 | - (multiply_u8_pixel(g, a) & 0xf0) as u16 | - ((multiply_u8_pixel(b, a) & 0xf0) as u16) << 4 | - pix & 0xf000).unwrap(); + for rgba in pixels.chunks_mut(2) { + let pix = NativeEndian::read_u16(rgba); + let extend_to_8_bits = |val| (val | val << 4) as u8; + let r = extend_to_8_bits(pix >> 12 & 0x0f); + let g = extend_to_8_bits(pix >> 8 & 0x0f); + let b = extend_to_8_bits(pix >> 4 & 0x0f); + let a = extend_to_8_bits(pix & 0x0f); + NativeEndian::write_u16( + rgba, + ((multiply_u8_pixel(r, a) & 0xf0) as u16) << 8 | + ((multiply_u8_pixel(g, a) & 0xf0) as u16) << 4 | + ((multiply_u8_pixel(b, a) & 0xf0) as u16) | + ((a & 0x0f) as u16), + ); } - premul - } - + }, // Other formats don't have alpha, so return their data untouched. - _ => pixels - } - } - - // Remove premultiplied alpha. - // This is only called when texImage2D is called using a canvas2d source and - // UNPACK_PREMULTIPLY_ALPHA_WEBGL is disabled. Pixels got from a canvas2D source - // are always RGBA8 with premultiplied alpha, so we don't have to worry about - // additional formats as happens in the premultiply_pixels method. - fn remove_premultiplied_alpha(&self, mut pixels: Vec<u8>) -> Vec<u8> { - for rgba in pixels.chunks_mut(4) { - let a = (rgba[3] as f32) / 255.0; - rgba[0] = (rgba[0] as f32 / a) as u8; - rgba[1] = (rgba[1] as f32 / a) as u8; - rgba[2] = (rgba[2] as f32 / a) as u8; + _ => {}, } - pixels } fn prepare_pixels(&self, @@ -921,16 +685,16 @@ impl WebGLRenderingContext { if !source_premultiplied && dest_premultiply { if source_from_image_or_canvas { // When the pixels come from image or canvas or imagedata, use RGBA8 format - pixels = self.premultiply_pixels(TexFormat::RGBA, TexDataType::UnsignedByte, pixels); + self.premultiply_pixels(TexFormat::RGBA, TexDataType::UnsignedByte, &mut pixels); } else { - pixels = self.premultiply_pixels(internal_format, data_type, pixels); + self.premultiply_pixels(internal_format, data_type, &mut pixels); } } else if source_premultiplied && !dest_premultiply { - pixels = self.remove_premultiplied_alpha(pixels); + remove_premultiplied_alpha(&mut pixels); } if source_from_image_or_canvas { - pixels = self.rgba8_image_to_tex_image_data(internal_format, data_type, pixels); + pixels = rgba8_image_to_tex_image_data(internal_format, data_type, pixels); } // FINISHME: Consider doing premultiply and flip in a single mutable Vec. @@ -970,17 +734,17 @@ impl WebGLRenderingContext { let internal_format = self.extension_manager.get_effective_tex_internal_format(format, data_type); // TODO(emilio): convert colorspace if requested - let msg = WebGLCommand::TexImage2D( + let (sender, receiver) = ipc::bytes_channel().unwrap(); + self.send_command(WebGLCommand::TexImage2D( target.as_gl_constant(), level as i32, internal_format as i32, width as i32, height as i32, format, data_type, - pixels.into(), - ); - - self.send_command(msg); + receiver, + )); + sender.send(&pixels).unwrap(); if let Some(fb) = self.bound_framebuffer.get() { fb.invalidate_texture(&*texture); @@ -1025,7 +789,8 @@ impl WebGLRenderingContext { self.send_command(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32)); // TODO(emilio): convert colorspace if requested - let msg = WebGLCommand::TexSubImage2D( + let (sender, receiver) = ipc::bytes_channel().unwrap(); + self.send_command(WebGLCommand::TexSubImage2D( target.as_gl_constant(), level as i32, xoffset, @@ -1034,10 +799,9 @@ impl WebGLRenderingContext { height as i32, format.as_gl_constant(), data_type.as_gl_constant(), - pixels.into(), - ); - - self.send_command(msg); + receiver, + )); + sender.send(&pixels).unwrap(); } fn get_gl_extensions(&self) -> String { @@ -1076,48 +840,38 @@ impl WebGLRenderingContext { first: i32, count: i32, primcount: i32, - ) { + ) -> WebGLResult<()> { match mode { constants::POINTS | constants::LINE_STRIP | constants::LINE_LOOP | constants::LINES | constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN | constants::TRIANGLES => {}, _ => { - return self.webgl_error(InvalidEnum); + return Err(InvalidEnum); } } if first < 0 || count < 0 || primcount < 0 { - return self.webgl_error(InvalidValue); + return Err(InvalidValue); } - let current_program = handle_potential_webgl_error!( - self, - self.current_program.get().ok_or(InvalidOperation), - return - ); + let current_program = self.current_program.get().ok_or(InvalidOperation)?; let required_len = if count > 0 { - handle_potential_webgl_error!( - self, - first.checked_add(count).map(|len| len as u32).ok_or(InvalidOperation), - return - ) + first.checked_add(count).map(|len| len as u32).ok_or(InvalidOperation)? } else { 0 }; - handle_potential_webgl_error!( - self, - self.current_vao().validate_for_draw(required_len, primcount as u32, ¤t_program.active_attribs()), - return - ); + self.current_vao().validate_for_draw( + required_len, + primcount as u32, + ¤t_program.active_attribs(), + )?; - if !self.validate_framebuffer_complete() { - return; - } + self.validate_framebuffer()?; if count == 0 || primcount == 0 { - return; + return Ok(()); } self.send_command(if primcount == 1 { @@ -1126,6 +880,7 @@ impl WebGLRenderingContext { WebGLCommand::DrawArraysInstanced { mode, first, count, primcount } }); self.mark_as_dirty(); + Ok(()) } // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/ @@ -1136,60 +891,47 @@ impl WebGLRenderingContext { type_: u32, offset: i64, primcount: i32, - ) { + ) -> WebGLResult<()> { match mode { constants::POINTS | constants::LINE_STRIP | constants::LINE_LOOP | constants::LINES | constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN | constants::TRIANGLES => {}, _ => { - return self.webgl_error(InvalidEnum); + return Err(InvalidEnum); } } if count < 0 || offset < 0 || primcount < 0 { - return self.webgl_error(InvalidValue); + return Err(InvalidValue); } let type_size = match type_ { constants::UNSIGNED_BYTE => 1, constants::UNSIGNED_SHORT => 2, constants::UNSIGNED_INT if self.extension_manager.is_element_index_uint_enabled() => 4, - _ => return self.webgl_error(InvalidEnum), + _ => return Err(InvalidEnum), }; if offset % type_size != 0 { - return self.webgl_error(InvalidOperation); + return Err(InvalidOperation); } - let current_program = handle_potential_webgl_error!( - self, - self.current_program.get().ok_or(InvalidOperation), - return - ); + let current_program = self.current_program.get().ok_or(InvalidOperation)?; + let array_buffer = self.current_vao().element_array_buffer().get().ok_or(InvalidOperation)?; if count > 0 && primcount > 0 { - if let Some(array_buffer) = self.current_vao().element_array_buffer().get() { - // This operation cannot overflow in u64 and we know all those values are nonnegative. - let val = offset as u64 + (count as u64 * type_size as u64); - if val > array_buffer.capacity() as u64 { - return self.webgl_error(InvalidOperation); - } - } else { - return self.webgl_error(InvalidOperation); + // This operation cannot overflow in u64 and we know all those values are nonnegative. + let val = offset as u64 + (count as u64 * type_size as u64); + if val > array_buffer.capacity() as u64 { + return Err(InvalidOperation); } } // TODO(nox): Pass the correct number of vertices required. - handle_potential_webgl_error!( - self, - self.current_vao().validate_for_draw(0, primcount as u32, ¤t_program.active_attribs()), - return - ); + self.current_vao().validate_for_draw(0, primcount as u32, ¤t_program.active_attribs())?; - if !self.validate_framebuffer_complete() { - return; - } + self.validate_framebuffer()?; if count == 0 || primcount == 0 { - return; + return Ok(()); } let offset = offset as u32; @@ -1199,6 +941,7 @@ impl WebGLRenderingContext { WebGLCommand::DrawElementsInstanced { mode, count, type_, offset, primcount } }); self.mark_as_dirty(); + Ok(()) } pub fn vertex_attrib_divisor(&self, index: u32, divisor: u32) { @@ -1216,20 +959,18 @@ impl WebGLRenderingContext { // can fail and that it is UB what happens in that case. // // https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2 - pub fn get_image_data(&self, mut width: u32, mut height: u32) -> Option<Vec<u8>> { - if !self.validate_framebuffer_complete() { - return None; - } + pub fn get_image_data(&self, width: u32, height: u32) -> Option<Vec<u8>> { + handle_potential_webgl_error!(self, self.validate_framebuffer(), return None); - if let Some((fb_width, fb_height)) = self.get_current_framebuffer_size() { - width = cmp::min(width, fb_width as u32); - height = cmp::min(height, fb_height as u32); - } else { - self.webgl_error(InvalidOperation); - return None; - } + let (fb_width, fb_height) = handle_potential_webgl_error!( + self, + self.get_current_framebuffer_size().ok_or(InvalidOperation), + return None + ); + let width = cmp::min(width, fb_width as u32); + let height = cmp::min(height, fb_height as u32); - let (sender, receiver) = webgl_channel().unwrap(); + let (sender, receiver) = ipc::bytes_channel().unwrap(); self.send_command(WebGLCommand::ReadPixels( 0, 0, @@ -1239,7 +980,7 @@ impl WebGLRenderingContext { constants::UNSIGNED_BYTE, sender, )); - Some(receiver.recv().unwrap().into()) + Some(receiver.recv().unwrap()) } pub fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> { @@ -1324,19 +1065,6 @@ impl Drop for WebGLRenderingContext { } } -#[allow(unsafe_code)] -unsafe fn fallible_array_buffer_view_to_vec( - cx: *mut JSContext, - abv: *mut JSObject, -) -> Result<Vec<u8>, Error> { - assert!(!abv.is_null()); - typedarray!(in(cx) let array_buffer_view: ArrayBufferView = abv); - match array_buffer_view { - Ok(v) => Ok(v.to_vec()), - Err(_) => Err(Error::Type("Not an ArrayBufferView".to_owned())), - } -} - impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1 fn Canvas(&self) -> DomRoot<HTMLCanvasElement> { @@ -1436,18 +1164,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // GL_OES_read_format support (assuming an underlying GLES // driver. Desktop is happy to format convert for us). constants::IMPLEMENTATION_COLOR_READ_FORMAT => { - if !self.validate_framebuffer_complete() { - return NullValue(); - } else { - return Int32Value(constants::RGBA as i32); - } + handle_potential_webgl_error!(self, self.validate_framebuffer(), return NullValue()); + return Int32Value(constants::RGBA as i32); } constants::IMPLEMENTATION_COLOR_READ_TYPE => { - if !self.validate_framebuffer_complete() { - return NullValue(); - } else { - return Int32Value(constants::UNSIGNED_BYTE as i32); - } + handle_potential_webgl_error!(self, self.validate_framebuffer(), return NullValue()); + return Int32Value(constants::UNSIGNED_BYTE as i32); } constants::COMPRESSED_TEXTURE_FORMATS => { // FIXME(nox): https://github.com/servo/servo/issues/20594 @@ -1892,80 +1614,71 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { handle_potential_webgl_error!(self, texture.generate_mipmap()); } - #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - unsafe fn BufferData( + #[allow(unsafe_code)] + fn BufferData( &self, - cx: *mut JSContext, target: u32, - data: *mut JSObject, + data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32, - ) -> ErrorResult { - if data.is_null() { - return Ok(self.webgl_error(InvalidValue)); - } + ) { + let data = handle_potential_webgl_error!(self, data.ok_or(InvalidValue), return); - typedarray!(in(cx) let array_buffer: ArrayBuffer = data); - let data_vec = match array_buffer { - Ok(data) => data.to_vec(), - Err(_) => fallible_array_buffer_view_to_vec(cx, data)?, - }; + let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); + let bound_buffer = handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return); - let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return Ok(())); - let bound_buffer = match bound_buffer { - Some(bound_buffer) => bound_buffer, - None => return Ok(self.webgl_error(InvalidOperation)), + let data = unsafe { + // Safe because we don't do anything with JS until the end of the method. + match data { + ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(), + ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(), + } }; - - handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, data_vec, usage)); - Ok(()) + handle_potential_webgl_error!(self, bound_buffer.buffer_data(data, usage)); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 - fn BufferData_(&self, target: u32, size: i64, usage: u32) -> ErrorResult { - let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return Ok(())); - let bound_buffer = match bound_buffer { - Some(bound_buffer) => bound_buffer, - None => return Ok(self.webgl_error(InvalidOperation)), - }; + fn BufferData_(&self, target: u32, size: i64, usage: u32) { + let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); + let bound_buffer = handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return); if size < 0 { - return Ok(self.webgl_error(InvalidValue)); + return self.webgl_error(InvalidValue); } // FIXME: Allocating a buffer based on user-requested size is // not great, but we don't have a fallible allocation to try. let data = vec![0u8; size as usize]; - handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, data, usage)); - Ok(()) + handle_potential_webgl_error!(self, bound_buffer.buffer_data(&data, usage)); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 + #[allow(unsafe_code)] fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) { - let data_vec = match data { - // Typed array is rooted, so we can safely temporarily retrieve its slice - ArrayBufferViewOrArrayBuffer::ArrayBuffer(inner) => inner.to_vec(), - ArrayBufferViewOrArrayBuffer::ArrayBufferView(inner) => inner.to_vec(), - }; - let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); - let bound_buffer = match bound_buffer { - Some(bound_buffer) => bound_buffer, - None => return self.webgl_error(InvalidOperation), - }; + let bound_buffer = handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return); if offset < 0 { return self.webgl_error(InvalidValue); } - if (offset as usize) + data_vec.len() > bound_buffer.capacity() { + let data = unsafe { + // Safe because we don't do anything with JS until the end of the method. + match data { + ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(), + ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(), + } + }; + if (offset as u64) + data.len() as u64 > bound_buffer.capacity() as u64 { return self.webgl_error(InvalidValue); } + let (sender, receiver) = ipc::bytes_channel().unwrap(); self.send_command(WebGLCommand::BufferSubData( target, offset as isize, - data_vec.into(), + receiver, )); + sender.send(data).unwrap(); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -1989,9 +1702,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn CopyTexImage2D(&self, target: u32, level: i32, internal_format: u32, x: i32, y: i32, width: i32, height: i32, border: i32) { - if !self.validate_framebuffer_complete() { - return; - } + handle_potential_webgl_error!(self, self.validate_framebuffer(), return); let validator = CommonTexImage2DValidator::new(self, target, level, internal_format, width, @@ -2046,9 +1757,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32, x: i32, y: i32, width: i32, height: i32) { - if !self.validate_framebuffer_complete() { - return; - } + handle_potential_webgl_error!(self, self.validate_framebuffer(), return); // NB: We use a dummy (valid) format and border in order to reuse the // common validations, but this should have its own validator. @@ -2089,9 +1798,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 fn Clear(&self, mask: u32) { - if !self.validate_framebuffer_complete() { - return; - } + handle_potential_webgl_error!(self, self.validate_framebuffer(), return); if mask & !(constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT | constants::COLOR_BUFFER_BIT) != 0 { return self.webgl_error(InvalidValue); } @@ -2347,12 +2054,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 fn DrawArrays(&self, mode: u32, first: i32, count: i32) { - self.draw_arrays_instanced(mode, first, count, 1); + handle_potential_webgl_error!(self, self.draw_arrays_instanced(mode, first, count, 1)); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) { - self.draw_elements_instanced(mode, count, type_, offset, 1); + handle_potential_webgl_error!(self, self.draw_elements_instanced(mode, count, type_, offset, 1)); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -2757,7 +2464,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool { buffer.map_or(false, |buf| { - self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_marked_for_deletion() + self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted() }) } @@ -2874,9 +2581,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Some(ref mut data) => (data.get_array_type(), unsafe { data.as_mut_slice() }), }; - if !self.validate_framebuffer_complete() { - return; - } + handle_potential_webgl_error!(self, self.validate_framebuffer(), return); match array_type { Type::Uint8 => (), @@ -2957,7 +2662,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { _ => return self.webgl_error(InvalidOperation), }; - let (sender, receiver) = webgl_channel().unwrap(); + let (sender, receiver) = ipc::bytes_channel().unwrap(); self.send_command(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender)); let result = receiver.recv().unwrap(); @@ -3643,7 +3348,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, }; if values.len() < 1 { - return self.webgl_error(InvalidOperation); + // https://github.com/KhronosGroup/WebGL/issues/2700 + return self.webgl_error(InvalidValue); } self.vertex_attrib(indx, values[0], 0f32, 0f32, 1f32); } @@ -3660,7 +3366,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, }; if values.len() < 2 { - return self.webgl_error(InvalidOperation); + // https://github.com/KhronosGroup/WebGL/issues/2700 + return self.webgl_error(InvalidValue); } self.vertex_attrib(indx, values[0], values[1], 0f32, 1f32); } @@ -3677,7 +3384,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, }; if values.len() < 3 { - return self.webgl_error(InvalidOperation); + // https://github.com/KhronosGroup/WebGL/issues/2700 + return self.webgl_error(InvalidValue); } self.vertex_attrib(indx, values[0], values[1], values[2], 1f32); } @@ -3694,7 +3402,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, }; if values.len() < 4 { - return self.webgl_error(InvalidOperation); + // https://github.com/KhronosGroup/WebGL/issues/2700 + return self.webgl_error(InvalidValue); } self.vertex_attrib(indx, values[0], values[1], values[2], values[3]); } @@ -3742,7 +3451,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { border: i32, format: u32, data_type: u32, - mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>, + pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>, ) -> ErrorResult { if !self.extension_manager.is_tex_type_enabled(data_type) { return Ok(self.webgl_error(InvalidEnum)); @@ -3780,7 +3489,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // initialized to 0 is passed. let buff = match *pixels { None => vec![0u8; expected_byte_length as usize], - Some(ref mut data) => data.to_vec(), + Some(ref data) => data.to_vec(), }; // From the WebGL spec: @@ -4271,3 +3980,217 @@ impl TextureUnit { None } } + +// Remove premultiplied alpha. +// This is only called when texImage2D is called using a canvas2d source and +// UNPACK_PREMULTIPLY_ALPHA_WEBGL is disabled. Pixels got from a canvas2D source +// are always RGBA8 with premultiplied alpha, so we don't have to worry about +// additional formats as happens in the premultiply_pixels method. +fn remove_premultiplied_alpha(pixels: &mut [u8]) { + for rgba in pixels.chunks_mut(4) { + let a = (rgba[3] as f32) / 255.0; + rgba[0] = (rgba[0] as f32 / a) as u8; + rgba[1] = (rgba[1] as f32 / a) as u8; + rgba[2] = (rgba[2] as f32 / a) as u8; + } +} + +/// Translates an image in rgba8 (red in the first byte) format to +/// the format that was requested of TexImage. +/// +/// From the WebGL 1.0 spec, 5.14.8: +/// +/// "The source image data is conceptually first converted to +/// the data type and format specified by the format and type +/// arguments, and then transferred to the WebGL +/// implementation. If a packed pixel format is specified +/// which would imply loss of bits of precision from the image +/// data, this loss of precision must occur." +fn rgba8_image_to_tex_image_data( + format: TexFormat, + data_type: TexDataType, + mut pixels: Vec<u8>, +) -> Vec<u8> { + // hint for vector allocation sizing. + let pixel_count = pixels.len() / 4; + + match (format, data_type) { + (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels, + (TexFormat::RGB, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let rgb = { + let rgb = &pixels[i * 4..i * 4 + 3]; + [rgb[0], rgb[1], rgb[2]] + }; + pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb); + } + pixels.truncate(pixel_count * 3); + pixels + }, + (TexFormat::Alpha, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let p = pixels[i * 4 + 3]; + pixels[i] = p; + } + pixels.truncate(pixel_count); + pixels + }, + (TexFormat::Luminance, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let p = pixels[i * 4]; + pixels[i] = p; + } + pixels.truncate(pixel_count); + pixels + }, + (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { + for i in 0..pixel_count { + let (lum, a) = { + let rgba = &pixels[i * 4..i * 4 + 4]; + (rgba[0], rgba[3]) + }; + pixels[i * 2] = lum; + pixels[i * 2 + 1] = a; + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { + for i in 0..pixel_count { + let p = { + let rgba = &pixels[i * 4..i * 4 + 4]; + (rgba[0] as u16 & 0xf0) << 8 | + (rgba[1] as u16 & 0xf0) << 4 | + (rgba[2] as u16 & 0xf0) | + (rgba[3] as u16 & 0xf0) >> 4 + }; + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { + for i in 0..pixel_count { + let p = { + let rgba = &pixels[i * 4..i * 4 + 4]; + (rgba[0] as u16 & 0xf8) << 8 | + (rgba[1] as u16 & 0xf8) << 3 | + (rgba[2] as u16 & 0xf8) >> 2 | + (rgba[3] as u16) >> 7 + }; + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGB, TexDataType::UnsignedShort565) => { + for i in 0..pixel_count { + let p = { + let rgb = &pixels[i * 4..i * 4 + 3]; + (rgb[0] as u16 & 0xf8) << 8 | + (rgb[1] as u16 & 0xfc) << 3 | + (rgb[2] as u16 & 0xf8) >> 3 + }; + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::RGBA, TexDataType::Float) => { + let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16); + for rgba8 in pixels.chunks(4) { + rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); + rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); + rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); + rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); + } + rgbaf32 + } + + (TexFormat::RGB, TexDataType::Float) => { + let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12); + for rgba8 in pixels.chunks(4) { + rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); + rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); + rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); + } + rgbf32 + } + + (TexFormat::Alpha, TexDataType::Float) => { + for rgba8 in pixels.chunks_mut(4) { + let p = rgba8[3] as f32; + NativeEndian::write_f32(rgba8, p); + } + pixels + }, + + (TexFormat::Luminance, TexDataType::Float) => { + for rgba8 in pixels.chunks_mut(4) { + let p = rgba8[0] as f32; + NativeEndian::write_f32(rgba8, p); + } + pixels + }, + + (TexFormat::LuminanceAlpha, TexDataType::Float) => { + let mut data = Vec::<u8>::with_capacity(pixel_count * 8); + for rgba8 in pixels.chunks(4) { + data.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); + data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); + } + data + }, + + (TexFormat::RGBA, TexDataType::HalfFloat) => { + let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8); + for rgba8 in pixels.chunks(4) { + rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap(); + rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap(); + rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap(); + rgbaf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).as_bits()).unwrap(); + } + rgbaf16 + }, + + (TexFormat::RGB, TexDataType::HalfFloat) => { + let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6); + for rgba8 in pixels.chunks(4) { + rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).as_bits()).unwrap(); + rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).as_bits()).unwrap(); + rgbf16.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).as_bits()).unwrap(); + } + rgbf16 + }, + (TexFormat::Alpha, TexDataType::HalfFloat) => { + for i in 0..pixel_count { + let p = f16::from_f32(pixels[i * 4 + 3] as f32).as_bits(); + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::Luminance, TexDataType::HalfFloat) => { + for i in 0..pixel_count { + let p = f16::from_f32(pixels[i * 4] as f32).as_bits(); + NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); + } + pixels.truncate(pixel_count * 2); + pixels + }, + (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) => { + for rgba8 in pixels.chunks_mut(4) { + let lum = f16::from_f32(rgba8[0] as f32).as_bits(); + let a = f16::from_f32(rgba8[3] as f32).as_bits(); + NativeEndian::write_u16(&mut rgba8[0..2], lum); + NativeEndian::write_u16(&mut rgba8[2..4], a); + } + pixels + }, + + // Validation should have ensured that we only hit the + // above cases, but we haven't turned the (format, type) + // into an enum yet so there's a default case here. + _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type) + } +} diff --git a/components/script/dom/webglvertexarrayobjectoes.rs b/components/script/dom/webglvertexarrayobjectoes.rs index c717108b2aa..ca13b2833f5 100644 --- a/components/script/dom/webglvertexarrayobjectoes.rs +++ b/components/script/dom/webglvertexarrayobjectoes.rs @@ -124,8 +124,15 @@ impl WebGLVertexArrayObjectOES { } let context = self.upcast::<WebGLObject>().context(); - let buffer = context.array_buffer().ok_or(WebGLError::InvalidOperation)?; - buffer.increment_attached_counter(); + let buffer = context.array_buffer(); + match buffer { + Some(ref buffer) => buffer.increment_attached_counter(), + None if offset != 0 => { + // https://github.com/KhronosGroup/WebGL/pull/2228 + return Err(WebGLError::InvalidOperation) + }, + _ => {}, + } context.send_command(WebGLCommand::VertexAttribPointer( index, size, @@ -146,7 +153,7 @@ impl WebGLVertexArrayObjectOES { normalized, stride: stride as u8, offset: offset as u32, - buffer: Some(Dom::from_ref(&*buffer)), + buffer: buffer.map(|b| Dom::from_ref(&*b)), divisor: data.divisor, }; diff --git a/components/script/dom/webidls/BaseAudioContext.webidl b/components/script/dom/webidls/BaseAudioContext.webidl index f00ec373667..f8b85007a64 100644 --- a/components/script/dom/webidls/BaseAudioContext.webidl +++ b/components/script/dom/webidls/BaseAudioContext.webidl @@ -30,13 +30,13 @@ interface BaseAudioContext : EventTarget { Promise<AudioBuffer> decodeAudioData(ArrayBuffer audioData, optional DecodeSuccessCallback successCallback, optional DecodeErrorCallback errorCallback); - AudioBufferSourceNode createBufferSource(); + [Throws] AudioBufferSourceNode createBufferSource(); // ConstantSourceNode createConstantSource(); // ScriptProcessorNode createScriptProcessor(optional unsigned long bufferSize = 0, // optional unsigned long numberOfInputChannels = 2, // optional unsigned long numberOfOutputChannels = 2); // AnalyserNode createAnalyser(); - GainNode createGain(); + [Throws] GainNode createGain(); // DelayNode createDelay(optional double maxDelayTime = 1); // BiquadFilterNode createBiquadFilter(); // IIRFilterNode createIIRFilter(sequence<double> feedforward, @@ -46,9 +46,9 @@ interface BaseAudioContext : EventTarget { // StereoPannerNode createStereoPanner(); // ConvolverNode createConvolver(); // ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6); - // ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6); + [Throws] ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6); // DynamicsCompressorNode createDynamicsCompressor(); - OscillatorNode createOscillator(); + [Throws] OscillatorNode createOscillator(); // PeriodicWave createPeriodicWave(sequence<float> real, // sequence<float> imag, // optional PeriodicWaveConstraints constraints); diff --git a/components/script/dom/webidls/ChannelMergerNode.webidl b/components/script/dom/webidls/ChannelMergerNode.webidl new file mode 100644 index 00000000000..071da8971a0 --- /dev/null +++ b/components/script/dom/webidls/ChannelMergerNode.webidl @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * The origin of this IDL file is + * https://webaudio.github.io/web-audio-api/#channelmergernode + */ + +dictionary ChannelMergerOptions : AudioNodeOptions { + unsigned long numberOfInputs = 6; +}; + +[Exposed=Window, + Constructor (BaseAudioContext context, optional ChannelMergerOptions options)] +interface ChannelMergerNode : AudioNode { +}; diff --git a/components/script/dom/webidls/PannerNode.webidl b/components/script/dom/webidls/PannerNode.webidl index 18d01f52b11..b1c1cdfb68f 100644 --- a/components/script/dom/webidls/PannerNode.webidl +++ b/components/script/dom/webidls/PannerNode.webidl @@ -45,7 +45,7 @@ interface PannerNode : AudioNode { readonly attribute AudioParam orientationY; readonly attribute AudioParam orientationZ; attribute DistanceModelType distanceModel; - attribute double refDistance; + [SetterThrows] attribute double refDistance; [SetterThrows] attribute double maxDistance; [SetterThrows] attribute double rolloffFactor; attribute double coneInnerAngle; diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index b14711d107e..88a2fb273b6 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -305,10 +305,7 @@ interface WebGL2RenderingContextBase /* Buffer objects */ // WebGL1: // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - // FIXME(xanewok): https://github.com/servo/servo/issues/20513 - [Throws] - void bufferData(GLenum target, object? data, GLenum usage); - [Throws] + 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: diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index d36154c7355..2a5d8cf99d0 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -421,7 +421,8 @@ interface WebGLRenderingContextBase const GLenum RGB5_A1 = 0x8057; const GLenum RGB565 = 0x8D62; const GLenum DEPTH_COMPONENT16 = 0x81A5; - const GLenum STENCIL_INDEX = 0x1901; + // https://github.com/KhronosGroup/WebGL/pull/2371 + // const GLenum STENCIL_INDEX = 0x1901; const GLenum STENCIL_INDEX8 = 0x8D48; const GLenum DEPTH_STENCIL = 0x84F9; @@ -687,10 +688,7 @@ interface WebGLRenderingContext { // BUG: https://github.com/KhronosGroup/WebGL/issues/2216 - // FIXME(xanewok): https://github.com/servo/servo/issues/20513 - [Throws] - void bufferData(GLenum target, object? data, GLenum usage); - [Throws] + void bufferData(GLenum target, /*[AllowShared]*/ BufferSource? data, GLenum usage); void bufferData(GLenum target, GLsizeiptr size, GLenum usage); void bufferSubData(GLenum target, GLintptr offset, /*[AllowShared]*/ BufferSource data); diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 2de8f97d98e..ac0f14ad436 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -39,8 +39,8 @@ use std::cell::Cell; use std::ptr; use std::thread; use task::{TaskOnce, TaskCanceller}; -use task_source::{TaskSource, TaskSourceName}; -use task_source::networking::NetworkingTaskSource; +use task_source::TaskSource; +use task_source::websocket::WebsocketTaskSource; #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)] enum WebSocketRequestState { @@ -70,7 +70,7 @@ mod close_code { pub fn close_the_websocket_connection( address: Trusted<WebSocket>, - task_source: &NetworkingTaskSource, + task_source: &WebsocketTaskSource, canceller: &TaskCanceller, code: Option<u16>, reason: String, @@ -86,7 +86,7 @@ pub fn close_the_websocket_connection( pub fn fail_the_websocket_connection( address: Trusted<WebSocket>, - task_source: &NetworkingTaskSource, + task_source: &WebsocketTaskSource, canceller: &TaskCanceller, ) { let close_task = CloseTask { @@ -199,11 +199,8 @@ impl WebSocket { }; let _ = global.core_resource_thread().send(CoreResourceMsg::Fetch(request, channels)); - // TODO: use a dedicated task source, - // https://html.spec.whatwg.org/multipage/#websocket-task-source - // When making the switch, also update the task_canceller call. - let task_source = global.networking_task_source(); - let canceller = global.task_canceller(TaskSourceName::Networking); + let task_source = global.websocket_task_source(); + let canceller = global.task_canceller(WebsocketTaskSource::NAME); thread::spawn(move || { while let Ok(event) = dom_event_receiver.recv() { match event { @@ -268,7 +265,13 @@ impl WebSocket { let pipeline_id = self.global().pipeline_id(); self.global() .script_chan() - .send(CommonScriptMsg::Task(WebSocketEvent, task, Some(pipeline_id))) + // TODO: Use a dedicated `websocket-task-source` task source instead. + .send(CommonScriptMsg::Task( + WebSocketEvent, + task, + Some(pipeline_id), + WebsocketTaskSource::NAME, + )) .unwrap(); } @@ -401,10 +404,10 @@ impl WebSocketMethods for WebSocket { // TODO: use a dedicated task source, // https://html.spec.whatwg.org/multipage/#websocket-task-source // When making the switch, also update the task_canceller call. - let task_source = self.global().networking_task_source(); + let task_source = self.global().websocket_task_source(); fail_the_websocket_connection(address, &task_source, - &self.global().task_canceller(TaskSourceName::Networking)); + &self.global().task_canceller(WebsocketTaskSource::NAME)); } WebSocketRequestState::Open => { self.ready_state.set(WebSocketRequestState::Closing); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index e125aa87fd9..052f121fb2a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -128,6 +128,7 @@ use task_source::networking::NetworkingTaskSource; use task_source::performance_timeline::PerformanceTimelineTaskSource; use task_source::remote_event::RemoteEventTaskSource; use task_source::user_interaction::UserInteractionTaskSource; +use task_source::websocket::WebsocketTaskSource; use time; use timers::{IsInterval, TimerCallback}; use url::Position; @@ -186,6 +187,8 @@ pub struct Window { performance_timeline_task_source: PerformanceTimelineTaskSource, #[ignore_malloc_size_of = "task sources are hard"] remote_event_task_source: RemoteEventTaskSource, + #[ignore_malloc_size_of = "task sources are hard"] + websocket_task_source: WebsocketTaskSource, navigator: MutNullableDom<Navigator>, #[ignore_malloc_size_of = "Arc"] image_cache: Arc<ImageCache>, @@ -376,6 +379,10 @@ impl Window { self.remote_event_task_source.clone() } + pub fn websocket_task_source(&self) -> WebsocketTaskSource { + self.websocket_task_source.clone() + } + pub fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> { &self.script_chan.0 } @@ -1089,6 +1096,7 @@ impl WindowMethods for Window { ParsingMode::DEFAULT, quirks_mode, self.css_error_reporter(), + None, ); let media_query_list = media_queries::MediaList::parse(&context, &mut parser); @@ -1666,7 +1674,8 @@ impl Window { let _ = self.script_chan.send(CommonScriptMsg::Task( ScriptThreadEventCategory::DomEvent, Box::new(self.task_canceller(TaskSourceName::DOMManipulation).wrap_task(task)), - self.pipeline_id() + self.pipeline_id(), + TaskSourceName::DOMManipulation, )); doc.set_url(url.clone()); return @@ -1902,6 +1911,7 @@ impl Window { file_reading_task_source: FileReadingTaskSource, performance_timeline_task_source: PerformanceTimelineTaskSource, remote_event_task_source: RemoteEventTaskSource, + websocket_task_source: WebsocketTaskSource, image_cache_chan: Sender<ImageCacheMsg>, image_cache: Arc<ImageCache>, resource_threads: ResourceThreads, @@ -1955,6 +1965,7 @@ impl Window { file_reading_task_source, performance_timeline_task_source, remote_event_task_source, + websocket_task_source, image_cache_chan, image_cache, navigator: Default::default(), @@ -2123,7 +2134,8 @@ impl Window { let _ = self.script_chan.send(CommonScriptMsg::Task( ScriptThreadEventCategory::DomEvent, Box::new(self.task_canceller(TaskSourceName::DOMManipulation).wrap_task(task)), - self.pipeline_id() + self.pipeline_id(), + TaskSourceName::DOMManipulation, )); } } diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index b3ba42afb2b..653a2fa141c 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -14,7 +14,7 @@ use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::bindings::root::DomRoot; use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; -use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; +use dom::dedicatedworkerglobalscope::{DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg}; use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use dom::messageevent::MessageEvent; @@ -40,14 +40,14 @@ pub struct Worker { #[ignore_malloc_size_of = "Defined in std"] /// Sender to the Receiver associated with the DedicatedWorkerGlobalScope /// this Worker created. - sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, + sender: Sender<DedicatedWorkerScriptMsg>, #[ignore_malloc_size_of = "Arc"] closing: Arc<AtomicBool>, terminated: Cell<bool>, } impl Worker { - fn new_inherited(sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, + fn new_inherited(sender: Sender<DedicatedWorkerScriptMsg>, closing: Arc<AtomicBool>) -> Worker { Worker { eventtarget: EventTarget::new_inherited(), @@ -58,7 +58,7 @@ impl Worker { } pub fn new(global: &GlobalScope, - sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, + sender: Sender<DedicatedWorkerScriptMsg>, closing: Arc<AtomicBool>) -> DomRoot<Worker> { reflect_dom_object(Box::new(Worker::new_inherited(sender, closing)), global, @@ -148,7 +148,7 @@ impl WorkerMethods for Worker { // NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage // indicates that a nonexistent communication channel should result in a silent error. - let _ = self.sender.send((address, WorkerScriptMsg::DOMMessage(data))); + let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(address, WorkerScriptMsg::DOMMessage(data))); Ok(()) } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 5415492b0da..ac372c5fe98 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -47,6 +47,7 @@ use task_source::file_reading::FileReadingTaskSource; use task_source::networking::NetworkingTaskSource; use task_source::performance_timeline::PerformanceTimelineTaskSource; use task_source::remote_event::RemoteEventTaskSource; +use task_source::websocket::WebsocketTaskSource; use time::precise_time_ns; use timers::{IsInterval, TimerCallback}; @@ -386,6 +387,10 @@ impl WorkerGlobalScope { RemoteEventTaskSource(self.script_chan(), self.pipeline_id()) } + pub fn websocket_task_source(&self) -> WebsocketTaskSource { + WebsocketTaskSource(self.script_chan(), self.pipeline_id()) + } + pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) { let dedicated = self.downcast::<DedicatedWorkerGlobalScope>(); if let Some(dedicated) = dedicated { @@ -397,7 +402,7 @@ impl WorkerGlobalScope { pub fn process_event(&self, msg: CommonScriptMsg) { match msg { - CommonScriptMsg::Task(_, task, _) => { + CommonScriptMsg::Task(_, task, _, _) => { task.run_box() }, CommonScriptMsg::CollectReports(reports_chan) => { @@ -407,8 +412,6 @@ impl WorkerGlobalScope { reports_chan.send(reports); }, } - - // FIXME(jdm): Should we do a microtask checkpoint here? } pub fn handle_fire_timer(&self, timer_id: TimerEventId) { diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index a2e454507a8..d1b7ba9c44a 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -66,6 +66,7 @@ use style::thread_state::{self, ThreadState}; use swapper::Swapper; use swapper::swapper; use task::TaskBox; +use task_source::TaskSourceName; use uuid::Uuid; // Magic numbers @@ -644,7 +645,14 @@ impl WorkletThread { where T: TaskBox + 'static, { - let msg = CommonScriptMsg::Task(ScriptThreadEventCategory::WorkletEvent, Box::new(task), None); + // NOTE: It's unclear which task source should be used here: + // https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule + let msg = CommonScriptMsg::Task( + ScriptThreadEventCategory::WorkletEvent, + Box::new(task), + None, + TaskSourceName::DOMManipulation, + ); let msg = MainThreadScriptMsg::Common(msg); self.global_init.to_script_thread_sender.send(msg).expect("Worklet thread outlived script thread."); } |