зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1558124 - When an exception happens in the AudioWorkletGlobalScope, fire `onprocessorerror`. r=karlt
Differential Revision: https://phabricator.services.mozilla.com/D64766 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
2fef3e1da9
Коммит
3514c96161
|
@ -10,10 +10,15 @@
|
|||
#include "js/Array.h" // JS::{Get,Set}ArrayLength, JS::NewArrayLength
|
||||
#include "mozilla/dom/AudioWorkletNodeBinding.h"
|
||||
#include "mozilla/dom/AudioParamMapBinding.h"
|
||||
#include "mozilla/dom/RootedDictionary.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "AudioParam.h"
|
||||
#include "AudioDestinationNode.h"
|
||||
#include "mozilla/dom/MessageChannel.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "PlayingRefChangeHandler.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
|
@ -32,6 +37,14 @@ struct NamedAudioParamTimeline {
|
|||
AudioParamTimeline mTimeline;
|
||||
};
|
||||
|
||||
struct ProcessorErrorDetails {
|
||||
ProcessorErrorDetails() : mLineno(0), mColno(0) {}
|
||||
unsigned mLineno;
|
||||
unsigned mColno;
|
||||
nsString mFilename;
|
||||
nsString mMessage;
|
||||
};
|
||||
|
||||
class WorkletNodeEngine final : public AudioNodeEngine {
|
||||
public:
|
||||
WorkletNodeEngine(AudioWorkletNode* aNode,
|
||||
|
@ -109,6 +122,8 @@ class WorkletNodeEngine final : public AudioNodeEngine {
|
|||
bool CallProcess(AudioNodeTrack* aTrack, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aCallable);
|
||||
void ProduceSilence(AudioNodeTrack* aTrack, Span<AudioBlock> aOutput);
|
||||
void SendErrorToMainThread(AudioNodeTrack* aTrack,
|
||||
const ProcessorErrorDetails& aDetails);
|
||||
|
||||
void ReleaseJSResources() {
|
||||
mInputs.mPorts.clearAndFree();
|
||||
|
@ -159,13 +174,70 @@ class WorkletNodeEngine final : public AudioNodeEngine {
|
|||
bool mKeepEngineActive = true;
|
||||
};
|
||||
|
||||
void WorkletNodeEngine::SendErrorToMainThread(
|
||||
AudioNodeTrack* aTrack, const ProcessorErrorDetails& aDetails) {
|
||||
RefPtr<AudioNodeTrack> track = aTrack;
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"WorkletNodeEngine::SendProcessorError",
|
||||
[track = std::move(track), aDetails]() mutable {
|
||||
AudioWorkletNode* node =
|
||||
static_cast<AudioWorkletNode*>(track->Engine()->NodeMainThread());
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
node->DispatchProcessorErrorEvent(aDetails);
|
||||
}));
|
||||
}
|
||||
|
||||
void WorkletNodeEngine::SendProcessorError(AudioNodeTrack* aTrack,
|
||||
JSContext* aCx) {
|
||||
/**
|
||||
* Note that once an exception is thrown, the processor will output silence
|
||||
* throughout its lifetime.
|
||||
*/
|
||||
// Note that once an exception is thrown, the processor will output silence
|
||||
// throughout its lifetime.
|
||||
ReleaseJSResources();
|
||||
// The processor errored out while getting a context, try to tell the node
|
||||
// anyways.
|
||||
if (!aCx) {
|
||||
ProcessorErrorDetails details;
|
||||
details.mMessage.Assign(u"Unknown processor error");
|
||||
SendErrorToMainThread(aTrack, details);
|
||||
return;
|
||||
}
|
||||
|
||||
js::ErrorReport jsReport(aCx);
|
||||
JS::Rooted<JS::Value> exn(aCx);
|
||||
JS::Rooted<JSObject*> exnStack(aCx);
|
||||
if (JS_GetPendingException(aCx, &exn)) {
|
||||
exnStack.set(JS::GetPendingExceptionStack(aCx));
|
||||
JS_ClearPendingException(aCx);
|
||||
if (!jsReport.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
|
||||
ProcessorErrorDetails details;
|
||||
details.mMessage.Assign(u"Unknown processor error");
|
||||
SendErrorToMainThread(aTrack, details);
|
||||
// Set the exception and stack back to have it in the console with a stack
|
||||
// trace.
|
||||
JS::SetPendingExceptionAndStack(aCx, exn, exnStack);
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessorErrorDetails details;
|
||||
|
||||
CopyUTF8toUTF16(mozilla::MakeStringSpan(jsReport.report()->filename),
|
||||
details.mFilename);
|
||||
|
||||
xpc::ErrorReport::ErrorReportToMessageString(jsReport.report(),
|
||||
details.mMessage);
|
||||
details.mLineno = jsReport.report()->lineno;
|
||||
details.mColno = jsReport.report()->column;
|
||||
MOZ_ASSERT(!jsReport.report()->isMuted);
|
||||
|
||||
SendErrorToMainThread(aTrack, details);
|
||||
|
||||
// Set the exception and stack back to have it in the console with a stack
|
||||
// trace.
|
||||
JS::SetPendingExceptionAndStack(aCx, exn, exnStack);
|
||||
} else {
|
||||
NS_WARNING("No exception, but processor errored out?");
|
||||
}
|
||||
}
|
||||
|
||||
void WorkletNodeEngine::ConstructProcessor(
|
||||
|
@ -754,6 +826,21 @@ AudioParamMap* AudioWorkletNode::GetParameters(ErrorResult& aRv) const {
|
|||
return mParameters.get();
|
||||
}
|
||||
|
||||
void AudioWorkletNode::DispatchProcessorErrorEvent(
|
||||
const ProcessorErrorDetails& aDetails) {
|
||||
if (HasListenersFor(nsGkAtoms::onprocessorerror)) {
|
||||
RootedDictionary<ErrorEventInit> init(RootingCx());
|
||||
init.mMessage = aDetails.mMessage;
|
||||
init.mFilename = aDetails.mFilename;
|
||||
init.mLineno = aDetails.mLineno;
|
||||
init.mColno = aDetails.mColno;
|
||||
RefPtr<ErrorEvent> errorEvent = ErrorEvent::Constructor(
|
||||
this, NS_LITERAL_STRING("processorerror"), init);
|
||||
MOZ_ASSERT(errorEvent);
|
||||
DispatchTrustedEvent(errorEvent);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject* AudioWorkletNode::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return AudioWorkletNode_Binding::Wrap(aCx, this, aGivenProto);
|
||||
|
|
|
@ -16,6 +16,7 @@ class AudioParamMap;
|
|||
struct AudioWorkletNodeOptions;
|
||||
class MessagePort;
|
||||
struct NamedAudioParamTimeline;
|
||||
struct ProcessorErrorDetails;
|
||||
|
||||
class AudioWorkletNode : public AudioNode {
|
||||
public:
|
||||
|
@ -40,6 +41,7 @@ class AudioWorkletNode : public AudioNode {
|
|||
uint16_t NumberOfInputs() const override { return mInputCount; }
|
||||
uint16_t NumberOfOutputs() const override { return mOutputCount; }
|
||||
const char* NodeType() const override { return "AudioWorkletNode"; }
|
||||
void DispatchProcessorErrorEvent(const ProcessorErrorDetails& aDetails);
|
||||
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
|
Загрузка…
Ссылка в новой задаче