Bug 1501709 - AudioWorkletGlobalScope::RegisterProcessor: check descriptors and convert them to an internal representation. r=karlt,baku,froydnj

Differential Revision: https://phabricator.services.mozilla.com/D11741

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Arnaud Bienner 2018-11-30 02:55:06 +00:00
Родитель 34921f8b0b
Коммит 6a459e7170
8 изменённых файлов: 358 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_AudioParamDescriptor_h
#define mozilla_dom_AudioParamDescriptor_h
#include "mozilla/dom/AudioParamDescriptorBinding.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
// Note: we call this "map" to match the spec, but we store audio param
// descriptors in an array, because we need ordered access, and don't need the
// map functionalities.
typedef nsTArray<AudioParamDescriptor> AudioParamDescriptorMap;
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_AudioParamDescriptor_h

Просмотреть файл

@ -10,6 +10,9 @@
#include "jsapi.h"
#include "mozilla/dom/AudioWorkletGlobalScopeBinding.h"
#include "mozilla/dom/WorkletPrincipal.h"
#include "mozilla/dom/AudioParamDescriptorBinding.h"
#include "nsPrintfCString.h"
#include "nsTHashtable.h"
namespace mozilla {
namespace dom {
@ -178,6 +181,10 @@ void AudioWorkletGlobalScope::RegisterProcessor(JSContext* aCx,
* (name - descriptors) to the node name to parameter descriptor
* map of the associated BaseAudioContext.
*/
AudioParamDescriptorMap map = DescriptorsFromJS(aCx, descriptors, aRv);
if (aRv.Failed()) {
return;
}
// TODO: we don't have a proper mechanism to communicate with the
// control thread currently. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=1473467#c3
@ -192,5 +199,80 @@ double AudioWorkletGlobalScope::CurrentTime() const { return mCurrentTime; }
float AudioWorkletGlobalScope::SampleRate() const { return mSampleRate; }
AudioParamDescriptorMap AudioWorkletGlobalScope::DescriptorsFromJS(
JSContext* aCx, const JS::Rooted<JS::Value>& aDescriptors, ErrorResult& aRv)
{
// We already checked if aDescriptors is an array or undefined in step 8 of
// registerProcessor, so we should be confident aDescriptors if valid here
if (aDescriptors.isUndefined()) {
return AudioParamDescriptorMap();
}
MOZ_ASSERT(aDescriptors.isObject());
AudioParamDescriptorMap res;
// To check for duplicates
nsTHashtable<nsStringHashKey> namesSet;
JS::Rooted<JSObject*> aDescriptorsArray(aCx, &aDescriptors.toObject());
uint32_t length = 0;
if (!JS_GetArrayLength(aCx, aDescriptorsArray, &length)) {
aRv.NoteJSContextException(aCx);
return AudioParamDescriptorMap();
}
for (uint32_t i = 0; i < length; ++i) {
JS::Rooted<JS::Value> descriptorElement(aCx);
if (!JS_GetElement(aCx, aDescriptorsArray, i, &descriptorElement)) {
aRv.NoteJSContextException(aCx);
return AudioParamDescriptorMap();
}
AudioParamDescriptor descriptor;
nsPrintfCString sourceDescription("Element %u in parameterDescriptors", i);
if (!descriptor.Init(aCx, descriptorElement, sourceDescription.get())) {
aRv.NoteJSContextException(aCx);
return AudioParamDescriptorMap();
}
if (namesSet.Contains(descriptor.mName)) {
aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("Duplicated name \"") +
NS_ConvertUTF16toUTF8(descriptor.mName) +
NS_LITERAL_CSTRING("\" in parameterDescriptors."));
return AudioParamDescriptorMap();
}
if (descriptor.mMinValue > descriptor.mMaxValue) {
aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("In parameterDescriptors, ") +
NS_ConvertUTF16toUTF8(descriptor.mName) +
NS_LITERAL_CSTRING(" minValue should be smaller than maxValue."));
return AudioParamDescriptorMap();
}
if (descriptor.mDefaultValue < descriptor.mMinValue ||
descriptor.mDefaultValue > descriptor.mMaxValue) {
aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("In parameterDescriptors, ") +
NS_ConvertUTF16toUTF8(descriptor.mName) +
NS_LITERAL_CSTRING(" defaultValue is out of the range defined by "
"minValue and maxValue."));
return AudioParamDescriptorMap();
}
if (!res.AppendElement(descriptor)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return AudioParamDescriptorMap();
}
if (!namesSet.PutEntry(descriptor.mName, fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return AudioParamDescriptorMap();
}
}
return res;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef mozilla_dom_AudioWorkletGlobalScope_h
#define mozilla_dom_AudioWorkletGlobalScope_h
#include "mozilla/dom/AudioParamDescriptorMap.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/WorkletGlobalScope.h"
#include "nsRefPtrHashtable.h"
@ -42,6 +43,13 @@ class AudioWorkletGlobalScope final : public WorkletGlobalScope {
private:
~AudioWorkletGlobalScope() = default;
// Returns an AudioParamDescriptorMap filled with AudioParamDescriptor
// objects, extracted from JS. Returns an empty map in case of error and set
// aRv accordingly.
AudioParamDescriptorMap DescriptorsFromJS(
JSContext* aCx, const JS::Rooted<JS::Value>& aDescriptors,
ErrorResult& aRv);
const RefPtr<AudioWorkletImpl> mImpl;
uint64_t mCurrentFrame;

Просмотреть файл

@ -47,6 +47,7 @@ EXPORTS.mozilla.dom += [
'AudioListener.h',
'AudioNode.h',
'AudioParam.h',
'AudioParamDescriptorMap.h',
'AudioParamMap.h',
'AudioProcessingEvent.h',
'AudioScheduledSourceNode.h',

Просмотреть файл

@ -0,0 +1,22 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/#dictdef-audioparamdescriptor
*
* Copyright © 2018 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
dictionary AudioParamDescriptor {
required DOMString name;
float defaultValue = 0;
float minValue = -3.4028235e38;
float maxValue = 3.4028235e38;
// AutomationRate for AudioWorklet is not needed until bug 1504984 is
// implemented
// AutomationRate automationRate = "a-rate";
};

Просмотреть файл

@ -378,6 +378,7 @@ WEBIDL_FILES = [
'AudioListener.webidl',
'AudioNode.webidl',
'AudioParam.webidl',
'AudioParamDescriptor.webidl',
'AudioParamMap.webidl',
'AudioProcessingEvent.webidl',
'AudioScheduledSourceNode.webidl',

Просмотреть файл

@ -20,6 +20,15 @@ function configureTest() {
"TypeError: Argument 2 of AudioWorkletGlobalScope.registerProcessor constructor.process is not callable.",
"TypeError: Argument 2 of AudioWorkletGlobalScope.registerProcessor constructor.parameterDescriptors is neither an array nor undefined.",
"NotSupportedError: Argument 1 of AudioWorkletGlobalScope.registerProcessor is invalid: a class with the same name is already registered.",
"TypeError: Missing required 'name' member of AudioParamDescriptor.",
"TypeError: 'defaultValue' member of AudioParamDescriptor is not a finite floating-point value.",
"TypeError: 'minValue' member of AudioParamDescriptor is not a finite floating-point value.",
"TypeError: 'maxValue' member of AudioParamDescriptor is not a finite floating-point value.",
"NotSupportedError: Duplicated name \"test\" in parameterDescriptors.",
"TypeError: Element 0 in parameterDescriptors can't be converted to a dictionary.",
"NotSupportedError: In parameterDescriptors, test defaultValue is out of the range defined by minValue and maxValue.",
"NotSupportedError: In parameterDescriptors, test defaultValue is out of the range defined by minValue and maxValue.",
"NotSupportedError: In parameterDescriptors, test minValue should be smaller than maxValue.",
];
var expected_errors_i = 0;

Просмотреть файл

@ -41,6 +41,144 @@ class DummyProcessWorkletProcessor extends AudioWorkletProcessor {
}
}
class DescriptorsNoNameWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
defaultValue: 0.707
}];
}
}
class DescriptorsDefaultValueNotNumberWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: "test",
defaultValue: "test"
}];
}
}
class DescriptorsMinValueNotNumberWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: "test",
minValue: "test"
}];
}
}
class DescriptorsMaxValueNotNumberWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: "test",
maxValue: "test"
}];
}
}
class DescriptorsDuplicatedNameWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: "test",
}, {
name: "test",
}];
}
}
class DescriptorsNotDictWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [42];
}
}
class DescriptorsOutOfRangeMinWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: 'test',
defaultValue: 0,
minValue: 1,
maxValue: 2,
}];
}
}
class DescriptorsOutOfRangeMaxWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: 'test',
defaultValue: 3,
minValue: 1,
maxValue: 2,
}];
}
}
class DescriptorsBadRangeMaxWorkletProcessor extends AudioWorkletProcessor {
constructor() { super(); }
process() {
// Do nothing, output silence
}
static get parameterDescriptors() {
return [{
name: 'test',
defaultValue: 1.5,
minValue: 2,
maxValue: 1,
}];
}
}
// Test not a constructor
// "TypeError: Argument 2 of AudioWorkletGlobalScope.registerProcessor is not a constructor."
try {
@ -104,3 +242,75 @@ try {
} catch (e) {
console.log(e)
}
// "name" is a mandatory field in descriptors
// "TypeError: Missing required 'name' member of AudioParamDescriptor."
try {
registerProcessor("descriptors-no-name-worklet-processor", DescriptorsNoNameWorkletProcessor);
} catch (e) {
console.log(e)
}
// "defaultValue" should be a number
// "TypeError: 'defaultValue' member of AudioParamDescriptor is not a finite floating-point value."
try {
registerProcessor("descriptors-default-value-not-number-worklet-processor", DescriptorsDefaultValueNotNumberWorkletProcessor);
} catch (e) {
console.log(e)
}
// "min" should be a number
// "TypeError: 'minValue' member of AudioParamDescriptor is not a finite floating-point value."
try {
registerProcessor("descriptors-min-value-not-number-worklet-processor", DescriptorsMinValueNotNumberWorkletProcessor);
} catch (e) {
console.log(e)
}
// "max" should be a number
// "TypeError: 'maxValue' member of AudioParamDescriptor is not a finite floating-point value."
try {
registerProcessor("descriptors-max-value-not-number-worklet-processor", DescriptorsMaxValueNotNumberWorkletProcessor);
} catch (e) {
console.log(e)
}
// Duplicated values are not allowed for "name"
// "NotSupportedError: Duplicated name \"test\" in parameterDescriptors"
try {
registerProcessor("descriptors-duplicated-name-worklet-processor", DescriptorsDuplicatedNameWorkletProcessor);
} catch (e) {
console.log(e)
}
// Descriptors' elements should be dictionnary
// "TypeError: Element 0 in parameterDescriptors can't be converted to a dictionary.",
try {
registerProcessor("descriptors-not-dict-worklet-processor", DescriptorsNotDictWorkletProcessor);
} catch (e) {
console.log(e)
}
// defaultValue value should be in range [minValue, maxValue]. defaultValue < minValue is not allowed
// "NotSupportedError: In parameterDescriptors, test defaultValue is out of the range defined by minValue and maxValue.",
try {
registerProcessor("descriptors-out-of-range-min-worklet-processor", DescriptorsOutOfRangeMinWorkletProcessor);
} catch (e) {
console.log(e)
}
// defaultValue value should be in range [minValue, maxValue]. defaultValue > maxValue is not allowed
// "NotSupportedError: In parameterDescriptors, test defaultValue is out of the range defined by minValue and maxValue.",
try {
registerProcessor("descriptors-out-of-range-max-worklet-processor", DescriptorsOutOfRangeMaxWorkletProcessor);
} catch (e) {
console.log(e)
}
// We should have minValue < maxValue to define a valid range
// "NotSupportedError: In parameterDescriptors, test minValue should be smaller than maxValue.",
try {
registerProcessor("descriptors-bad-range-max-worklet-processor", DescriptorsBadRangeMaxWorkletProcessor);
} catch (e) {
console.log(e)
}