Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab88527fe6 | ||
|
|
6c0ff1881a | ||
|
|
1e7f4c8474 | ||
|
|
542eaca577 | ||
|
|
1cb6f56418 | ||
|
|
a503e883d9 | ||
|
|
347681c219 | ||
|
|
2984237f4f | ||
|
|
1c4a73de71 | ||
|
|
d27df4d88f | ||
|
|
9562888e77 | ||
|
|
5536b38401 | ||
|
|
c011b271a1 | ||
|
|
c21fb81d3e | ||
|
|
5a3dcf5939 | ||
|
|
1d709fe942 | ||
|
|
e55c16146f | ||
|
|
997a9a3e13 | ||
|
|
008f38579c | ||
|
|
d943e86b28 | ||
|
|
3f2d707a83 | ||
|
|
b145a93ad6 | ||
|
|
3056028db4 | ||
|
|
b4dac14720 | ||
|
|
76694b956f | ||
|
|
9fab5b3e3c | ||
|
|
d54491fdd0 | ||
|
|
00c3c3a546 | ||
|
|
1da5c344be | ||
|
|
76d66b5dad | ||
|
|
e502735eb2 | ||
|
|
edfebe1b99 | ||
|
|
250e4f75da | ||
|
|
53f1bdd9c2 | ||
|
|
ade298d0a7 | ||
|
|
387b01ce14 | ||
|
|
47d2094329 | ||
|
|
43f9a8e1f0 | ||
|
|
30e287371f | ||
|
|
a9246d7b28 | ||
|
|
db1d73f05d | ||
|
|
72b14ea177 | ||
|
|
430c4a3f79 | ||
|
|
93ed92f63b | ||
|
|
4a1312c060 | ||
|
|
303e1ecf34 | ||
|
|
57b0e4a65b | ||
|
|
804a301068 | ||
|
|
aedb8a4d08 | ||
|
|
93072ab51e | ||
|
|
ca11a325bd | ||
|
|
3b088af0b1 | ||
|
|
09e52646e9 | ||
|
|
26ca6f4fcc | ||
|
|
b467d710ac | ||
|
|
f4f11fada8 | ||
|
|
43b3b47426 |
No files matched your search
@@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Clobber for ICU 78.1
|
||||
Clobber for libvpx changes
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/InternalMutationEvent.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "WebGLActiveInfo.h"
|
||||
#include "WebGLBuffer.h"
|
||||
#include "WebGLContextUtils.h"
|
||||
#include "WebGLFramebuffer.h"
|
||||
@@ -27,6 +28,30 @@ namespace mozilla {
|
||||
// For a Tegra workaround.
|
||||
static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
|
||||
|
||||
static uint8_t
|
||||
NumUsedLocationsByElemTypeForDraw(GLenum elemType)
|
||||
{
|
||||
switch (elemType) {
|
||||
case LOCAL_GL_FLOAT_MAT2:
|
||||
case LOCAL_GL_FLOAT_MAT2x3:
|
||||
case LOCAL_GL_FLOAT_MAT2x4:
|
||||
return 2;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT3x2:
|
||||
case LOCAL_GL_FLOAT_MAT3:
|
||||
case LOCAL_GL_FLOAT_MAT3x4:
|
||||
return 3;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT4x2:
|
||||
case LOCAL_GL_FLOAT_MAT4x3:
|
||||
case LOCAL_GL_FLOAT_MAT4:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
class ScopedResolveTexturesForDraw
|
||||
@@ -331,6 +356,7 @@ class ScopedDrawHelper final
|
||||
{
|
||||
WebGLContext* const mWebGL;
|
||||
bool mDidFake;
|
||||
std::vector<GLuint> mDisabledUnusedAttribs;
|
||||
|
||||
public:
|
||||
ScopedDrawHelper(WebGLContext* webgl, const char* funcName, uint32_t firstVertex,
|
||||
@@ -367,11 +393,43 @@ public:
|
||||
}
|
||||
mDidFake = true;
|
||||
|
||||
const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
if (mWebGL->gl->WorkAroundDriverBugs()) {
|
||||
std::vector<bool> isActiveAttrib(mWebGL->mBoundVertexArray->mAttribs.Length(),
|
||||
false);
|
||||
for (const auto& progAttrib : linkInfo->attribs) {
|
||||
const auto loc = progAttrib.mLoc;
|
||||
if (loc < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto usedLocs =
|
||||
NumUsedLocationsByElemTypeForDraw(progAttrib.mActiveInfo->mElemType);
|
||||
for (uint32_t i = 0; i < usedLocs; ++i) {
|
||||
const auto usedLoc = static_cast<uint32_t>(loc) + i;
|
||||
if (usedLoc < isActiveAttrib.size()) {
|
||||
isActiveAttrib[usedLoc] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mWebGL->mBoundVertexArray->mAttribs.Length(); ++i) {
|
||||
const auto& attrib = mWebGL->mBoundVertexArray->mAttribs[i];
|
||||
if (!attrib.mEnabled || isActiveAttrib[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mWebGL->gl->fDisableVertexAttribArray(i);
|
||||
mDisabledUnusedAttribs.push_back(i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
////
|
||||
// Check UBO sizes.
|
||||
|
||||
const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
|
||||
|
||||
for (const auto& cur : linkInfo->uniformBlocks) {
|
||||
const auto& dataSize = cur->mDataSize;
|
||||
const auto& binding = cur->mBinding;
|
||||
@@ -477,6 +535,10 @@ public:
|
||||
if (mDidFake) {
|
||||
mWebGL->UndoFakeVertexAttrib0();
|
||||
}
|
||||
|
||||
for (const auto loc : mDisabledUnusedAttribs) {
|
||||
mWebGL->gl->fEnableVertexAttribArray(loc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -607,12 +669,12 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
if (vertCount > mMaxVertIdsPerDraw) {
|
||||
ErrorOutOfMemory(
|
||||
"Context's max vertCount is %u, but %u requested. [webgl.max-vert-ids-per-draw]", mMaxVertIdsPerDraw, vertCount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vertCount > mMaxVertIdsPerDraw) {
|
||||
ErrorOutOfMemory(
|
||||
"Context's max vertCount is %u, but %u requested. [webgl.max-vert-ids-per-draw]", mMaxVertIdsPerDraw, vertCount);
|
||||
return;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
|
||||
@@ -855,12 +917,12 @@ WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type,
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
if (vertCount > mMaxVertIdsPerDraw) {
|
||||
ErrorOutOfMemory(
|
||||
"Context's max vertCount is %u, but %u requested. [webgl.max-vert-ids-per-draw]", mMaxVertIdsPerDraw, vertCount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vertCount > mMaxVertIdsPerDraw) {
|
||||
ErrorOutOfMemory(
|
||||
"Context's max vertCount is %u, but %u requested. [webgl.max-vert-ids-per-draw]", mMaxVertIdsPerDraw, vertCount);
|
||||
return;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
|
||||
|
||||
@@ -100,6 +100,45 @@ public:
|
||||
uint32_t mChunksToProcess = 0;
|
||||
};
|
||||
|
||||
/* static */ already_AddRefed<AnalyserNode>
|
||||
AnalyserNode::Create(AudioContext& aAudioContext,
|
||||
const AnalyserOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<AnalyserNode> analyserNode = new AnalyserNode(&aAudioContext);
|
||||
|
||||
analyserNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
analyserNode->SetFftSize(aOptions.mFftSize, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
analyserNode->SetMinDecibels(aOptions.mMinDecibels, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
analyserNode->SetMaxDecibels(aOptions.mMaxDecibels, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
analyserNode->SetSmoothingTimeConstant(aOptions.mSmoothingTimeConstant, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return analyserNode.forget();
|
||||
}
|
||||
|
||||
AnalyserNode::AnalyserNode(AudioContext* aContext)
|
||||
: AudioNode(aContext,
|
||||
1,
|
||||
|
||||
@@ -14,16 +14,26 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct AnalyserOptions;
|
||||
|
||||
class AnalyserNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit AnalyserNode(AudioContext* aContext);
|
||||
static already_AddRefed<AnalyserNode>
|
||||
Create(AudioContext& aAudioContext, const AnalyserOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<AnalyserNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const AnalyserOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
void GetFloatFrequencyData(const Float32Array& aArray);
|
||||
void GetByteFrequencyData(const Uint8Array& aArray);
|
||||
void GetFloatTimeDomainData(const Float32Array& aArray);
|
||||
@@ -61,10 +71,9 @@ public:
|
||||
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
~AnalyserNode() {}
|
||||
|
||||
private:
|
||||
~AnalyserNode() = default;
|
||||
|
||||
friend class AnalyserNodeEngine;
|
||||
void AppendChunk(const AudioChunk& aChunk);
|
||||
bool AllocateBuffer();
|
||||
@@ -73,6 +82,8 @@ private:
|
||||
void GetTimeDomainData(float* aData, size_t aLength);
|
||||
|
||||
private:
|
||||
explicit AnalyserNode(AudioContext* aContext);
|
||||
|
||||
FFTBlock mAnalysisBlock;
|
||||
nsTArray<AudioChunk> mChunks;
|
||||
double mMinDecibels;
|
||||
|
||||
@@ -178,6 +178,24 @@ AudioBuffer::~AudioBuffer()
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AudioBuffer>
|
||||
AudioBuffer::Constructor(const GlobalObject& aGlobal,
|
||||
AudioContext& aAudioContext,
|
||||
const AudioBufferOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aOptions.mNumberOfChannels) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
float sampleRate = aOptions.mSampleRate.WasPassed()
|
||||
? aOptions.mSampleRate.Value()
|
||||
: aAudioContext.SampleRate();
|
||||
return Create(&aAudioContext, aOptions.mNumberOfChannels, aOptions.mLength,
|
||||
sampleRate, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
AudioBuffer::ClearJSChannels()
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@ class ThreadSharedFloatArrayBufferList;
|
||||
|
||||
namespace dom {
|
||||
|
||||
struct AudioBufferOptions;
|
||||
class AudioContext;
|
||||
|
||||
/**
|
||||
@@ -56,6 +57,10 @@ public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioBuffer)
|
||||
|
||||
static already_AddRefed<AudioBuffer>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const AudioBufferOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindowInner> parentObject = do_QueryReferent(mOwnerWindow);
|
||||
@@ -134,4 +139,3 @@ protected:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -607,10 +607,30 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
|
||||
mStream->AddMainThreadListener(this);
|
||||
}
|
||||
|
||||
AudioBufferSourceNode::~AudioBufferSourceNode()
|
||||
/* static */ already_AddRefed<AudioBufferSourceNode>
|
||||
AudioBufferSourceNode::Create(JSContext* aCx, AudioContext& aAudioContext,
|
||||
const AudioBufferSourceOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
}
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<AudioBufferSourceNode> audioNode = new AudioBufferSourceNode(&aAudioContext);
|
||||
|
||||
if (aOptions.mBuffer.WasPassed()) {
|
||||
MOZ_ASSERT(aCx);
|
||||
audioNode->SetBuffer(aCx, aOptions.mBuffer.Value());
|
||||
}
|
||||
|
||||
audioNode->Detune()->SetValue(aOptions.mDetune);
|
||||
audioNode->SetLoop(aOptions.mLoop);
|
||||
audioNode->SetLoopEnd(aOptions.mLoopEnd);
|
||||
audioNode->SetLoopStart(aOptions.mLoopStart);
|
||||
audioNode->PlaybackRate()->SetValue(aOptions.mPlaybackRate);
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
void
|
||||
AudioBufferSourceNode::DestroyMediaStream()
|
||||
{
|
||||
|
||||
@@ -12,13 +12,16 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct AudioBufferSourceOptions;
|
||||
class AudioParam;
|
||||
|
||||
class AudioBufferSourceNode final : public AudioNode,
|
||||
public MainThreadMediaStreamListener
|
||||
class AudioBufferSourceNode final : public AudioNode
|
||||
, public MainThreadMediaStreamListener
|
||||
{
|
||||
public:
|
||||
explicit AudioBufferSourceNode(AudioContext* aContext);
|
||||
static already_AddRefed<AudioBufferSourceNode>
|
||||
Create(JSContext* aCx, AudioContext& aAudioContext,
|
||||
const AudioBufferSourceOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
void DestroyMediaStream() override;
|
||||
|
||||
@@ -33,6 +36,13 @@ public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioBufferSourceNode, AudioNode)
|
||||
|
||||
static already_AddRefed<AudioBufferSourceNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const AudioBufferSourceOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aGlobal.Context(), aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void Start(double aWhen, double aOffset,
|
||||
@@ -98,10 +108,10 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~AudioBufferSourceNode();
|
||||
|
||||
private:
|
||||
explicit AudioBufferSourceNode(AudioContext* aContext);
|
||||
~AudioBufferSourceNode() = default;
|
||||
|
||||
friend class AudioBufferSourceNodeEngine;
|
||||
// START is sent during Start().
|
||||
// STOP is sent during Stop().
|
||||
@@ -129,7 +139,6 @@ private:
|
||||
void SendBufferParameterToStream(JSContext* aCx);
|
||||
void SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream);
|
||||
|
||||
private:
|
||||
double mLoopStart;
|
||||
double mLoopEnd;
|
||||
double mOffset;
|
||||
@@ -145,4 +154,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,10 +11,26 @@
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
|
||||
#include "mozilla/dom/AnalyserNode.h"
|
||||
#include "mozilla/dom/AnalyserNodeBinding.h"
|
||||
#include "mozilla/dom/AudioBufferSourceNodeBinding.h"
|
||||
#include "mozilla/dom/AudioContextBinding.h"
|
||||
#include "mozilla/dom/BiquadFilterNodeBinding.h"
|
||||
#include "mozilla/dom/ChannelMergerNodeBinding.h"
|
||||
#include "mozilla/dom/ChannelSplitterNodeBinding.h"
|
||||
#include "mozilla/dom/ConvolverNodeBinding.h"
|
||||
#include "mozilla/dom/DelayNodeBinding.h"
|
||||
#include "mozilla/dom/DynamicsCompressorNodeBinding.h"
|
||||
#include "mozilla/dom/GainNodeBinding.h"
|
||||
#include "mozilla/dom/IIRFilterNodeBinding.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/dom/MediaElementAudioSourceNodeBinding.h"
|
||||
#include "mozilla/dom/MediaStreamAudioSourceNodeBinding.h"
|
||||
#include "mozilla/dom/OfflineAudioContextBinding.h"
|
||||
#include "mozilla/dom/OscillatorNodeBinding.h"
|
||||
#include "mozilla/dom/PannerNodeBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/StereoPannerNodeBinding.h"
|
||||
#include "mozilla/dom/WaveShaperNodeBinding.h"
|
||||
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioBufferSourceNode.h"
|
||||
@@ -245,13 +261,9 @@ bool AudioContext::CheckClosed(ErrorResult& aRv)
|
||||
already_AddRefed<AudioBufferSourceNode>
|
||||
AudioContext::CreateBufferSource(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<AudioBufferSourceNode> bufferNode =
|
||||
new AudioBufferSourceNode(this);
|
||||
return bufferNode.forget();
|
||||
return AudioBufferSourceNode::Create(nullptr, *this,
|
||||
AudioBufferSourceOptions(),
|
||||
aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<ConstantSourceNode>
|
||||
@@ -303,18 +315,8 @@ bool IsValidBufferSize(uint32_t aBufferSize) {
|
||||
already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
AudioContext::CreateMediaStreamDestination(ErrorResult& aRv)
|
||||
{
|
||||
if (mIsOffline) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaStreamAudioDestinationNode> node =
|
||||
new MediaStreamAudioDestinationNode(this);
|
||||
return node.forget();
|
||||
return MediaStreamAudioDestinationNode::Create(*this, AudioNodeOptions(),
|
||||
aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<ScriptProcessorNode>
|
||||
@@ -344,181 +346,93 @@ AudioContext::CreateScriptProcessor(uint32_t aBufferSize,
|
||||
already_AddRefed<AnalyserNode>
|
||||
AudioContext::CreateAnalyser(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<AnalyserNode> analyserNode = new AnalyserNode(this);
|
||||
return analyserNode.forget();
|
||||
return AnalyserNode::Create(*this, AnalyserOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<StereoPannerNode>
|
||||
AudioContext::CreateStereoPanner(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<StereoPannerNode> stereoPannerNode = new StereoPannerNode(this);
|
||||
return stereoPannerNode.forget();
|
||||
return StereoPannerNode::Create(*this, StereoPannerOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<MediaElementAudioSourceNode>
|
||||
AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mIsOffline) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
MediaElementAudioSourceOptions options;
|
||||
options.mMediaElement = aMediaElement;
|
||||
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DOMMediaStream> stream =
|
||||
aMediaElement.CaptureAudio(aRv, mDestination->Stream()->Graph());
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return MediaElementAudioSourceNode::Create(this, stream, aRv);
|
||||
return MediaElementAudioSourceNode::Create(*this, options, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<MediaStreamAudioSourceNode>
|
||||
AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mIsOffline) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
MediaStreamAudioSourceOptions options;
|
||||
options.mMediaStream = aMediaStream;
|
||||
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MediaStreamAudioSourceNode::Create(this, &aMediaStream, aRv);
|
||||
return MediaStreamAudioSourceNode::Create(*this, options, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<GainNode>
|
||||
AudioContext::CreateGain(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GainNode> gainNode = new GainNode(this);
|
||||
return gainNode.forget();
|
||||
return GainNode::Create(*this, GainOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<WaveShaperNode>
|
||||
AudioContext::CreateWaveShaper(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<WaveShaperNode> waveShaperNode = new WaveShaperNode(this);
|
||||
return waveShaperNode.forget();
|
||||
return WaveShaperNode::Create(*this, WaveShaperOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DelayNode>
|
||||
AudioContext::CreateDelay(double aMaxDelayTime, ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aMaxDelayTime > 0. && aMaxDelayTime < 180.) {
|
||||
RefPtr<DelayNode> delayNode = new DelayNode(this, aMaxDelayTime);
|
||||
return delayNode.forget();
|
||||
}
|
||||
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
DelayOptions options;
|
||||
options.mMaxDelayTime = aMaxDelayTime;
|
||||
return DelayNode::Create(*this, options, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<PannerNode>
|
||||
AudioContext::CreatePanner(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PannerNode> pannerNode = new PannerNode(this);
|
||||
mPannerNodes.PutEntry(pannerNode);
|
||||
return pannerNode.forget();
|
||||
return PannerNode::Create(*this, PannerOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<ConvolverNode>
|
||||
AudioContext::CreateConvolver(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ConvolverNode> convolverNode = new ConvolverNode(this);
|
||||
return convolverNode.forget();
|
||||
return ConvolverNode::Create(nullptr, *this, ConvolverOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<ChannelSplitterNode>
|
||||
AudioContext::CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv)
|
||||
{
|
||||
if (aNumberOfOutputs == 0 ||
|
||||
aNumberOfOutputs > WebAudioUtils::MaxChannelCount) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ChannelSplitterNode> splitterNode =
|
||||
new ChannelSplitterNode(this, aNumberOfOutputs);
|
||||
return splitterNode.forget();
|
||||
ChannelSplitterOptions options;
|
||||
options.mNumberOfOutputs = aNumberOfOutputs;
|
||||
return ChannelSplitterNode::Create(*this, options, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<ChannelMergerNode>
|
||||
AudioContext::CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv)
|
||||
{
|
||||
if (aNumberOfInputs == 0 ||
|
||||
aNumberOfInputs > WebAudioUtils::MaxChannelCount) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ChannelMergerNode> mergerNode =
|
||||
new ChannelMergerNode(this, aNumberOfInputs);
|
||||
return mergerNode.forget();
|
||||
ChannelMergerOptions options;
|
||||
options.mNumberOfInputs = aNumberOfInputs;
|
||||
return ChannelMergerNode::Create(*this, options, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DynamicsCompressorNode>
|
||||
AudioContext::CreateDynamicsCompressor(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DynamicsCompressorNode> compressorNode =
|
||||
new DynamicsCompressorNode(this);
|
||||
return compressorNode.forget();
|
||||
return DynamicsCompressorNode::Create(*this, DynamicsCompressorOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<BiquadFilterNode>
|
||||
AudioContext::CreateBiquadFilter(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<BiquadFilterNode> filterNode =
|
||||
new BiquadFilterNode(this);
|
||||
return filterNode.forget();
|
||||
return BiquadFilterNode::Create(*this, BiquadFilterOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<IIRFilterNode>
|
||||
@@ -526,47 +440,16 @@ AudioContext::CreateIIRFilter(const mozilla::dom::binding_detail::AutoSequence<d
|
||||
const mozilla::dom::binding_detail::AutoSequence<double>& aFeedback,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aFeedforward.Length() == 0 || aFeedforward.Length() > 20) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aFeedback.Length() == 0 || aFeedback.Length() > 20) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool feedforwardAllZeros = true;
|
||||
for (size_t i = 0; i < aFeedforward.Length(); ++i) {
|
||||
if (aFeedforward.Elements()[i] != 0.0) {
|
||||
feedforwardAllZeros = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (feedforwardAllZeros || aFeedback.Elements()[0] == 0.0) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<IIRFilterNode> filterNode =
|
||||
new IIRFilterNode(this, aFeedforward, aFeedback);
|
||||
return filterNode.forget();
|
||||
IIRFilterOptions options;
|
||||
options.mFeedforward = aFeedforward;
|
||||
options.mFeedback = aFeedback;
|
||||
return IIRFilterNode::Create(*this, options, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<OscillatorNode>
|
||||
AudioContext::CreateOscillator(ErrorResult& aRv)
|
||||
{
|
||||
if (CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<OscillatorNode> oscillatorNode =
|
||||
new OscillatorNode(this);
|
||||
return oscillatorNode.forget();
|
||||
return OscillatorNode::Create(*this, OscillatorOptions(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<PeriodicWave>
|
||||
|
||||
@@ -316,6 +316,8 @@ public:
|
||||
IMPL_EVENT_HANDLER(mozinterruptbegin)
|
||||
IMPL_EVENT_HANDLER(mozinterruptend)
|
||||
|
||||
bool CheckClosed(ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
void DisconnectFromWindow();
|
||||
void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
|
||||
@@ -326,8 +328,6 @@ private:
|
||||
|
||||
friend struct ::mozilla::WebAudioDecodeJob;
|
||||
|
||||
bool CheckClosed(ErrorResult& aRv);
|
||||
|
||||
nsTArray<MediaStream*> GetAllStreams() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -71,6 +71,28 @@ AudioNode::~AudioNode()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
if (aOptions.mChannelCount.WasPassed()) {
|
||||
SetChannelCount(aOptions.mChannelCount.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (aOptions.mChannelCountMode.WasPassed()) {
|
||||
SetChannelCountModeValue(aOptions.mChannelCountMode.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (aOptions.mChannelInterpretation.WasPassed()) {
|
||||
SetChannelInterpretationValue(aOptions.mChannelInterpretation.Value());
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
|
||||
@@ -250,6 +250,9 @@ private:
|
||||
bool DisconnectFromOutputIfConnected(uint32_t aOutputIndex, uint32_t aInputIndex);
|
||||
|
||||
protected:
|
||||
// Helper for the Constructors for nodes.
|
||||
void Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
// Helpers for sending different value types to streams
|
||||
void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
|
||||
void SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue);
|
||||
|
||||
@@ -262,8 +262,29 @@ BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
BiquadFilterNode::~BiquadFilterNode()
|
||||
/* static */ already_AddRefed<BiquadFilterNode>
|
||||
BiquadFilterNode::Create(AudioContext& aAudioContext,
|
||||
const BiquadFilterOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<BiquadFilterNode> audioNode = new BiquadFilterNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->SetType(aOptions.mType);
|
||||
audioNode->Q()->SetValue(aOptions.mQ);
|
||||
audioNode->Detune()->SetValue(aOptions.mDetune);
|
||||
audioNode->Frequency()->SetValue(aOptions.mFrequency);
|
||||
audioNode->Gain()->SetValue(aOptions.mGain);
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -14,15 +14,25 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct BiquadFilterOptions;
|
||||
|
||||
class BiquadFilterNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit BiquadFilterNode(AudioContext* aContext);
|
||||
static already_AddRefed<BiquadFilterNode>
|
||||
Create(AudioContext& aAudioContext, const BiquadFilterOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BiquadFilterNode, AudioNode)
|
||||
|
||||
static already_AddRefed<BiquadFilterNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const BiquadFilterOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
BiquadFilterType Type() const
|
||||
@@ -63,10 +73,10 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~BiquadFilterNode();
|
||||
|
||||
private:
|
||||
explicit BiquadFilterNode(AudioContext* aContext);
|
||||
~BiquadFilterNode() = default;
|
||||
|
||||
BiquadFilterType mType;
|
||||
RefPtr<AudioParam> mFrequency;
|
||||
RefPtr<AudioParam> mDetune;
|
||||
@@ -78,4 +88,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -74,8 +74,30 @@ ChannelMergerNode::ChannelMergerNode(AudioContext* aContext,
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
ChannelMergerNode::~ChannelMergerNode()
|
||||
/* static */ already_AddRefed<ChannelMergerNode>
|
||||
ChannelMergerNode::Create(AudioContext& aAudioContext,
|
||||
const ChannelMergerOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mNumberOfInputs == 0 ||
|
||||
aOptions.mNumberOfInputs > WebAudioUtils::MaxChannelCount) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ChannelMergerNode> audioNode =
|
||||
new ChannelMergerNode(&aAudioContext, aOptions.mNumberOfInputs);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@@ -86,4 +108,3 @@ ChannelMergerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -12,15 +12,24 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct ChannelMergerOptions;
|
||||
|
||||
class ChannelMergerNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
ChannelMergerNode(AudioContext* aContext,
|
||||
uint16_t aInputCount);
|
||||
static already_AddRefed<ChannelMergerNode>
|
||||
Create(AudioContext& aAudioContext, const ChannelMergerOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
static already_AddRefed<ChannelMergerNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const ChannelMergerOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
uint16_t NumberOfInputs() const override { return mInputCount; }
|
||||
@@ -35,10 +44,11 @@ public:
|
||||
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~ChannelMergerNode();
|
||||
|
||||
private:
|
||||
ChannelMergerNode(AudioContext* aContext,
|
||||
uint16_t aInputCount);
|
||||
~ChannelMergerNode() = default;
|
||||
|
||||
const uint16_t mInputCount;
|
||||
};
|
||||
|
||||
@@ -46,4 +56,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,8 +65,30 @@ ChannelSplitterNode::ChannelSplitterNode(AudioContext* aContext,
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
ChannelSplitterNode::~ChannelSplitterNode()
|
||||
/* static */ already_AddRefed<ChannelSplitterNode>
|
||||
ChannelSplitterNode::Create(AudioContext& aAudioContext,
|
||||
const ChannelSplitterOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mNumberOfOutputs == 0 ||
|
||||
aOptions.mNumberOfOutputs > WebAudioUtils::MaxChannelCount) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ChannelSplitterNode> audioNode =
|
||||
new ChannelSplitterNode(&aAudioContext, aOptions.mNumberOfOutputs);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@@ -77,4 +99,3 @@ ChannelSplitterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProt
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -12,15 +12,24 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct ChannelSplitterOptions;
|
||||
|
||||
class ChannelSplitterNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
ChannelSplitterNode(AudioContext* aContext,
|
||||
uint16_t aOutputCount);
|
||||
static already_AddRefed<ChannelSplitterNode>
|
||||
Create(AudioContext& aAudioContext, const ChannelSplitterOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
static already_AddRefed<ChannelSplitterNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const ChannelSplitterOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
uint16_t NumberOfOutputs() const override { return mOutputCount; }
|
||||
@@ -35,10 +44,11 @@ public:
|
||||
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~ChannelSplitterNode();
|
||||
|
||||
private:
|
||||
ChannelSplitterNode(AudioContext* aContext,
|
||||
uint16_t aOutputCount);
|
||||
~ChannelSplitterNode() = default;
|
||||
|
||||
const uint16_t mOutputCount;
|
||||
};
|
||||
|
||||
@@ -46,4 +56,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -203,8 +203,34 @@ ConvolverNode::ConvolverNode(AudioContext* aContext)
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
ConvolverNode::~ConvolverNode()
|
||||
/* static */ already_AddRefed<ConvolverNode>
|
||||
ConvolverNode::Create(JSContext* aCx, AudioContext& aAudioContext,
|
||||
const ConvolverOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ConvolverNode> audioNode = new ConvolverNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This must be done before setting the buffer.
|
||||
audioNode->SetNormalize(!aOptions.mDisableNormalization);
|
||||
|
||||
if (aOptions.mBuffer.WasPassed()) {
|
||||
MOZ_ASSERT(aCx);
|
||||
audioNode->SetBuffer(aCx, aOptions.mBuffer.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -291,4 +317,3 @@ ConvolverNode::SetNormalize(bool aNormalize)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -12,14 +12,26 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct ConvolverOptions;
|
||||
|
||||
class ConvolverNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit ConvolverNode(AudioContext* aContext);
|
||||
static already_AddRefed<ConvolverNode>
|
||||
Create(JSContext* aCx, AudioContext& aAudioContext,
|
||||
const ConvolverOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ConvolverNode, AudioNode);
|
||||
|
||||
static already_AddRefed<ConvolverNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const ConvolverOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aGlobal.Context(), aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
AudioBuffer* GetBuffer(JSContext* aCx) const
|
||||
@@ -27,7 +39,7 @@ public:
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
void SetBuffer(JSContext* aCx, AudioBuffer* aBufferi, ErrorResult& aRv);
|
||||
void SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv);
|
||||
|
||||
bool Normalize() const
|
||||
{
|
||||
@@ -61,10 +73,10 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~ConvolverNode();
|
||||
|
||||
private:
|
||||
explicit ConvolverNode(AudioContext* aContext);
|
||||
~ConvolverNode() = default;
|
||||
|
||||
RefPtr<AudioBuffer> mBuffer;
|
||||
bool mNormalize;
|
||||
};
|
||||
@@ -74,4 +86,3 @@ private:
|
||||
} //end namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -205,8 +205,30 @@ DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
DelayNode::~DelayNode()
|
||||
/* static */ already_AddRefed<DelayNode>
|
||||
DelayNode::Create(AudioContext& aAudioContext,
|
||||
const DelayOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mMaxDelayTime <= 0. || aOptions.mMaxDelayTime >= 180.) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DelayNode> audioNode = new DelayNode(&aAudioContext,
|
||||
aOptions.mMaxDelayTime);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->DelayTime()->SetValue(aOptions.mDelayTime);
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -13,15 +13,25 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct DelayOptions;
|
||||
|
||||
class DelayNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
DelayNode(AudioContext* aContext, double aMaxDelay);
|
||||
static already_AddRefed<DelayNode>
|
||||
Create(AudioContext& aAudioContext, const DelayOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DelayNode, AudioNode)
|
||||
|
||||
static already_AddRefed<DelayNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const DelayOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
AudioParam* DelayTime() const
|
||||
@@ -37,13 +47,12 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~DelayNode();
|
||||
|
||||
private:
|
||||
DelayNode(AudioContext* aContext, double aMaxDelay);
|
||||
~DelayNode() = default;
|
||||
|
||||
friend class DelayNodeEngine;
|
||||
|
||||
private:
|
||||
RefPtr<AudioParam> mDelay;
|
||||
};
|
||||
|
||||
@@ -51,4 +60,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -204,8 +204,30 @@ DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
DynamicsCompressorNode::~DynamicsCompressorNode()
|
||||
/* static */ already_AddRefed<DynamicsCompressorNode>
|
||||
DynamicsCompressorNode::Create(AudioContext& aAudioContext,
|
||||
const DynamicsCompressorOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DynamicsCompressorNode> audioNode =
|
||||
new DynamicsCompressorNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->Attack()->SetValue(aOptions.mAttack);
|
||||
audioNode->Knee()->SetValue(aOptions.mKnee);
|
||||
audioNode->Ratio()->SetValue(aOptions.mRatio);
|
||||
audioNode->GetRelease()->SetValue(aOptions.mRelease);
|
||||
audioNode->Threshold()->SetValue(aOptions.mThreshold);
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -13,15 +13,25 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct DynamicsCompressorOptions;
|
||||
|
||||
class DynamicsCompressorNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit DynamicsCompressorNode(AudioContext* aContext);
|
||||
static already_AddRefed<DynamicsCompressorNode>
|
||||
Create(AudioContext& aAudioContext, const DynamicsCompressorOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DynamicsCompressorNode, AudioNode)
|
||||
|
||||
static already_AddRefed<DynamicsCompressorNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const DynamicsCompressorOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
AudioParam* Threshold() const
|
||||
@@ -69,10 +79,10 @@ public:
|
||||
mReduction = aReduction;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~DynamicsCompressorNode();
|
||||
|
||||
private:
|
||||
explicit DynamicsCompressorNode(AudioContext* aContext);
|
||||
~DynamicsCompressorNode() = default;
|
||||
|
||||
RefPtr<AudioParam> mThreshold;
|
||||
RefPtr<AudioParam> mKnee;
|
||||
RefPtr<AudioParam> mRatio;
|
||||
@@ -85,4 +95,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -128,8 +128,24 @@ GainNode::GainNode(AudioContext* aContext)
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
GainNode::~GainNode()
|
||||
/* static */ already_AddRefed<GainNode>
|
||||
GainNode::Create(AudioContext& aAudioContext,
|
||||
const GainOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->Gain()->SetValue(aOptions.mGain);
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -13,15 +13,25 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct GainOptions;
|
||||
|
||||
class GainNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit GainNode(AudioContext* aContext);
|
||||
static already_AddRefed<GainNode>
|
||||
Create(AudioContext& aAudioContext, const GainOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GainNode, AudioNode)
|
||||
|
||||
static already_AddRefed<GainNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const GainOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
AudioParam* Gain() const
|
||||
@@ -37,10 +47,10 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~GainNode();
|
||||
|
||||
private:
|
||||
explicit GainNode(AudioContext* aContext);
|
||||
~GainNode() = default;
|
||||
|
||||
RefPtr<AudioParam> mGain;
|
||||
};
|
||||
|
||||
@@ -48,4 +58,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -138,8 +138,8 @@ private:
|
||||
};
|
||||
|
||||
IIRFilterNode::IIRFilterNode(AudioContext* aContext,
|
||||
const mozilla::dom::binding_detail::AutoSequence<double>& aFeedforward,
|
||||
const mozilla::dom::binding_detail::AutoSequence<double>& aFeedback)
|
||||
const Sequence<double>& aFeedforward,
|
||||
const Sequence<double>& aFeedback)
|
||||
: AudioNode(aContext,
|
||||
2,
|
||||
ChannelCountMode::Max,
|
||||
@@ -173,8 +173,46 @@ IIRFilterNode::IIRFilterNode(AudioContext* aContext,
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
IIRFilterNode::~IIRFilterNode()
|
||||
/* static */ already_AddRefed<IIRFilterNode>
|
||||
IIRFilterNode::Create(AudioContext& aAudioContext,
|
||||
const IIRFilterOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mFeedforward.Length() == 0 || aOptions.mFeedforward.Length() > 20) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mFeedback.Length() == 0 || aOptions.mFeedback.Length() > 20) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool feedforwardAllZeros = true;
|
||||
for (size_t i = 0; i < aOptions.mFeedforward.Length(); ++i) {
|
||||
if (aOptions.mFeedforward.Elements()[i] != 0.0) {
|
||||
feedforwardAllZeros = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (feedforwardAllZeros || aOptions.mFeedback.Elements()[0] == 0.0) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<IIRFilterNode> audioNode =
|
||||
new IIRFilterNode(&aAudioContext, aOptions.mFeedforward, aOptions.mFeedback);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -14,18 +14,25 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct IIRFilterOptions;
|
||||
|
||||
class IIRFilterNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit IIRFilterNode(AudioContext* aContext,
|
||||
const mozilla::dom::binding_detail::AutoSequence<double>& aFeedforward,
|
||||
const mozilla::dom::binding_detail::AutoSequence<double>& aFeedback);
|
||||
static already_AddRefed<IIRFilterNode>
|
||||
Create(AudioContext& aAudioContext, const IIRFilterOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<IIRFilterNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const IIRFilterOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
||||
const Float32Array& aMagResponse,
|
||||
@@ -39,16 +46,17 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~IIRFilterNode();
|
||||
|
||||
private:
|
||||
nsTArray<double> mFeedback;
|
||||
nsTArray<double> mFeedforward;
|
||||
IIRFilterNode(AudioContext* aContext,
|
||||
const Sequence<double>& aFeedforward,
|
||||
const Sequence<double>& aFeedback);
|
||||
~IIRFilterNode() = default;
|
||||
|
||||
nsTArray<double> mFeedback;
|
||||
nsTArray<double> mFeedforward;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,13 +15,29 @@ MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext)
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MediaElementAudioSourceNode>
|
||||
MediaElementAudioSourceNode::Create(AudioContext* aContext,
|
||||
DOMMediaStream* aStream, ErrorResult& aRv)
|
||||
MediaElementAudioSourceNode::Create(AudioContext& aAudioContext,
|
||||
const MediaElementAudioSourceOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<MediaElementAudioSourceNode> node =
|
||||
new MediaElementAudioSourceNode(aContext);
|
||||
if (aAudioContext.IsOffline()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->Init(aStream, aRv);
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaElementAudioSourceNode> node =
|
||||
new MediaElementAudioSourceNode(&aAudioContext);
|
||||
|
||||
RefPtr<DOMMediaStream> stream =
|
||||
aOptions.mMediaElement->CaptureAudio(aRv, aAudioContext.Destination()->Stream()->Graph());
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->Init(stream, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -11,11 +11,23 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct MediaElementAudioSourceOptions;
|
||||
|
||||
class MediaElementAudioSourceNode final : public MediaStreamAudioSourceNode
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<MediaElementAudioSourceNode>
|
||||
Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
|
||||
Create(AudioContext& aAudioContext,
|
||||
const MediaElementAudioSourceOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<MediaElementAudioSourceNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const MediaElementAudioSourceOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioDestinationTrackSource :
|
||||
class AudioDestinationTrackSource final :
|
||||
public MediaStreamTrackSource
|
||||
{
|
||||
public:
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~AudioDestinationTrackSource() {}
|
||||
~AudioDestinationTrackSource() = default;
|
||||
|
||||
RefPtr<MediaStreamAudioDestinationNode> mNode;
|
||||
};
|
||||
@@ -101,8 +101,29 @@ MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext* a
|
||||
mPort = outputStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK);
|
||||
}
|
||||
|
||||
MediaStreamAudioDestinationNode::~MediaStreamAudioDestinationNode()
|
||||
/* static */ already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
MediaStreamAudioDestinationNode::Create(AudioContext& aAudioContext,
|
||||
const AudioNodeOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.IsOffline()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaStreamAudioDestinationNode> audioNode =
|
||||
new MediaStreamAudioDestinationNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -11,14 +11,26 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct AudioNodeOptions;
|
||||
|
||||
class MediaStreamAudioDestinationNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit MediaStreamAudioDestinationNode(AudioContext* aContext);
|
||||
static already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
Create(AudioContext& aAudioContext, const AudioNodeOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioDestinationNode, AudioNode)
|
||||
|
||||
static already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const AudioNodeOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
uint16_t NumberOfOutputs() const final override
|
||||
@@ -41,10 +53,10 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~MediaStreamAudioDestinationNode();
|
||||
|
||||
private:
|
||||
explicit MediaStreamAudioDestinationNode(AudioContext* aContext);
|
||||
~MediaStreamAudioDestinationNode() = default;
|
||||
|
||||
RefPtr<DOMMediaStream> mDOMStream;
|
||||
RefPtr<MediaInputPort> mPort;
|
||||
};
|
||||
|
||||
@@ -44,13 +44,23 @@ MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext)
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MediaStreamAudioSourceNode>
|
||||
MediaStreamAudioSourceNode::Create(AudioContext* aContext,
|
||||
DOMMediaStream* aStream, ErrorResult& aRv)
|
||||
MediaStreamAudioSourceNode::Create(AudioContext& aAudioContext,
|
||||
const MediaStreamAudioSourceOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<MediaStreamAudioSourceNode> node =
|
||||
new MediaStreamAudioSourceNode(aContext);
|
||||
if (aAudioContext.IsOffline()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->Init(aStream, aRv);
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaStreamAudioSourceNode> node =
|
||||
new MediaStreamAudioSourceNode(&aAudioContext);
|
||||
|
||||
node->Init(aOptions.mMediaStream, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct MediaStreamAudioSourceOptions;
|
||||
|
||||
class MediaStreamAudioSourceNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
@@ -45,11 +48,19 @@ class MediaStreamAudioSourceNode : public AudioNode,
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<MediaStreamAudioSourceNode>
|
||||
Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
|
||||
Create(AudioContext& aContext, const MediaStreamAudioSourceOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
|
||||
|
||||
static already_AddRefed<MediaStreamAudioSourceNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const MediaStreamAudioSourceOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void DestroyMediaStream() override;
|
||||
|
||||
@@ -425,8 +425,35 @@ OscillatorNode::OscillatorNode(AudioContext* aContext)
|
||||
mStream->AddMainThreadListener(this);
|
||||
}
|
||||
|
||||
OscillatorNode::~OscillatorNode()
|
||||
/* static */ already_AddRefed<OscillatorNode>
|
||||
OscillatorNode::Create(AudioContext& aAudioContext,
|
||||
const OscillatorOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<OscillatorNode> audioNode = new OscillatorNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->SetType(aOptions.mType, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->Frequency()->SetValue(aOptions.mFrequency);
|
||||
audioNode->Detune()->SetValue(aOptions.mDetune);
|
||||
|
||||
if (aOptions.mPeriodicWave.WasPassed()) {
|
||||
audioNode->SetPeriodicWave(aOptions.mPeriodicWave.Value());
|
||||
}
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -15,16 +15,26 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct OscillatorOptions;
|
||||
|
||||
class OscillatorNode final : public AudioNode,
|
||||
public MainThreadMediaStreamListener
|
||||
{
|
||||
public:
|
||||
explicit OscillatorNode(AudioContext* aContext);
|
||||
static already_AddRefed<OscillatorNode>
|
||||
Create(AudioContext& aAudioContext, const OscillatorOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
|
||||
|
||||
static already_AddRefed<OscillatorNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const OscillatorOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void DestroyMediaStream() override;
|
||||
@@ -81,14 +91,13 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~OscillatorNode();
|
||||
|
||||
private:
|
||||
explicit OscillatorNode(AudioContext* aContext);
|
||||
~OscillatorNode() = default;
|
||||
|
||||
void SendTypeToStream();
|
||||
void SendPeriodicWaveToStream();
|
||||
|
||||
private:
|
||||
OscillatorType mType;
|
||||
RefPtr<PeriodicWave> mPeriodicWave;
|
||||
RefPtr<AudioParam> mFrequency;
|
||||
@@ -100,4 +109,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -329,6 +329,38 @@ PannerNode::~PannerNode()
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<PannerNode>
|
||||
PannerNode::Create(AudioContext& aAudioContext,
|
||||
const PannerOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PannerNode> audioNode = new PannerNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->SetPanningModel(aOptions.mPanningModel);
|
||||
audioNode->SetDistanceModel(aOptions.mDistanceModel);
|
||||
audioNode->SetPosition(aOptions.mPositionX, aOptions.mPositionY,
|
||||
aOptions.mPositionZ);
|
||||
audioNode->SetOrientation(aOptions.mOrientationX, aOptions.mOrientationY,
|
||||
aOptions.mOrientationZ);
|
||||
audioNode->SetRefDistance(aOptions.mRefDistance);
|
||||
audioNode->SetMaxDistance(aOptions.mMaxDistance);
|
||||
audioNode->SetRolloffFactor(aOptions.mRolloffFactor);
|
||||
audioNode->SetConeInnerAngle(aOptions.mConeInnerAngle);
|
||||
audioNode->SetConeOuterAngle(aOptions.mConeOuterAngle);
|
||||
audioNode->SetConeOuterGain(aOptions.mConeOuterGain);
|
||||
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
void PannerNode::SetPanningModel(PanningModelType aPanningModel)
|
||||
{
|
||||
mPanningModel = aPanningModel;
|
||||
@@ -784,4 +816,3 @@ PannerNode::SendDopplerToSourcesIfNeeded()
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -19,13 +19,24 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
class AudioBufferSourceNode;
|
||||
struct PannerOptions;
|
||||
|
||||
class PannerNode final : public AudioNode,
|
||||
public SupportsWeakPtr<PannerNode>
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<PannerNode>
|
||||
Create(AudioContext& aAudioContext, const PannerOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PannerNode)
|
||||
explicit PannerNode(AudioContext* aContext);
|
||||
|
||||
static already_AddRefed<PannerNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const PannerOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
@@ -229,10 +240,10 @@ public:
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~PannerNode();
|
||||
|
||||
private:
|
||||
explicit PannerNode(AudioContext* aContext);
|
||||
~PannerNode();
|
||||
|
||||
friend class AudioListener;
|
||||
friend class PannerNodeEngine;
|
||||
enum EngineParameters {
|
||||
@@ -292,4 +303,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,6 +43,32 @@ PeriodicWave::PeriodicWave(AudioContext* aContext,
|
||||
mCoefficients->SetData(1, nullptr, free, buffer+aLength);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<PeriodicWave>
|
||||
PeriodicWave::Constructor(const GlobalObject& aGlobal,
|
||||
AudioContext& aAudioContext,
|
||||
const PeriodicWaveOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aOptions.mReal.WasPassed() || !aOptions.mImag.WasPassed() ||
|
||||
aOptions.mReal.Value().Length() != aOptions.mImag.Value().Length() ||
|
||||
aOptions.mReal.Value().Length() == 0) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PeriodicWave> wave =
|
||||
new PeriodicWave(&aAudioContext, aOptions.mReal.Value().Elements(),
|
||||
aOptions.mImag.Value().Elements(),
|
||||
aOptions.mReal.Value().Length(),
|
||||
aOptions.mDisableNormalization,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return wave.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
PeriodicWave::SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
@@ -70,4 +96,3 @@ PeriodicWave::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -16,6 +16,9 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct PeriodicWaveOptions;
|
||||
|
||||
class PeriodicWave final : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
@@ -29,6 +32,10 @@ public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PeriodicWave)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PeriodicWave)
|
||||
|
||||
static already_AddRefed<PeriodicWave>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const PeriodicWaveOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
AudioContext* GetParentObject() const
|
||||
{
|
||||
return mContext;
|
||||
@@ -55,7 +62,7 @@ public:
|
||||
size_t SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
private:
|
||||
~PeriodicWave() {}
|
||||
~PeriodicWave() = default;
|
||||
|
||||
RefPtr<AudioContext> mContext;
|
||||
RefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
|
||||
|
||||
@@ -182,8 +182,24 @@ StereoPannerNode::StereoPannerNode(AudioContext* aContext)
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
StereoPannerNode::~StereoPannerNode()
|
||||
/* static */ already_AddRefed<StereoPannerNode>
|
||||
StereoPannerNode::Create(AudioContext& aAudioContext,
|
||||
const StereoPannerOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<StereoPannerNode> audioNode = new StereoPannerNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
audioNode->Pan()->SetValue(aOptions.mPan);
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -13,12 +13,23 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct StereoPannerOptions;
|
||||
|
||||
class StereoPannerNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<StereoPannerNode>
|
||||
Create(AudioContext& aAudioContext, const StereoPannerOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(StereoPannerNode)
|
||||
explicit StereoPannerNode(AudioContext* aContext);
|
||||
|
||||
static already_AddRefed<StereoPannerNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const StereoPannerOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
@@ -55,10 +66,10 @@ public:
|
||||
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
protected:
|
||||
virtual ~StereoPannerNode();
|
||||
|
||||
private:
|
||||
explicit StereoPannerNode(AudioContext* aContext);
|
||||
~StereoPannerNode() = default;
|
||||
|
||||
RefPtr<AudioParam> mPan;
|
||||
};
|
||||
|
||||
@@ -66,4 +77,3 @@ private:
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,7 +18,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
tmp->ClearCurve();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode)
|
||||
@@ -26,7 +25,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCurve)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WaveShaperNode)
|
||||
@@ -313,27 +311,39 @@ WaveShaperNode::WaveShaperNode(AudioContext* aContext)
|
||||
2,
|
||||
ChannelCountMode::Max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mCurve(nullptr)
|
||||
, mType(OverSampleType::None)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
|
||||
WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
|
||||
mStream = AudioNodeStream::Create(aContext, engine,
|
||||
AudioNodeStream::NO_STREAM_FLAGS,
|
||||
aContext->Graph());
|
||||
}
|
||||
|
||||
WaveShaperNode::~WaveShaperNode()
|
||||
/* static */ already_AddRefed<WaveShaperNode>
|
||||
WaveShaperNode::Create(AudioContext& aAudioContext,
|
||||
const WaveShaperOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
ClearCurve();
|
||||
}
|
||||
if (aAudioContext.CheckClosed(aRv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WaveShaperNode::ClearCurve()
|
||||
{
|
||||
mCurve = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
RefPtr<WaveShaperNode> audioNode = new WaveShaperNode(&aAudioContext);
|
||||
|
||||
audioNode->Initialize(aOptions, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mCurve.WasPassed()) {
|
||||
audioNode->SetCurveInternal(aOptions.mCurve.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
audioNode->SetOversample(aOptions.mOversample);
|
||||
return audioNode.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@@ -345,45 +355,85 @@ WaveShaperNode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
void
|
||||
WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve, ErrorResult& aRv)
|
||||
{
|
||||
nsTArray<float> curve;
|
||||
if (!aCurve.IsNull()) {
|
||||
const Float32Array& floats = aCurve.Value();
|
||||
// Let's purge the cached value for the curve attribute.
|
||||
WaveShaperNodeBinding::ClearCachedCurveValue(this);
|
||||
|
||||
floats.ComputeLengthAndData();
|
||||
if (floats.IsShared()) {
|
||||
// Throw if the object is mapping shared memory (must opt in).
|
||||
aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(NS_LITERAL_STRING("Argument of WaveShaperNode.setCurve"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t argLength = floats.Length();
|
||||
if (argLength < 2) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!curve.SetLength(argLength, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
PodCopy(curve.Elements(), floats.Data(), floats.Length());
|
||||
|
||||
mCurve = floats.Obj();
|
||||
} else {
|
||||
mCurve = nullptr;
|
||||
if (aCurve.IsNull()) {
|
||||
CleanCurveInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
const Float32Array& floats = aCurve.Value();
|
||||
|
||||
floats.ComputeLengthAndData();
|
||||
if (floats.IsShared()) {
|
||||
// Throw if the object is mapping shared memory (must opt in).
|
||||
aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(NS_LITERAL_STRING("Argument of WaveShaperNode.setCurve"));
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<float> curve;
|
||||
uint32_t argLength = floats.Length();
|
||||
if (!curve.SetLength(argLength, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
PodCopy(curve.Elements(), floats.Data(), argLength);
|
||||
SetCurveInternal(curve, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WaveShaperNode::SetCurveInternal(const nsTArray<float>& aCurve,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aCurve.Length() < 2) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mCurve = aCurve;
|
||||
SendCurveToStream();
|
||||
}
|
||||
|
||||
void
|
||||
WaveShaperNode::CleanCurveInternal()
|
||||
{
|
||||
mCurve.Clear();
|
||||
SendCurveToStream();
|
||||
}
|
||||
|
||||
void
|
||||
WaveShaperNode::SendCurveToStream()
|
||||
{
|
||||
AudioNodeStream* ns = mStream;
|
||||
MOZ_ASSERT(ns, "Why don't we have a stream here?");
|
||||
ns->SetRawArrayData(curve);
|
||||
|
||||
nsTArray<float> copyCurve(mCurve);
|
||||
ns->SetRawArrayData(copyCurve);
|
||||
}
|
||||
|
||||
void
|
||||
WaveShaperNode::GetCurve(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetval)
|
||||
{
|
||||
// Let's return a null value if the list is empty.
|
||||
if (mCurve.IsEmpty()) {
|
||||
aRetval.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mCurve.Length() >= 2);
|
||||
aRetval.set(Float32Array::Create(aCx, this, mCurve.Length(),
|
||||
mCurve.Elements()));
|
||||
}
|
||||
|
||||
void
|
||||
WaveShaperNode::SetOversample(OverSampleType aType)
|
||||
{
|
||||
mType = aType;
|
||||
SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType));
|
||||
SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE,
|
||||
static_cast<int32_t>(aType));
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -14,21 +14,28 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
struct WaveShaperOptions;
|
||||
|
||||
class WaveShaperNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit WaveShaperNode(AudioContext *aContext);
|
||||
static already_AddRefed<WaveShaperNode>
|
||||
Create(AudioContext& aAudioContext, const WaveShaperOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WaveShaperNode, AudioNode)
|
||||
|
||||
static already_AddRefed<WaveShaperNode>
|
||||
Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
|
||||
const WaveShaperOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Create(aAudioContext, aOptions, aRv);
|
||||
}
|
||||
|
||||
JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void GetCurve(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval) const
|
||||
{
|
||||
aRetval.set(mCurve);
|
||||
}
|
||||
void GetCurve(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval);
|
||||
void SetCurve(const Nullable<Float32Array>& aData, ErrorResult& aRv);
|
||||
|
||||
OverSampleType Oversample() const
|
||||
@@ -54,14 +61,17 @@ public:
|
||||
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~WaveShaperNode();
|
||||
|
||||
private:
|
||||
void ClearCurve();
|
||||
explicit WaveShaperNode(AudioContext *aContext);
|
||||
~WaveShaperNode() = default;
|
||||
|
||||
private:
|
||||
JS::Heap<JSObject*> mCurve;
|
||||
void SetCurveInternal(const nsTArray<float>& aCurve,
|
||||
ErrorResult& aRv);
|
||||
void CleanCurveInternal();
|
||||
|
||||
void SendCurveToStream();
|
||||
|
||||
nsTArray<float> mCurve;
|
||||
OverSampleType mType;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
|
||||
function testNode() {
|
||||
var context = new AudioContext();
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
@@ -84,7 +82,7 @@ addLoadEvent(function() {
|
||||
analyser.maxDecibels = -101;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
|
||||
is(analyser.smoothingTimeConstant, 0.8, "Correct default value for smoothingTimeConstant");
|
||||
ok(Math.abs(analyser.smoothingTimeConstant - 0.8) < 0.001, "Correct default value for smoothingTimeConstant");
|
||||
expectException(function() {
|
||||
analyser.smoothingTimeConstant = -0.1;
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
@@ -93,6 +91,83 @@ addLoadEvent(function() {
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
analyser.smoothingTimeConstant = 0;
|
||||
analyser.smoothingTimeConstant = 1;
|
||||
}
|
||||
|
||||
function testConstructor() {
|
||||
var context = new AudioContext();
|
||||
|
||||
var analyser = new AnalyserNode(context);
|
||||
is(analyser.channelCount, 1, "analyser node has 1 input channels by default");
|
||||
is(analyser.channelCountMode, "max", "Correct channelCountMode for the analyser node");
|
||||
is(analyser.channelInterpretation, "speakers", "Correct channelCountInterpretation for the analyser node");
|
||||
|
||||
is(analyser.fftSize, 2048, "Correct default value for fftSize");
|
||||
is(analyser.frequencyBinCount, 1024, "Correct default value for frequencyBinCount");
|
||||
is(analyser.minDecibels, -100, "Correct default value for minDecibels");
|
||||
is(analyser.maxDecibels, -30, "Correct default value for maxDecibels");
|
||||
ok(Math.abs(analyser.smoothingTimeConstant - 0.8) < 0.001, "Correct default value for smoothingTimeConstant");
|
||||
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 0 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 1 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 8 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 100 }); // non-power of two
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 2049 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 4097 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 8193 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 16385 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 32769 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { fftSize: 65536 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
analyser = new AnalyserNode(context, { fftSize: 1024 });
|
||||
is(analyser.frequencyBinCount, 512, "Correct new value for frequencyBinCount");
|
||||
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { minDecibels: -30 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { minDecibels: -29 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { maxDecibels: -100 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { maxDecibels: -101 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { smoothingTimeConstant: -0.1 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
analyser = new AnalyserNode(context, { smoothingTimeConstant: -1.1 });
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
analyser = new AnalyserNode(context, { smoothingTimeConstant: 0 });
|
||||
analyser = new AnalyserNode(context, { smoothingTimeConstant: 1 });
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
|
||||
testNode();
|
||||
testConstructor();
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
@@ -19,12 +19,8 @@ var gTest = {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
var source = new AudioBufferSourceNode(context, {buffer: buffer, loop: true, loopStart: buffer.duration * 0.25, loopEnd: buffer.duration * 0.75 });
|
||||
source.start(0);
|
||||
source.loop = true;
|
||||
source.loopStart = buffer.duration * 0.25;
|
||||
source.loopEnd = buffer.duration * 0.75;
|
||||
return source;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
|
||||
@@ -24,7 +24,7 @@ var gTest = {
|
||||
buffers.push(buffer);
|
||||
}
|
||||
|
||||
var merger = context.createChannelMerger();
|
||||
var merger = new ChannelMergerNode(context);
|
||||
is(merger.channelCount, 1, "merger node has 1 input channels");
|
||||
is(merger.channelCountMode, "explicit", "Correct channelCountMode for the merger node");
|
||||
is(merger.channelInterpretation, "speakers", "Correct channelCountInterpretation for the merger node");
|
||||
|
||||
@@ -29,7 +29,7 @@ addLoadEvent(function() {
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var splitter = context.createChannelSplitter();
|
||||
var splitter = new ChannelSplitterNode(context);
|
||||
is(splitter.channelCount, 2, "splitter node has 2 input channels by default");
|
||||
is(splitter.channelCountMode, "max", "Correct channelCountMode for the splitter node");
|
||||
is(splitter.channelInterpretation, "speakers", "Correct channelCountInterpretation for the splitter node");
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var context = new AudioContext();
|
||||
var conv = context.createConvolver();
|
||||
var conv = new ConvolverNode(context);
|
||||
|
||||
is(conv.channelCount, 2, "Convolver node has 2 input channels by default");
|
||||
is(conv.channelCountMode, "clamped-max", "Correct channelCountMode for the Convolver node");
|
||||
|
||||
@@ -14,6 +14,19 @@ var gTest = {
|
||||
length: 4096,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var delay = new DelayNode(context);
|
||||
ok(delay.delayTime, "The audioparam member must exist");
|
||||
is(delay.delayTime.value, 0, "Correct initial value");
|
||||
is(delay.delayTime.defaultValue, 0, "Correct default value");
|
||||
delay.delayTime.value = 0.5;
|
||||
is(delay.delayTime.value, 0.5, "Correct initial value");
|
||||
is(delay.delayTime.defaultValue, 0, "Correct default value");
|
||||
|
||||
delay = new DelayNode(context, { delayTime: 0.5 });
|
||||
ok(delay.delayTime, "The audioparam member must exist");
|
||||
is(delay.delayTime.value, 0.5, "Correct initial value");
|
||||
is(delay.delayTime.defaultValue, 0, "Correct default value");
|
||||
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
@@ -21,7 +34,7 @@ var gTest = {
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var delay = context.createDelay();
|
||||
delay = context.createDelay();
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
@@ -49,6 +62,20 @@ var gTest = {
|
||||
expectException(function() {
|
||||
context.createDelay(-1);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
||||
expectException(function() {
|
||||
new DelayNode(context, { maxDelayTime: 0 });
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectException(function() {
|
||||
new DelayNode(context, { maxDelayTime: 180 });
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectTypeError(function() {
|
||||
new DelayNode(context, { maxDelayTime: NaN });
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectException(function() {
|
||||
new DelayNode(context, { maxDelayTime: -1 });
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
||||
context.createDelay(1); // should not throw
|
||||
|
||||
// Delay the source stream by 2048 frames
|
||||
|
||||
@@ -20,7 +20,7 @@ addLoadEvent(function() {
|
||||
var osc = context.createOscillator();
|
||||
var sp = context.createScriptProcessor();
|
||||
|
||||
var compressor = context.createDynamicsCompressor();
|
||||
var compressor = new DynamicsCompressorNode(context);
|
||||
|
||||
osc.connect(compressor);
|
||||
osc.connect(sp);
|
||||
|
||||
@@ -14,17 +14,32 @@ var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
|
||||
var gain = new GainNode(context);
|
||||
ok(gain.gain, "The audioparam member must exist");
|
||||
is(gain.gain.value, 1.0, "Correct initial value");
|
||||
is(gain.gain.defaultValue, 1.0, "Correct default value");
|
||||
gain.gain.value = 0.5;
|
||||
is(gain.gain.value, 0.5, "Correct initial value");
|
||||
is(gain.gain.defaultValue, 1.0, "Correct default value");
|
||||
|
||||
gain = new GainNode(context, { gain: 0.5 });
|
||||
ok(gain.gain, "The audioparam member must exist");
|
||||
is(gain.gain.value, 0.5, "Correct initial value");
|
||||
is(gain.gain.defaultValue, 1.0, "Correct default value");
|
||||
gain.gain.value = 0.5;
|
||||
is(gain.gain.value, 0.5, "Correct initial value");
|
||||
is(gain.gain.defaultValue, 1.0, "Correct default value");
|
||||
|
||||
var buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
gain = context.createGain();
|
||||
source.connect(gain);
|
||||
|
||||
ok(gain.gain, "The audioparam member must exist");
|
||||
|
||||
@@ -56,7 +56,7 @@ function test() {
|
||||
}
|
||||
|
||||
audio.onloadedmetadata = function() {
|
||||
var node = context.createMediaElementSource(audio);
|
||||
var node = new MediaElementAudioSourceNode(context, { mediaElement: audio });
|
||||
var sp = context.createScriptProcessor(2048, 1);
|
||||
node.connect(sp);
|
||||
// Use a fuzz factor of 100 to account for samples that just happen to be zero
|
||||
|
||||
@@ -23,7 +23,7 @@ addLoadEvent(function() {
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
var dest = context.createMediaStreamDestination();
|
||||
var dest = new MediaStreamAudioDestinationNode(context);
|
||||
source.connect(dest);
|
||||
|
||||
var elem = document.getElementById('audioelem');
|
||||
|
||||
@@ -31,7 +31,7 @@ var gTest = {
|
||||
source.connect(dest);
|
||||
source.start(0);
|
||||
|
||||
var mediaStreamSource = context.createMediaStreamSource(dest.stream);
|
||||
var mediaStreamSource = new MediaStreamAudioSourceNode(context, { mediaStream: dest.stream });
|
||||
// channelCount and channelCountMode should have no effect
|
||||
mediaStreamSource.channelCount = 1;
|
||||
mediaStreamSource.channelCountMode = "explicit";
|
||||
|
||||
@@ -14,7 +14,7 @@ SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
|
||||
var context = new AudioContext();
|
||||
var osc = context.createOscillator();
|
||||
var osc = new OscillatorNode(context);
|
||||
|
||||
is(osc.channelCount, 2, "Oscillator node has 2 input channels by default");
|
||||
is(osc.channelCountMode, "max", "Correct channelCountMode for the Oscillator node");
|
||||
|
||||
@@ -25,7 +25,7 @@ addLoadEvent(function() {
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var panner = context.createPanner();
|
||||
var panner = new PannerNode(context);
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ function applyMonoToStereoPanning(c, panning) {
|
||||
|
||||
// Test the DOM interface
|
||||
var context = new OfflineAudioContext(1, 1, SR);
|
||||
var stereoPanner = context.createStereoPanner();
|
||||
var stereoPanner = new StereoPannerNode(context);
|
||||
ok(stereoPanner.pan, "The AudioParam member must exist");
|
||||
is(stereoPanner.pan.value, 0.0, "Correct initial value");
|
||||
is(stereoPanner.pan.defaultValue, 0.0, "Correct default value");
|
||||
@@ -70,8 +70,11 @@ function sine(t) {
|
||||
|
||||
// A couple mono and stereo buffers: the StereoPannerNode equation is different
|
||||
// if the input is mono or stereo
|
||||
var stereoBuffer = context.createBuffer(2, BUF_SIZE, context.sampleRate);
|
||||
var monoBuffer = context.createBuffer(1, BUF_SIZE, context.sampleRate);
|
||||
var stereoBuffer = new AudioBuffer(context, { numberOfChannels: 2,
|
||||
length: BUF_SIZE });
|
||||
var monoBuffer = new AudioBuffer(context, { numberOfChannels: 1,
|
||||
length: BUF_SIZE,
|
||||
sampleRate: context.sampleRate });
|
||||
for (var i = 0; i < BUF_SIZE; ++i) {
|
||||
monoBuffer.getChannelData(0)[i] =
|
||||
stereoBuffer.getChannelData(0)[i] =
|
||||
@@ -81,7 +84,9 @@ for (var i = 0; i < BUF_SIZE; ++i) {
|
||||
// Expected test vectors
|
||||
function expectedBufferNoop(gain) {
|
||||
gain = gain || 1.0;
|
||||
var expectedBuffer = context.createBuffer(2, BUF_SIZE, SR);
|
||||
var expectedBuffer = new AudioBuffer(context, { numberOfChannels: 2,
|
||||
length: BUF_SIZE,
|
||||
sampleRate: SR });
|
||||
for (var i = 0; i < BUF_SIZE; i++) {
|
||||
expectedBuffer.getChannelData(0)[i] = gain * sine(i);
|
||||
expectedBuffer.getChannelData(1)[i] = gain * sine(i);
|
||||
@@ -91,7 +96,9 @@ function expectedBufferNoop(gain) {
|
||||
|
||||
function expectedBufferForStereo(gain) {
|
||||
gain = gain || 1.0;
|
||||
var expectedBuffer = context.createBuffer(2, BUF_SIZE, SR);
|
||||
var expectedBuffer = new AudioBuffer(context, { numberOfChannels: 2,
|
||||
length: BUF_SIZE,
|
||||
sampleRate: SR });
|
||||
var gainPanning = gainForPanningStereoToStereo(PANNING);
|
||||
gainPanning[0] *= gain;
|
||||
gainPanning[1] *= gain;
|
||||
@@ -106,7 +113,9 @@ function expectedBufferForStereo(gain) {
|
||||
|
||||
function expectedBufferForMono(gain) {
|
||||
gain = gain || 1.0;
|
||||
var expectedBuffer = context.createBuffer(2, BUF_SIZE, SR);
|
||||
var expectedBuffer = new AudioBuffer(context, { numberOfChannels: 2,
|
||||
length: BUF_SIZE,
|
||||
sampleRate: SR });
|
||||
var gainPanning = gainForPanningMonoToStereo(PANNING);
|
||||
gainPanning[0] *= gain;
|
||||
gainPanning[1] *= gain;
|
||||
|
||||
@@ -17,7 +17,7 @@ var gTest = {
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = this.buffer;
|
||||
|
||||
var shaper = context.createWaveShaper();
|
||||
var shaper = new WaveShaperNode(context);
|
||||
shaper.curve = this.curve;
|
||||
|
||||
source.connect(shaper);
|
||||
|
||||
@@ -10,7 +10,15 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary AnalyserOptions : AudioNodeOptions {
|
||||
unsigned long fftSize = 2048;
|
||||
float maxDecibels = -30;
|
||||
float minDecibels = -100;
|
||||
float smoothingTimeConstant = 0.8;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional AnalyserOptions options)]
|
||||
interface AnalyserNode : AudioNode {
|
||||
|
||||
// Real-time frequency-domain data
|
||||
@@ -38,4 +46,3 @@ interface AnalyserNode : AudioNode {
|
||||
|
||||
// Mozilla extension
|
||||
AnalyserNode implements AudioNodePassThrough;
|
||||
|
||||
@@ -10,7 +10,14 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary AudioBufferOptions {
|
||||
unsigned long numberOfChannels = 1;
|
||||
required unsigned long length;
|
||||
float sampleRate;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, AudioBufferOptions options)]
|
||||
interface AudioBuffer {
|
||||
|
||||
readonly attribute float sampleRate;
|
||||
@@ -29,4 +36,3 @@ interface AudioBuffer {
|
||||
[Throws]
|
||||
void copyToChannel(Float32Array source, long channelNumber, optional unsigned long startInChannel = 0);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,17 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary AudioBufferSourceOptions {
|
||||
AudioBuffer? buffer;
|
||||
float detune = 0;
|
||||
boolean loop = false;
|
||||
double loopEnd = 0;
|
||||
double loopStart = 0;
|
||||
float playbackRate = 1;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional AudioBufferSourceOptions options)]
|
||||
interface AudioBufferSourceNode : AudioNode {
|
||||
|
||||
attribute AudioBuffer? buffer;
|
||||
|
||||
@@ -21,6 +21,12 @@ enum ChannelInterpretation {
|
||||
"discrete"
|
||||
};
|
||||
|
||||
dictionary AudioNodeOptions {
|
||||
unsigned long channelCount;
|
||||
ChannelCountMode channelCountMode;
|
||||
ChannelInterpretation channelInterpretation;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
interface AudioNode : EventTarget {
|
||||
|
||||
|
||||
@@ -21,7 +21,16 @@ enum BiquadFilterType {
|
||||
"allpass"
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary BiquadFilterOptions : AudioNodeOptions {
|
||||
BiquadFilterType type = "lowpass";
|
||||
float Q = 1;
|
||||
float detune = 0;
|
||||
float frequency = 350;
|
||||
float gain = 0;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional BiquadFilterOptions options)]
|
||||
interface BiquadFilterNode : AudioNode {
|
||||
|
||||
attribute BiquadFilterType type;
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
interface ChannelMergerNode : AudioNode {
|
||||
|
||||
dictionary ChannelMergerOptions : AudioNodeOptions {
|
||||
unsigned long numberOfInputs = 6;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional ChannelMergerOptions options)]
|
||||
interface ChannelMergerNode : AudioNode {
|
||||
};
|
||||
@@ -10,7 +10,12 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary ChannelSplitterOptions : AudioNodeOptions {
|
||||
unsigned long numberOfOutputs = 6;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional ChannelSplitterOptions options)]
|
||||
interface ChannelSplitterNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary ConvolverOptions : AudioNodeOptions {
|
||||
AudioBuffer? buffer;
|
||||
boolean disableNormalization = false;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional ConvolverOptions options)]
|
||||
interface ConvolverNode : AudioNode {
|
||||
|
||||
[SetterThrows]
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary DelayOptions : AudioNodeOptions {
|
||||
double maxDelayTime = 1;
|
||||
double delayTime = 0;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional DelayOptions options)]
|
||||
interface DelayNode : AudioNode {
|
||||
|
||||
readonly attribute AudioParam delayTime;
|
||||
|
||||
@@ -10,7 +10,16 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary DynamicsCompressorOptions : AudioNodeOptions {
|
||||
float attack = 0.003;
|
||||
float knee = 30;
|
||||
float ratio = 12;
|
||||
float release = 0.25;
|
||||
float threshold = -24;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional DynamicsCompressorOptions options)]
|
||||
interface DynamicsCompressorNode : AudioNode {
|
||||
|
||||
readonly attribute AudioParam threshold; // in Decibels
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary GainOptions : AudioNodeOptions {
|
||||
float gain = 1.0;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional GainOptions options)]
|
||||
interface GainNode : AudioNode {
|
||||
|
||||
readonly attribute AudioParam gain;
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary IIRFilterOptions : AudioNodeOptions {
|
||||
required sequence<double> feedforward;
|
||||
required sequence<double> feedback;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, IIRFilterOptions options)]
|
||||
interface IIRFilterNode : AudioNode {
|
||||
void getFrequencyResponse(Float32Array frequencyHz, Float32Array magResponse, Float32Array phaseResponse);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary MediaElementAudioSourceOptions {
|
||||
required HTMLMediaElement mediaElement;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, MediaElementAudioSourceOptions options)]
|
||||
interface MediaElementAudioSourceNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional AudioNodeOptions options)]
|
||||
interface MediaStreamAudioDestinationNode : AudioNode {
|
||||
|
||||
readonly attribute MediaStream stream;
|
||||
|
||||
};
|
||||
@@ -10,7 +10,12 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary MediaStreamAudioSourceOptions {
|
||||
required MediaStream mediaStream;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, MediaStreamAudioSourceOptions options)]
|
||||
interface MediaStreamAudioSourceNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
@@ -18,7 +18,15 @@ enum OscillatorType {
|
||||
"custom"
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary OscillatorOptions : AudioNodeOptions {
|
||||
OscillatorType type = "sine";
|
||||
float frequency = 440;
|
||||
float detune = 0;
|
||||
PeriodicWave periodicWave;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional OscillatorOptions options)]
|
||||
interface OscillatorNode : AudioNode {
|
||||
|
||||
[SetterThrows]
|
||||
@@ -39,4 +47,3 @@ interface OscillatorNode : AudioNode {
|
||||
|
||||
// Mozilla extensions
|
||||
OscillatorNode implements AudioNodePassThrough;
|
||||
|
||||
@@ -21,7 +21,25 @@ enum DistanceModelType {
|
||||
"exponential"
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary PannerOptions : AudioNodeOptions {
|
||||
PanningModelType panningModel = "equalpower";
|
||||
DistanceModelType distanceModel = "inverse";
|
||||
float positionX = 0;
|
||||
float positionY = 0;
|
||||
float positionZ = 0;
|
||||
float orientationX = 1;
|
||||
float orientationY = 0;
|
||||
float orientationZ = 0;
|
||||
double refDistance = 1;
|
||||
double maxDistance = 10000;
|
||||
double rolloffFactor = 1;
|
||||
double coneInnerAngle = 360;
|
||||
double coneOuterAngle = 360;
|
||||
double coneOuterGain = 0;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional PannerOptions options)]
|
||||
interface PannerNode : AudioNode {
|
||||
|
||||
// Default for stereo is equalpower
|
||||
|
||||
@@ -10,8 +10,12 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
interface PeriodicWave {
|
||||
|
||||
dictionary PeriodicWaveOptions : PeriodicWaveConstraints {
|
||||
sequence<float> real;
|
||||
sequence<float> imag;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, PeriodicWaveOptions options)]
|
||||
interface PeriodicWave {
|
||||
};
|
||||
@@ -10,7 +10,12 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary StereoPannerOptions : AudioNodeOptions {
|
||||
float pan = 0;
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional StereoPannerOptions options)]
|
||||
interface StereoPannerNode : AudioNode {
|
||||
readonly attribute AudioParam pan;
|
||||
};
|
||||
|
||||
@@ -16,10 +16,16 @@ enum OverSampleType {
|
||||
"4x"
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
dictionary WaveShaperOptions : AudioNodeOptions {
|
||||
sequence<float> curve;
|
||||
OverSampleType oversample = "none";
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
Constructor(AudioContext context, optional WaveShaperOptions options)]
|
||||
interface WaveShaperNode : AudioNode {
|
||||
|
||||
[SetterThrows]
|
||||
[Cached, Pure, SetterThrows]
|
||||
attribute Float32Array? curve;
|
||||
attribute OverSampleType oversample;
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ EditorBase::EditorBase()
|
||||
, mDispatchInputEvent(true)
|
||||
, mIsInEditAction(false)
|
||||
, mHidingCaret(false)
|
||||
, mIsHTMLEditorClass(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ class DeleteNodeTransaction;
|
||||
class DeleteTextTransaction;
|
||||
class EditAggregateTransaction;
|
||||
class ErrorResult;
|
||||
class HTMLEditor;
|
||||
class InsertNodeTransaction;
|
||||
class InsertTextTransaction;
|
||||
class JoinNodeTransaction;
|
||||
@@ -121,6 +122,7 @@ class PlaceholderTransaction;
|
||||
class RemoveStyleSheetTransaction;
|
||||
class SplitNodeTransaction;
|
||||
class TextComposition;
|
||||
class TextEditor;
|
||||
struct EditorDOMPoint;
|
||||
|
||||
namespace dom {
|
||||
@@ -1113,14 +1115,32 @@ protected:
|
||||
bool mIsInEditAction;
|
||||
// Whether caret is hidden forcibly.
|
||||
bool mHidingCaret;
|
||||
// Whether we are an HTML editor class.
|
||||
bool mIsHTMLEditorClass;
|
||||
|
||||
friend bool NSCanUnload(nsISupports* serviceMgr);
|
||||
friend class AutoRules;
|
||||
friend class AutoSelectionRestorer;
|
||||
friend class AutoTransactionsConserveSelection;
|
||||
friend class RangeUpdater;
|
||||
friend class nsIEditor;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
// nsIEditor helper functions.
|
||||
// Here because of code context.
|
||||
mozilla::EditorBase*
|
||||
nsIEditor::AsEditorBase()
|
||||
{
|
||||
return static_cast<mozilla::EditorBase*>(this);
|
||||
}
|
||||
|
||||
const mozilla::EditorBase*
|
||||
nsIEditor::AsEditorBase() const
|
||||
{
|
||||
return static_cast<const mozilla::EditorBase*>(this);
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifndef mozilla_EditorBase_h
|
||||
@@ -233,9 +233,16 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
|
||||
NS_IMETHODIMP
|
||||
HTMLEditRules::Init(TextEditor* aTextEditor)
|
||||
{
|
||||
if (NS_WARN_IF(!aTextEditor)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
InitFields();
|
||||
|
||||
mHTMLEditor = static_cast<HTMLEditor*>(aTextEditor);
|
||||
mHTMLEditor = aTextEditor->AsHTMLEditor();
|
||||
if (NS_WARN_IF(!mHTMLEditor)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// call through to base class Init
|
||||
nsresult rv = TextEditRules::Init(aTextEditor);
|
||||
|
||||
@@ -132,6 +132,7 @@ HTMLEditor::HTMLEditor()
|
||||
, mPositionedObjectBorderTop(0)
|
||||
, mGridSize(0)
|
||||
{
|
||||
mIsHTMLEditorClass = true;
|
||||
}
|
||||
|
||||
HTMLEditor::~HTMLEditor()
|
||||
@@ -501,7 +502,7 @@ HTMLEditor::InitRules()
|
||||
// instantiate the rules for the html editor
|
||||
mRules = new HTMLEditRules();
|
||||
}
|
||||
return mRules->Init(static_cast<TextEditor*>(this));
|
||||
return mRules->Init(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
||||
@@ -1108,4 +1108,20 @@ private:
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
// nsIEditor helper functions.
|
||||
// Here because of code context.
|
||||
mozilla::HTMLEditor*
|
||||
nsIEditor::AsHTMLEditor()
|
||||
{
|
||||
return static_cast<mozilla::EditorBase*>(this)->mIsHTMLEditorClass ?
|
||||
static_cast<mozilla::HTMLEditor*>(this) : nullptr;
|
||||
}
|
||||
|
||||
const mozilla::HTMLEditor*
|
||||
nsIEditor::AsHTMLEditor() const
|
||||
{
|
||||
return static_cast<const mozilla::EditorBase*>(this)->mIsHTMLEditorClass ?
|
||||
static_cast<const mozilla::HTMLEditor*>(this) : nullptr;
|
||||
}
|
||||
|
||||
#endif // #ifndef mozilla_HTMLEditor_h
|
||||
@@ -30,24 +30,18 @@ namespace mozilla {
|
||||
|
||||
using namespace dom;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsresult
|
||||
HTMLEditorEventListener::Connect(EditorBase* aEditorBase)
|
||||
{
|
||||
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryObject(aEditorBase);
|
||||
nsCOMPtr<nsIHTMLInlineTableEditor> htmlInlineTableEditor =
|
||||
do_QueryObject(aEditorBase);
|
||||
NS_PRECONDITION(htmlEditor && htmlInlineTableEditor,
|
||||
"Set HTMLEditor or its sub class");
|
||||
return EditorEventListener::Connect(aEditorBase);
|
||||
}
|
||||
#endif
|
||||
|
||||
HTMLEditor*
|
||||
HTMLEditorEventListener::GetHTMLEditor()
|
||||
{
|
||||
// mEditor must be HTMLEditor or its subclass.
|
||||
return static_cast<HTMLEditor*>(mEditorBase);
|
||||
if (NS_WARN_IF(!aEditorBase)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
// Guarantee that mEditorBase is always HTMLEditor.
|
||||
HTMLEditor* htmlEditor = aEditorBase->AsHTMLEditor();
|
||||
if (NS_WARN_IF(!htmlEditor)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return EditorEventListener::Connect(htmlEditor);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -59,7 +53,8 @@ HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
|
||||
|
||||
// FYI: We need to notify HTML editor of mouseup even if it's consumed
|
||||
// because HTML editor always needs to release grabbing resizer.
|
||||
HTMLEditor* htmlEditor = GetHTMLEditor();
|
||||
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
|
||||
MOZ_ASSERT(htmlEditor);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
|
||||
@@ -85,7 +80,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
|
||||
WidgetMouseEvent* mousedownEvent =
|
||||
aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
|
||||
|
||||
HTMLEditor* htmlEditor = GetHTMLEditor();
|
||||
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
|
||||
MOZ_ASSERT(htmlEditor);
|
||||
|
||||
// Contenteditable should disregard mousedowns outside it.
|
||||
// IsAcceptableInputEvent() checks it for a mouse event.
|
||||
if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) {
|
||||
@@ -221,13 +218,19 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
|
||||
nsresult
|
||||
HTMLEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
|
||||
{
|
||||
if (NS_WARN_IF(DetachedFromEditor())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
|
||||
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
|
||||
|
||||
GetHTMLEditor()->DoInlineTableEditingAction(element);
|
||||
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
|
||||
MOZ_ASSERT(htmlEditor);
|
||||
htmlEditor->DoInlineTableEditingAction(element);
|
||||
|
||||
return EditorEventListener::MouseClick(aMouseEvent);
|
||||
}
|
||||
|
||||
@@ -25,17 +25,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// WARNING: You must be use HTMLEditor or its sub class for this class.
|
||||
// Connect() fails if aEditorBase isn't an HTMLEditor instance.
|
||||
virtual nsresult Connect(EditorBase* aEditorBase) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual nsresult MouseDown(nsIDOMMouseEvent* aMouseEvent) override;
|
||||
virtual nsresult MouseUp(nsIDOMMouseEvent* aMouseEvent) override;
|
||||
virtual nsresult MouseClick(nsIDOMMouseEvent* aMouseEvent) override;
|
||||
|
||||
inline HTMLEditor* GetHTMLEditor();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "gfxFontUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/EditorUtils.h" // AutoEditBatch, AutoRules
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TextEditRules.h"
|
||||
|
||||
@@ -247,4 +247,18 @@ protected:
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
// nsIEditor helper functions.
|
||||
// Here because of code context.
|
||||
mozilla::TextEditor*
|
||||
nsIEditor::AsTextEditor()
|
||||
{
|
||||
return static_cast<mozilla::TextEditor*>(this);
|
||||
}
|
||||
|
||||
const mozilla::TextEditor*
|
||||
nsIEditor::AsTextEditor() const
|
||||
{
|
||||
return static_cast<const mozilla::TextEditor*>(this);
|
||||
}
|
||||
|
||||
#endif // #ifndef mozilla_TextEditor_h
|
||||
@@ -21,6 +21,14 @@ interface nsIEditActionListener;
|
||||
interface nsIInlineSpellChecker;
|
||||
interface nsITransferable;
|
||||
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
class EditorBase;
|
||||
class HTMLEditor;
|
||||
class TextEditor;
|
||||
} // namespace mozilla
|
||||
%}
|
||||
|
||||
[scriptable, uuid(094be624-f0bf-400f-89e2-6a84baab9474)]
|
||||
interface nsIEditor : nsISupports
|
||||
{
|
||||
@@ -564,4 +572,34 @@ interface nsIEditor : nsISupports
|
||||
* or nsIEditorObserver::CancelEditAction(). Otherwise, false.
|
||||
*/
|
||||
[noscript] readonly attribute boolean isInEditAction;
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* AsEditorBase() returns a pointer to EditorBase class.
|
||||
*
|
||||
* In order to avoid circular dependency issues, this method is defined
|
||||
* in mozilla/EditorBase.h. Consumers need to #include that header.
|
||||
*/
|
||||
inline mozilla::EditorBase* AsEditorBase();
|
||||
inline const mozilla::EditorBase* AsEditorBase() const;
|
||||
|
||||
/**
|
||||
* AsTextEditor() returns a pointer to TextEditor class.
|
||||
*
|
||||
* In order to avoid circular dependency issues, this method is defined
|
||||
* in mozilla/TextEditor.h. Consumers need to #include that header.
|
||||
*/
|
||||
inline mozilla::TextEditor* AsTextEditor();
|
||||
inline const mozilla::TextEditor* AsTextEditor() const;
|
||||
|
||||
/**
|
||||
* AsHTMLEditor() returns a pointer to HTMLEditor class.
|
||||
*
|
||||
* In order to avoid circular dependency issues, this method is defined
|
||||
* in mozilla/HTMLEditor.h. Consumers need to #include that header.
|
||||
*/
|
||||
inline mozilla::HTMLEditor* AsHTMLEditor();
|
||||
inline const mozilla::HTMLEditor* AsHTMLEditor() const;
|
||||
%}
|
||||
|
||||
};
|
||||
@@ -97,9 +97,6 @@ using namespace mozilla::dom;
|
||||
#define INLINESPELL_STARTED_TOPIC "inlineSpellChecker-spellCheck-started"
|
||||
#define INLINESPELL_ENDED_TOPIC "inlineSpellChecker-spellCheck-ended"
|
||||
|
||||
static bool ContentIsDescendantOf(nsINode* aPossibleDescendant,
|
||||
nsINode* aPossibleAncestor);
|
||||
|
||||
static const char kMaxSpellCheckSelectionSize[] = "extensions.spellcheck.inline.max-misspellings";
|
||||
|
||||
mozInlineSpellStatus::mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker)
|
||||
@@ -238,7 +235,7 @@ mozInlineSpellStatus::InitForNavigation(
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsINode> currentAnchor = do_QueryInterface(aOldAnchorNode, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (root && currentAnchor && ! ContentIsDescendantOf(currentAnchor, root)) {
|
||||
if (root && currentAnchor && !nsContentUtils::ContentIsShadowIncludingDescendantOf(currentAnchor, root)) {
|
||||
*aContinue = false;
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -1490,8 +1487,11 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aWordUtil.SetEnd(endNode, endOffset);
|
||||
aWordUtil.SetPosition(beginNode, beginOffset);
|
||||
nsresult rv = aWordUtil.SetPositionAndEnd(beginNode, beginOffset, endNode, endOffset);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Just bail out and don't try to spell-check this
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// aWordUtil.SetPosition flushes pending notifications, check editor again.
|
||||
@@ -1840,24 +1840,6 @@ nsresult mozInlineSpellChecker::SaveCurrentSelectionPosition()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This is a copy of nsContentUtils::ContentIsDescendantOf. Another crime
|
||||
// for XPCOM's rap sheet
|
||||
bool // static
|
||||
ContentIsDescendantOf(nsINode* aPossibleDescendant,
|
||||
nsINode* aPossibleAncestor)
|
||||
{
|
||||
NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
|
||||
NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
|
||||
|
||||
do {
|
||||
if (aPossibleDescendant == aPossibleAncestor)
|
||||
return true;
|
||||
aPossibleDescendant = aPossibleDescendant->GetParentNode();
|
||||
} while (aPossibleDescendant);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::HandleNavigationEvent
|
||||
//
|
||||
// Acts upon mouse clicks and keyboard navigation changes, spell checking
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "nsIFrame.h"
|
||||
#include <algorithm>
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
@@ -69,8 +71,10 @@ mozInlineSpellWordUtil::Init(nsWeakPtr aWeakEditor)
|
||||
mDOMDocument = domDoc;
|
||||
mDocument = do_QueryInterface(domDoc);
|
||||
|
||||
// Find the root node for the editor. For contenteditable we'll need something
|
||||
// cleverer here.
|
||||
mIsContentEditableOrDesignMode = !!editor->AsHTMLEditor();
|
||||
|
||||
// Find the root node for the editor. For contenteditable the mRootNode could
|
||||
// change to shadow root if the begin and end are inside the shadowDOM.
|
||||
nsCOMPtr<nsIDOMElement> rootElt;
|
||||
rv = editor->GetRootElement(getter_AddRefs(rootElt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@@ -154,7 +158,7 @@ FindNextTextNode(nsINode* aNode, int32_t aOffset, nsINode* aRoot)
|
||||
return checkNode;
|
||||
}
|
||||
|
||||
// mozInlineSpellWordUtil::SetEnd
|
||||
// mozInlineSpellWordUtil::SetPositionAndEnd
|
||||
//
|
||||
// We have two ranges "hard" and "soft". The hard boundary is simply
|
||||
// the scope of the root node. The soft boundary is that which is set
|
||||
@@ -172,34 +176,44 @@ FindNextTextNode(nsINode* aNode, int32_t aOffset, nsINode* aRoot)
|
||||
// position.
|
||||
|
||||
nsresult
|
||||
mozInlineSpellWordUtil::SetEnd(nsINode* aEndNode, int32_t aEndOffset)
|
||||
mozInlineSpellWordUtil::SetPositionAndEnd(nsINode* aPositionNode,
|
||||
int32_t aPositionOffset,
|
||||
nsINode* aEndNode,
|
||||
int32_t aEndOffset)
|
||||
{
|
||||
MOZ_ASSERT(aPositionNode, "Null begin node?");
|
||||
NS_PRECONDITION(aEndNode, "Null end node?");
|
||||
|
||||
NS_ASSERTION(mRootNode, "Not initialized");
|
||||
|
||||
// Find a appropriate root if we are dealing with contenteditable nodes which
|
||||
// are in the shadow DOM. See UXP Issue #3011
|
||||
if (mIsContentEditableOrDesignMode) {
|
||||
nsINode* rootNode = aPositionNode->SubtreeRoot();
|
||||
if (rootNode != aEndNode->SubtreeRoot()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mozilla::dom::ShadowRoot::FromNode(rootNode)) {
|
||||
mRootNode = rootNode;
|
||||
}
|
||||
}
|
||||
|
||||
InvalidateWords();
|
||||
|
||||
if (!IsTextNode(aPositionNode)) {
|
||||
// Start at the start of the first text node after aNode/aOffset.
|
||||
aPositionNode = FindNextTextNode(aPositionNode, aPositionOffset, mRootNode);
|
||||
aPositionOffset = 0;
|
||||
}
|
||||
mSoftBegin = NodeOffset(aPositionNode, aPositionOffset);
|
||||
|
||||
if (!IsTextNode(aEndNode)) {
|
||||
// End at the start of the first text node after aEndNode/aEndOffset.
|
||||
aEndNode = FindNextTextNode(aEndNode, aEndOffset, mRootNode);
|
||||
aEndOffset = 0;
|
||||
}
|
||||
mSoftEnd = NodeOffset(aEndNode, aEndOffset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
mozInlineSpellWordUtil::SetPosition(nsINode* aNode, int32_t aOffset)
|
||||
{
|
||||
InvalidateWords();
|
||||
|
||||
if (!IsTextNode(aNode)) {
|
||||
// Start at the start of the first text node after aNode/aOffset.
|
||||
aNode = FindNextTextNode(aNode, aOffset, mRootNode);
|
||||
aOffset = 0;
|
||||
}
|
||||
mSoftBegin = NodeOffset(aNode, aOffset);
|
||||
|
||||
nsresult rv = EnsureWords();
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -207,8 +221,10 @@ mozInlineSpellWordUtil::SetPosition(nsINode* aNode, int32_t aOffset)
|
||||
}
|
||||
|
||||
int32_t textOffset = MapDOMPositionToSoftTextOffset(mSoftBegin);
|
||||
if (textOffset < 0)
|
||||
if (textOffset < 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mNextWordIndex = FindRealWordContaining(textOffset, HINT_END, true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,11 @@ class nsINode;
|
||||
* The basic operation is:
|
||||
*
|
||||
* 1. Call Init with the weak pointer to the editor that you're using.
|
||||
* 2. Call SetEnd to set where you want to stop spellchecking. We'll stop
|
||||
* at the word boundary after that. If SetEnd is not called, we'll stop
|
||||
* at the end of the document's root element.
|
||||
* 3. Call SetPosition to initialize the current position inside the
|
||||
* previously given range.
|
||||
* 4. Call GetNextWord over and over until it returns false.
|
||||
* 2. Call SetPositionAndEnd to to initialize the current position inside the
|
||||
* previously given range and set where you want to stop spellchecking.
|
||||
* We'll stop at the word boundary after that. If SetEnd is not called,
|
||||
* we'll stop at the end of the root element.
|
||||
* 3. Call GetNextWord over and over until it returns false.
|
||||
*/
|
||||
|
||||
class mozInlineSpellWordUtil
|
||||
@@ -57,17 +56,22 @@ public:
|
||||
};
|
||||
|
||||
mozInlineSpellWordUtil()
|
||||
: mRootNode(nullptr),
|
||||
mSoftBegin(nullptr, 0), mSoftEnd(nullptr, 0),
|
||||
mNextWordIndex(-1), mSoftTextValid(false) {}
|
||||
: mIsContentEditableOrDesignMode(false)
|
||||
, mRootNode(nullptr)
|
||||
, mSoftBegin(nullptr, 0)
|
||||
, mSoftEnd(nullptr, 0)
|
||||
, mNextWordIndex(-1)
|
||||
, mSoftTextValid(false)
|
||||
{}
|
||||
|
||||
nsresult Init(nsWeakPtr aWeakEditor);
|
||||
|
||||
nsresult SetEnd(nsINode* aEndNode, int32_t aEndOffset);
|
||||
|
||||
// sets the current position, this should be inside the range. If we are in
|
||||
// the middle of a word, we'll move to its start.
|
||||
nsresult SetPosition(nsINode* aNode, int32_t aOffset);
|
||||
// Sets the current position and end. This should be inside the range.
|
||||
// If we are in the middle of a word, we'll move to its start.
|
||||
nsresult SetPositionAndEnd(nsINode* aPositionNode,
|
||||
int32_t aPositionOffset,
|
||||
nsINode* aEndNode,
|
||||
int32_t aEndOffset);
|
||||
|
||||
// Given a point inside or immediately following a word, this returns the
|
||||
// DOM range that exactly encloses that word's characters. The current
|
||||
@@ -100,7 +104,8 @@ private:
|
||||
|
||||
// cached stuff for the editor, set by Init
|
||||
nsCOMPtr<nsIDOMDocument> mDOMDocument;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
bool mIsContentEditableOrDesignMode;
|
||||
|
||||
// range to check, see SetPosition and SetEnd
|
||||
nsINode* mRootNode;
|
||||
|
||||
+1132
-1048
File diff suppressed because it is too large.
Load diff
@@ -1247,6 +1247,7 @@ static const JSFunctionSpec object_static_methods[] = {
|
||||
JS_FN("seal", obj_seal, 1, 0),
|
||||
JS_FN("isSealed", obj_isSealed, 1, 0),
|
||||
JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0),
|
||||
JS_SELF_HOSTED_FN("groupBy", "ObjectGroupBy", 2, 0),
|
||||
JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
+178
-138
@@ -4,228 +4,268 @@
|
||||
|
||||
// ES6 draft rev36 2015-03-17 19.1.2.1
|
||||
function ObjectStaticAssign(target, firstSource) {
|
||||
// Steps 1-2.
|
||||
var to = ToObject(target);
|
||||
// Steps 1-2.
|
||||
var to = ToObject(target);
|
||||
|
||||
// Step 3.
|
||||
if (arguments.length < 2)
|
||||
return to;
|
||||
// Step 3.
|
||||
if (arguments.length < 2) return to;
|
||||
|
||||
// Steps 4-5.
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
// Step 5.a.
|
||||
var nextSource = arguments[i];
|
||||
if (nextSource === null || nextSource === undefined)
|
||||
continue;
|
||||
// Steps 4-5.
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
// Step 5.a.
|
||||
var nextSource = arguments[i];
|
||||
if (nextSource === null || nextSource === undefined) continue;
|
||||
|
||||
// Steps 5.b.i-ii.
|
||||
var from = ToObject(nextSource);
|
||||
// Steps 5.b.i-ii.
|
||||
var from = ToObject(nextSource);
|
||||
|
||||
// Steps 5.b.iii-iv.
|
||||
var keys = OwnPropertyKeys(from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
|
||||
// Steps 5.b.iii-iv.
|
||||
var keys = OwnPropertyKeys(
|
||||
from,
|
||||
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
|
||||
);
|
||||
|
||||
// Step 5.c.
|
||||
for (var nextIndex = 0, len = keys.length; nextIndex < len; nextIndex++) {
|
||||
var nextKey = keys[nextIndex];
|
||||
// Step 5.c.
|
||||
for (var nextIndex = 0, len = keys.length; nextIndex < len; nextIndex++) {
|
||||
var nextKey = keys[nextIndex];
|
||||
|
||||
// Steps 5.c.i-iii. We abbreviate this by calling propertyIsEnumerable
|
||||
// which is faster and returns false for not defined properties.
|
||||
if (callFunction(std_Object_propertyIsEnumerable, from, nextKey)) {
|
||||
// Steps 5.c.iii.1-4.
|
||||
to[nextKey] = from[nextKey];
|
||||
}
|
||||
}
|
||||
// Steps 5.c.i-iii. We abbreviate this by calling propertyIsEnumerable
|
||||
// which is faster and returns false for not defined properties.
|
||||
if (callFunction(std_Object_propertyIsEnumerable, from, nextKey)) {
|
||||
// Steps 5.c.iii.1-4.
|
||||
to[nextKey] = from[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
return to;
|
||||
// Step 6.
|
||||
return to;
|
||||
}
|
||||
|
||||
// ES stage 4 proposal
|
||||
function ObjectGetOwnPropertyDescriptors(O) {
|
||||
// Step 1.
|
||||
var obj = ToObject(O);
|
||||
// Step 1.
|
||||
var obj = ToObject(O);
|
||||
|
||||
// Step 2.
|
||||
var keys = OwnPropertyKeys(obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
|
||||
// Step 2.
|
||||
var keys = OwnPropertyKeys(
|
||||
obj,
|
||||
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
|
||||
);
|
||||
|
||||
// Step 3.
|
||||
var descriptors = {};
|
||||
// Step 3.
|
||||
var descriptors = {};
|
||||
|
||||
// Step 4.
|
||||
for (var index = 0, len = keys.length; index < len; index++) {
|
||||
var key = keys[index];
|
||||
// Step 4.
|
||||
for (var index = 0, len = keys.length; index < len; index++) {
|
||||
var key = keys[index];
|
||||
|
||||
// Steps 4.a-b.
|
||||
var desc = std_Object_getOwnPropertyDescriptor(obj, key);
|
||||
// Steps 4.a-b.
|
||||
var desc = std_Object_getOwnPropertyDescriptor(obj, key);
|
||||
|
||||
// Step 4.c.
|
||||
if (typeof desc !== "undefined")
|
||||
_DefineDataProperty(descriptors, key, desc);
|
||||
}
|
||||
// Step 4.c.
|
||||
if (typeof desc !== "undefined")
|
||||
_DefineDataProperty(descriptors, key, desc);
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
return descriptors;
|
||||
// Step 5.
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.9. */
|
||||
function ObjectGetPrototypeOf(obj) {
|
||||
return std_Reflect_getPrototypeOf(ToObject(obj));
|
||||
return std_Reflect_getPrototypeOf(ToObject(obj));
|
||||
}
|
||||
|
||||
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.11. */
|
||||
function ObjectIsExtensible(obj) {
|
||||
return IsObject(obj) && std_Reflect_isExtensible(obj);
|
||||
return IsObject(obj) && std_Reflect_isExtensible(obj);
|
||||
}
|
||||
|
||||
/* ES2015 19.1.3.5 Object.prototype.toLocaleString */
|
||||
function Object_toLocaleString() {
|
||||
// Step 1.
|
||||
var O = this;
|
||||
// Step 1.
|
||||
var O = this;
|
||||
|
||||
// Step 2.
|
||||
return callContentFunction(O.toString, O);
|
||||
// Step 2.
|
||||
return callContentFunction(O.toString, O);
|
||||
}
|
||||
|
||||
// ES 2017 draft bb96899bb0d9ef9be08164a26efae2ee5f25e875 19.1.3.7
|
||||
function Object_valueOf() {
|
||||
// Step 1.
|
||||
return ToObject(this);
|
||||
// Step 1.
|
||||
return ToObject(this);
|
||||
}
|
||||
|
||||
// ES7 draft (2016 March 8) B.2.2.3
|
||||
function ObjectDefineSetter(name, setter) {
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
|
||||
// Step 2.
|
||||
if (!IsCallable(setter))
|
||||
ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter");
|
||||
// Step 2.
|
||||
if (!IsCallable(setter)) ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter");
|
||||
|
||||
// Step 3.
|
||||
var desc = {
|
||||
__proto__: null,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: setter
|
||||
};
|
||||
// Step 3.
|
||||
var desc = {
|
||||
__proto__: null,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: setter,
|
||||
};
|
||||
|
||||
// Step 4.
|
||||
var key = ToPropertyKey(name);
|
||||
// Step 4.
|
||||
var key = ToPropertyKey(name);
|
||||
|
||||
// Step 5.
|
||||
std_Object_defineProperty(object, key, desc);
|
||||
// Step 5.
|
||||
std_Object_defineProperty(object, key, desc);
|
||||
|
||||
// Step 6. (implicit)
|
||||
// Step 6. (implicit)
|
||||
}
|
||||
|
||||
// ES7 draft (2016 March 8) B.2.2.2
|
||||
function ObjectDefineGetter(name, getter) {
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
|
||||
// Step 2.
|
||||
if (!IsCallable(getter))
|
||||
ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "getter");
|
||||
// Step 2.
|
||||
if (!IsCallable(getter)) ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "getter");
|
||||
|
||||
// Step 3.
|
||||
var desc = {
|
||||
__proto__: null,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: getter
|
||||
};
|
||||
// Step 3.
|
||||
var desc = {
|
||||
__proto__: null,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: getter,
|
||||
};
|
||||
|
||||
// Step 4.
|
||||
var key = ToPropertyKey(name);
|
||||
// Step 4.
|
||||
var key = ToPropertyKey(name);
|
||||
|
||||
// Step 5.
|
||||
std_Object_defineProperty(object, key, desc);
|
||||
// Step 5.
|
||||
std_Object_defineProperty(object, key, desc);
|
||||
|
||||
// Step 6. (implicit)
|
||||
// Step 6. (implicit)
|
||||
}
|
||||
|
||||
// ES7 draft (2016 March 8) B.2.2.5
|
||||
function ObjectLookupSetter(name) {
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
|
||||
// Step 2.
|
||||
var key = ToPropertyKey(name)
|
||||
// Step 2.
|
||||
var key = ToPropertyKey(name);
|
||||
|
||||
do {
|
||||
// Step 3.a.
|
||||
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
||||
do {
|
||||
// Step 3.a.
|
||||
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
||||
|
||||
// Step 3.b.
|
||||
if (desc) {
|
||||
// Step.b.i.
|
||||
if (callFunction(std_Object_hasOwnProperty, desc, "set"))
|
||||
return desc.set;
|
||||
// Step 3.b.
|
||||
if (desc) {
|
||||
// Step.b.i.
|
||||
if (callFunction(std_Object_hasOwnProperty, desc, "set")) return desc.set;
|
||||
|
||||
// Step.b.ii.
|
||||
return undefined;
|
||||
}
|
||||
// Step.b.ii.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Step 3.c.
|
||||
object = std_Reflect_getPrototypeOf(object);
|
||||
} while (object !== null);
|
||||
// Step 3.c.
|
||||
object = std_Reflect_getPrototypeOf(object);
|
||||
} while (object !== null);
|
||||
|
||||
// Step 3.d. (implicit)
|
||||
// Step 3.d. (implicit)
|
||||
}
|
||||
|
||||
// ES7 draft (2016 March 8) B.2.2.4
|
||||
function ObjectLookupGetter(name) {
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
// Step 1.
|
||||
var object = ToObject(this);
|
||||
|
||||
// Step 2.
|
||||
var key = ToPropertyKey(name)
|
||||
// Step 2.
|
||||
var key = ToPropertyKey(name);
|
||||
|
||||
do {
|
||||
// Step 3.a.
|
||||
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
||||
do {
|
||||
// Step 3.a.
|
||||
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
||||
|
||||
// Step 3.b.
|
||||
if (desc) {
|
||||
// Step.b.i.
|
||||
if (callFunction(std_Object_hasOwnProperty, desc, "get"))
|
||||
return desc.get;
|
||||
// Step 3.b.
|
||||
if (desc) {
|
||||
// Step.b.i.
|
||||
if (callFunction(std_Object_hasOwnProperty, desc, "get")) return desc.get;
|
||||
|
||||
// Step.b.ii.
|
||||
return undefined;
|
||||
}
|
||||
// Step.b.ii.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Step 3.c.
|
||||
object = std_Reflect_getPrototypeOf(object);
|
||||
} while (object !== null);
|
||||
// Step 3.c.
|
||||
object = std_Reflect_getPrototypeOf(object);
|
||||
} while (object !== null);
|
||||
|
||||
// Step 3.d. (implicit)
|
||||
// Step 3.d. (implicit)
|
||||
}
|
||||
|
||||
// Stage 4 draft 2020-09-06 https://tc39.github.io/proposal-object-from-entries/
|
||||
// Object.fromEntries (iterable)
|
||||
function ObjectFromEntries(iter) {
|
||||
// We omit the usual step number comments here because they don't help.
|
||||
// This implementation inlines AddEntriesFromIterator and
|
||||
// CreateDataPropertyOnObject, so it looks more like the polyfill
|
||||
// than the step-by-step spec algorithm.
|
||||
const obj = {};
|
||||
// We omit the usual step number comments here because they don't help.
|
||||
// This implementation inlines AddEntriesFromIterator and
|
||||
// CreateDataPropertyOnObject, so it looks more like the polyfill
|
||||
// than the step-by-step spec algorithm.
|
||||
const obj = {};
|
||||
|
||||
for (const pair of allowContentIter(iter)) {
|
||||
if (!IsObject(pair))
|
||||
ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries");
|
||||
_DefineDataProperty(obj, pair[0], pair[1]);
|
||||
for (const pair of allowContentIter(iter)) {
|
||||
if (!IsObject(pair))
|
||||
ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries");
|
||||
_DefineDataProperty(obj, pair[0], pair[1]);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// ES2024
|
||||
// Object.groupBy ( items, callbackfn )
|
||||
function ObjectGroupBy(items, callbackfn) {
|
||||
// Step 1.
|
||||
RequireObjectCoercible(items);
|
||||
|
||||
// Step 2.
|
||||
if (!IsCallable(callbackfn))
|
||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
|
||||
|
||||
// Step 3.
|
||||
var groups = std_Object_create(null);
|
||||
|
||||
// Step 4.
|
||||
var k = 0;
|
||||
|
||||
// Steps 5-8.
|
||||
for (var value of allowContentIter(items)) {
|
||||
// Step 6.a.
|
||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||
|
||||
// Step 6.b.
|
||||
key = ToPropertyKey(key);
|
||||
|
||||
// Steps 6.c-d.
|
||||
var elements = groups[key];
|
||||
if (elements === undefined) {
|
||||
_DefineDataProperty(groups, key, [value]);
|
||||
} else {
|
||||
callFunction(std_Array_push, elements, value);
|
||||
}
|
||||
|
||||
return obj;
|
||||
// Step 6.e.
|
||||
k++;
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
return groups;
|
||||
}
|
||||
|
||||
// Proposal https://github.com/tc39/proposal-accessible-object-hasownproperty
|
||||
// Object.hasOwn (Object, Property)
|
||||
function ObjectHasOwn(O, P) {
|
||||
// Step 1.
|
||||
var obj = ToObject(O);
|
||||
// Step 2-3.
|
||||
return callFunction(std_Object_hasOwnProperty, obj, P);
|
||||
// Step 1.
|
||||
var obj = ToObject(O);
|
||||
// Step 2-3.
|
||||
return callFunction(std_Object_hasOwnProperty, obj, P);
|
||||
}
|
||||
@@ -60,6 +60,12 @@ function IsTypedArrayEnsuringArrayBuffer(arg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsObject(arg) && IsPossiblyWrappedTypedArray(arg)) {
|
||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(arg))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
return false;
|
||||
}
|
||||
|
||||
callFunction(CallTypedArrayMethodIfWrapped, arg, "GetAttachedArrayBufferMethod");
|
||||
return false;
|
||||
}
|
||||
@@ -157,6 +163,15 @@ function TypedArraySpeciesCreateWithBuffer(exemplar, buffer, byteOffset, length)
|
||||
return TypedArrayCreateWithBuffer(C, buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
// ES2024 23.2.4.5 TypedArrayCreateSameType ( exemplar, argumentList )
|
||||
function TypedArrayCreateSameType(exemplar, length) {
|
||||
// Step 1.
|
||||
var defaultConstructor = _ConstructorForTypedArray(exemplar);
|
||||
|
||||
// Step 2.
|
||||
return TypedArrayCreateWithLength(defaultConstructor, length);
|
||||
}
|
||||
|
||||
// ES6 draft 20150304 %TypedArray%.prototype.copyWithin
|
||||
function TypedArrayCopyWithin(target, start, end = undefined) {
|
||||
// This function is not generic.
|
||||
@@ -848,6 +863,38 @@ function TypedArrayReverse() {
|
||||
return O;
|
||||
}
|
||||
|
||||
// ES2023 23.2.3.32 %TypedArray%.prototype.toReversed ( )
|
||||
function TypedArrayToReversed() {
|
||||
// Step 1.
|
||||
var O = this;
|
||||
|
||||
// Step 2.
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a wrapper for one.
|
||||
|
||||
// Step 3.
|
||||
var len;
|
||||
if (isTypedArray)
|
||||
len = TypedArrayLength(O);
|
||||
else
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod");
|
||||
|
||||
// Step 4.
|
||||
var A = TypedArrayCreateSameType(O, len);
|
||||
|
||||
// Steps 5-6.
|
||||
for (var k = 0; k < len; k++) {
|
||||
var from = len - k - 1;
|
||||
A[k] = O[from];
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
return A;
|
||||
}
|
||||
|
||||
// ES6 draft 20150220 22.2.3.22.1 %TypedArray%.prototype.set(array [, offset])
|
||||
function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetBuffer) {
|
||||
assert(!IsPossiblyWrappedTypedArray(array),
|
||||
|
||||
+5
-1
@@ -3209,7 +3209,9 @@ static const JSFunctionSpec array_methods[] = {
|
||||
JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1,0),
|
||||
JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1,0),
|
||||
JS_SELF_HOSTED_FN("toReversed", "ArrayToReversed", 0,0),
|
||||
JS_SELF_HOSTED_FN("toSpliced", "ArrayToSpliced", 2,0),
|
||||
JS_SELF_HOSTED_FN("toSorted", "ArrayToSorted", 1,0),
|
||||
JS_SELF_HOSTED_FN("with", "ArrayWith", 2,0),
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
@@ -3382,8 +3384,10 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
||||
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toReversed, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toSpliced, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().values, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toSorted, value))
|
||||
!DefineProperty(cx, unscopables, cx->names().toSorted, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().with, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>Array.prototype.toSpliced test</title>
|
||||
<style>
|
||||
body { font: 14px/1.4 sans-serif; padding: 16px; }
|
||||
.pass { color: #0a0; }
|
||||
.fail { color: #c00; }
|
||||
pre { white-space: pre-wrap; }
|
||||
</style>
|
||||
<pre id="log"></pre>
|
||||
<script>
|
||||
(function() {
|
||||
const log = document.getElementById("log");
|
||||
function write(msg, cls) {
|
||||
const line = document.createElement("div");
|
||||
if (cls) line.className = cls;
|
||||
line.textContent = msg;
|
||||
log.appendChild(line);
|
||||
}
|
||||
|
||||
window.assertEq = function(actual, expected, msg) {
|
||||
if (actual !== expected) {
|
||||
throw new Error((msg ? msg + ": " : "") +
|
||||
"expected " + expected + ", got " + actual);
|
||||
}
|
||||
};
|
||||
|
||||
window.assertThrowsInstanceOf = function(fn, ctor, msg) {
|
||||
let threw = false;
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
if (e instanceof ctor) {
|
||||
threw = true;
|
||||
} else {
|
||||
throw new Error((msg ? msg + ": " : "") +
|
||||
"threw " + e + ", expected " + ctor.name);
|
||||
}
|
||||
}
|
||||
if (!threw) {
|
||||
throw new Error((msg ? msg + ": " : "") + "did not throw");
|
||||
}
|
||||
};
|
||||
|
||||
window.reportCompare = function() {};
|
||||
|
||||
let hadError = false;
|
||||
window.addEventListener("error", function(e) {
|
||||
hadError = true;
|
||||
write("FAIL: " + e.message, "fail");
|
||||
});
|
||||
window.addEventListener("load", function() {
|
||||
write("PASS: toSpliced.js loaded", "pass");
|
||||
if (!hadError)
|
||||
write("PASS: all toSpliced tests passed", "pass");
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="toSpliced.js"></script>
|
||||
@@ -0,0 +1,113 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
assertEq(typeof Array.prototype.toSpliced, "function");
|
||||
assertEq(Array.prototype.toSpliced.length, 2);
|
||||
|
||||
let desc = Object.getOwnPropertyDescriptor(Array.prototype, "toSpliced");
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, true);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
Array.prototype.toSpliced.call(null, 0, 0);
|
||||
}, TypeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
Array.prototype.toSpliced.call(undefined, 0, 0);
|
||||
}, TypeError);
|
||||
|
||||
// Non-mutating behavior.
|
||||
let original = [1, 2, 3, 4];
|
||||
let spliced = original.toSpliced(1, 2, "a", "b");
|
||||
assertEq(original !== spliced, true);
|
||||
assertEq(original.join(","), "1,2,3,4");
|
||||
assertEq(spliced.join(","), "1,a,b,4");
|
||||
assertEq(original.toSpliced().join(","), "1,2,3,4");
|
||||
|
||||
// Start index clamping and negatives.
|
||||
assertEq([1, 2, 3].toSpliced(-1, 1, 9).join(","), "1,2,9");
|
||||
assertEq([1, 2, 3].toSpliced(-99, 1, 9).join(","), "9,2,3");
|
||||
assertEq([1, 2, 3].toSpliced(99, 1, 9).join(","), "1,2,3,9");
|
||||
|
||||
// deleteCount handling.
|
||||
assertEq([1, 2, 3].toSpliced(1).join(","), "1");
|
||||
assertEq([1, 2, 3].toSpliced(1, -3, 9).join(","), "1,9,2,3");
|
||||
|
||||
// Holes are materialized as undefined in copied segments.
|
||||
let sparse = [1, , 3, 4];
|
||||
let sparseResult = sparse.toSpliced(2, 1, "x");
|
||||
assertEq(sparseResult.length, 4);
|
||||
assertEq(sparseResult[0], 1);
|
||||
assertEq(sparseResult[1], undefined);
|
||||
assertEq(1 in sparseResult, true);
|
||||
assertEq(sparseResult[2], "x");
|
||||
assertEq(sparseResult[3], 4);
|
||||
|
||||
// Generic behavior on array-like values.
|
||||
let arrayLike = { 0: "a", 1: "b", 3: "d", length: 4 };
|
||||
let arrayLikeResult = Array.prototype.toSpliced.call(arrayLike, 1, 2, "X", "Y");
|
||||
assertEq(Array.isArray(arrayLikeResult), true);
|
||||
assertEq(arrayLikeResult.join(","), "a,X,Y,d");
|
||||
|
||||
// Element reads are ascending and skipped region is not read.
|
||||
let accessLog = [];
|
||||
let getterArr = {
|
||||
length: 5,
|
||||
get 0() {
|
||||
accessLog.push(0);
|
||||
return "z0";
|
||||
},
|
||||
get 1() {
|
||||
accessLog.push(1);
|
||||
throw new Error("must not be read");
|
||||
},
|
||||
get 2() {
|
||||
accessLog.push(2);
|
||||
throw new Error("must not be read");
|
||||
},
|
||||
get 3() {
|
||||
accessLog.push(3);
|
||||
return "z3";
|
||||
},
|
||||
get 4() {
|
||||
accessLog.push(4);
|
||||
return "z4";
|
||||
},
|
||||
};
|
||||
let getterResult = Array.prototype.toSpliced.call(getterArr, 1, 2, "X");
|
||||
assertEq(accessLog.join(","), "0,3,4");
|
||||
assertEq(getterResult.join(","), "z0,X,z3,z4");
|
||||
|
||||
// constructor / Symbol.species is ignored and constructor is not read.
|
||||
let constructorAccessed = false;
|
||||
let speciesIgnored = [1, 2, 3];
|
||||
Object.defineProperty(speciesIgnored, "constructor", {
|
||||
get: function () {
|
||||
constructorAccessed = true;
|
||||
throw new Error("constructor should not be read");
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
let speciesIgnoredResult = speciesIgnored.toSpliced(1, 1, 9);
|
||||
assertEq(constructorAccessed, false);
|
||||
assertEq(Array.isArray(speciesIgnoredResult), true);
|
||||
assertEq(speciesIgnoredResult.join(","), "1,9,3");
|
||||
|
||||
// Length limit check happens before indexed reads.
|
||||
let indexedRead = false;
|
||||
let tooLong = {
|
||||
length: 4294967296,
|
||||
get 0() {
|
||||
indexedRead = true;
|
||||
throw new Error("index getter should not run");
|
||||
},
|
||||
};
|
||||
assertThrowsInstanceOf(function () {
|
||||
Array.prototype.toSpliced.call(tooLong, 0, 0);
|
||||
}, RangeError);
|
||||
assertEq(indexedRead, false);
|
||||
|
||||
// @@unscopables must include toSpliced.
|
||||
assertEq(Array.prototype[Symbol.unscopables].toSpliced, true);
|
||||
|
||||
if (typeof reportCompare === "function") reportCompare(0, 0);
|
||||
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user