WebWorkerNote
From MozillaWiki
Dedicated Worker
already_AddRefed<WorkerPrivate>
WorkerPrivate::Constructor(JSContext* aCx,
const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
const nsACString& aSharedWorkerName,
LoadInfo* aLoadInfo, ErrorResult& aRv)
{
WorkerPrivate* parent = NS_IsMainThread() ?
nullptr :
GetCurrentThreadWorkerPrivate();
if (parent) {
parent->AssertIsOnWorkerThread();
} else {
AssertIsOnMainThread();
}
MOZ_ASSERT_IF(aWorkerType != WorkerTypeDedicated,
!aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(aWorkerType == WorkerTypeDedicated,
aSharedWorkerName.IsEmpty());
Maybe<LoadInfo> stackLoadInfo;
if (!aLoadInfo) {
stackLoadInfo.emplace();
nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
aIsChromeWorker, stackLoadInfo.ptr());
if (NS_FAILED(rv)) {
scriptloader::ReportLoadError(aCx, aScriptURL, rv, !parent);
aRv.Throw(rv);
return nullptr;
}
aLoadInfo = stackLoadInfo.ptr();
}
// NB: This has to be done before creating the WorkerPrivate, because it will
// attempt to use static variables that are initialized in the RuntimeService
// constructor.
RuntimeService* runtimeService;
if (!parent) {
runtimeService = RuntimeService::GetOrCreateService();
if (!runtimeService) {
JS_ReportError(aCx, "Failed to create runtime service!");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
}
else {
runtimeService = RuntimeService::GetService();
}
MOZ_ASSERT(runtimeService);
nsRefPtr<WorkerPrivate> worker =
new WorkerPrivate(aCx, parent, aScriptURL, aIsChromeWorker,
aWorkerType, aSharedWorkerName, *aLoadInfo);
if (!runtimeService->RegisterWorker(aCx, worker)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
worker->EnableDebugger();
nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
if (!compiler->Dispatch(aCx)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
worker->mSelfRef = worker;
return worker.forget();
}
NS_IMETHODIMP
WorkerRunnable::Run()
{
bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
mBehavior == WorkerThreadUnchangedBusyCount;
#ifdef DEBUG
MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
if (targetIsWorkerThread) {
mWorkerPrivate->AssertIsOnWorkerThread();
}
else {
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
mWorkerPrivate->AssertIsOnParentThread();
}
#endif
if (IsCanceled() && !mCallingCancelWithinRun) {
return NS_OK;
}
if (targetIsWorkerThread &&
mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
!IsCanceled() && !mCallingCancelWithinRun) {
// Prevent recursion.
mCallingCancelWithinRun = true;
Cancel();
MOZ_ASSERT(mCallingCancelWithinRun);
mCallingCancelWithinRun = false;
MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
return NS_OK;
}
// Track down the appropriate global to use for the AutoJSAPI/AutoEntryScript.
nsCOMPtr<nsIGlobalObject> globalObject;
bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent();
MOZ_ASSERT(isMainThread == NS_IsMainThread());
nsRefPtr<WorkerPrivate> kungFuDeathGrip;
if (targetIsWorkerThread) {
globalObject = mWorkerPrivate->GlobalScope();
}
else {
kungFuDeathGrip = mWorkerPrivate;
if (isMainThread) {
globalObject = static_cast<nsGlobalWindow*>(mWorkerPrivate->GetWindow());
} else {
globalObject = mWorkerPrivate->GetParent()->GlobalScope();
}
}
// We might run script as part of WorkerRun, so we need an AutoEntryScript.
// This is part of the HTML spec for workers at:
// http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker
// If we don't have a globalObject we have to use an AutoJSAPI instead, but
// this is OK as we won't be running script in these circumstances.
// It's important that aes is declared after jsapi, because if WorkerRun
// creates a global then we construct aes before PostRun and we need them to
// be destroyed in the correct order.
mozilla::dom::AutoJSAPI jsapi;
Maybe<mozilla::dom::AutoEntryScript> aes;
JSContext* cx;
if (globalObject) {
aes.emplace(globalObject, isMainThread, isMainThread ? nullptr :
GetCurrentThreadJSContext());
cx = aes->cx();
} else {
jsapi.Init();
cx = jsapi.cx();
}
// If we're not on the worker thread we'll either be in our parent's
// compartment or the null compartment, so we need to enter our own.
Maybe<JSAutoCompartment> ac;
if (!targetIsWorkerThread && mWorkerPrivate->GetWrapper()) {
ac.emplace(cx, mWorkerPrivate->GetWrapper());
}
bool result = WorkerRun(cx, mWorkerPrivate);
// In the case of CompileScriptRunnnable, WorkerRun above can cause us to
// lazily create a global, so we construct aes here before calling PostRun.
if (targetIsWorkerThread && !aes && mWorkerPrivate->GlobalScope()) {
aes.emplace(mWorkerPrivate->GlobalScope(), false, GetCurrentThreadJSContext());
cx = aes->cx();
}
PostRun(cx, mWorkerPrivate, result);
return result ? NS_OK : NS_ERROR_FAILURE;
}
class CompileScriptRunnable MOZ_FINAL : public WorkerRunnable
{
public:
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
{ }
private:
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
{
JS::Rooted<JSObject*> global(aCx,
aWorkerPrivate->CreateGlobalScope(aCx));
if (!global) {
NS_WARNING("Failed to make global!");
return false;
}
JSAutoCompartment ac(aCx, global);
bool result = scriptloader::LoadWorkerScript(aCx);
if (result) {
aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
}
return result;
}
};
JSObject*
WorkerPrivate::CreateGlobalScope(JSContext* aCx)
{
AssertIsOnWorkerThread();
nsRefPtr<WorkerGlobalScope> globalScope;
if (IsSharedWorker()) {
globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName());
} else if (IsServiceWorker()) {
globalScope = new ServiceWorkerGlobalScope(this, SharedWorkerName());
} else {
globalScope = new DedicatedWorkerGlobalScope(this);
}
JS::Rooted<JSObject*> global(aCx, globalScope->WrapGlobalObject(aCx));
NS_ENSURE_TRUE(global, nullptr);
JSAutoCompartment ac(aCx, global);
if (!RegisterBindings(aCx, global)) {
return nullptr;
}
mScope = globalScope.forget();
JS_FireOnNewGlobalObject(aCx, global);
return global;
}
AudioProcessingEvent in WebAudio
class AudioProcessingEvent : public Event
{
public:
AudioProcessingEvent(ScriptProcessorNode* aOwner,
nsPresContext* aPresContext,
WidgetEvent* aEvent);
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_TO_EVENT
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioProcessingEvent, Event)
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE;
void InitEvent(AudioBuffer* aInputBuffer,
uint32_t aNumberOfInputChannels,
double aPlaybackTime)
{
InitEvent(NS_LITERAL_STRING("audioprocess"), false, false);
mInputBuffer = aInputBuffer;
mNumberOfInputChannels = aNumberOfInputChannels;
mPlaybackTime = aPlaybackTime;
}
double PlaybackTime() const
{
return mPlaybackTime;
}
AudioBuffer* GetInputBuffer(ErrorResult& aRv)
{
if (!mInputBuffer) {
mInputBuffer = LazilyCreateBuffer(mNumberOfInputChannels, aRv);
}
return mInputBuffer;
}
AudioBuffer* GetOutputBuffer(ErrorResult& aRv)
{
if (!mOutputBuffer) {
mOutputBuffer = LazilyCreateBuffer(mNode->NumberOfOutputChannels(), aRv);
}
return mOutputBuffer;
}
bool HasOutputBuffer() const
{
return !!mOutputBuffer;
}
protected:
virtual ~AudioProcessingEvent();
private:
already_AddRefed<AudioBuffer>
LazilyCreateBuffer(uint32_t aNumberOfChannels, ErrorResult& rv);
private:
double mPlaybackTime;
nsRefPtr<AudioBuffer> mInputBuffer;
nsRefPtr<AudioBuffer> mOutputBuffer;
nsRefPtr<ScriptProcessorNode> mNode;
uint32_t mNumberOfInputChannels;
};
// This class manages a queue of output buffers shared between
// the main thread and the Media Stream Graph thread.
class SharedBuffers
{
private:
class OutputQueue
{
public:
explicit OutputQueue(const char* aName)
: mMutex(aName)
{}
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
mMutex.AssertCurrentThreadOwns();
size_t amount = 0;
for (size_t i = 0; i < mBufferList.size(); i++) {
amount += mBufferList[i].SizeOfExcludingThis(aMallocSizeOf, false);
}
return amount;
}
Mutex& Lock() const { return const_cast<OutputQueue*>(this)->mMutex; }
size_t ReadyToConsume() const
{
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(!NS_IsMainThread());
return mBufferList.size();
}
// Produce one buffer
AudioChunk& Produce()
{
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(NS_IsMainThread());
mBufferList.push_back(AudioChunk());
return mBufferList.back();
}
// Consumes one buffer.
AudioChunk Consume()
{
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(ReadyToConsume() > 0);
AudioChunk front = mBufferList.front();
mBufferList.pop_front();
return front;
}
// Empties the buffer queue.
void Clear()
{
mMutex.AssertCurrentThreadOwns();
mBufferList.clear();
}
private:
typedef std::deque<AudioChunk> BufferList;
// Synchronizes access to mBufferList. Note that it's the responsibility
// of the callers to perform the required locking, and we assert that every
// time we access mBufferList.
Mutex mMutex;
// The list representing the queue.
BufferList mBufferList;
};
public:
explicit SharedBuffers(float aSampleRate)
: mOutputQueue("SharedBuffers::outputQueue")
, mDelaySoFar(STREAM_TIME_MAX)
, mSampleRate(aSampleRate)
, mLatency(0.0)
, mDroppingBuffers(false)
{
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = aMallocSizeOf(this);
{
MutexAutoLock lock(mOutputQueue.Lock());
amount += mOutputQueue.SizeOfExcludingThis(aMallocSizeOf);
}
return amount;
}
// main thread
void FinishProducingOutputBuffer(ThreadSharedFloatArrayBufferList* aBuffer,
uint32_t aBufferSize)
{
MOZ_ASSERT(NS_IsMainThread());
TimeStamp now = TimeStamp::Now();
if (mLastEventTime.IsNull()) {
mLastEventTime = now;
} else {
// When the main thread is blocked, and all the event are processed in a
// burst after the main thread unblocks, the |(now - mLastEventTime)|
// interval will be very short. |latency - bufferDuration| will be
// negative, effectively moving back mLatency to a smaller and smaller
// value, until it crosses zero, at which point we stop dropping buffers
// and resume normal operation. This does not work if at the same time,
// the MSG thread was also slowed down, so if the latency on the MSG
// thread is normal, and we are still dropping buffers, and mLatency is
// still more than twice the duration of a buffer, we reset it and stop
// dropping buffers.
float latency = (now - mLastEventTime).ToSeconds();
float bufferDuration = aBufferSize / mSampleRate;
mLatency += latency - bufferDuration;
mLastEventTime = now;
if (mLatency > MAX_LATENCY_S ||
(mDroppingBuffers && mLatency > 0.0 &&
fabs(latency - bufferDuration) < bufferDuration)) {
mDroppingBuffers = true;
return;
} else {
if (mDroppingBuffers) {
mLatency = 0;
}
mDroppingBuffers = false;
}
}
MutexAutoLock lock(mOutputQueue.Lock());
for (uint32_t offset = 0; offset < aBufferSize; offset += WEBAUDIO_BLOCK_SIZE) {
AudioChunk& chunk = mOutputQueue.Produce();
if (aBuffer) {
chunk.mDuration = WEBAUDIO_BLOCK_SIZE;
chunk.mBuffer = aBuffer;
chunk.mChannelData.SetLength(aBuffer->GetChannels());
for (uint32_t i = 0; i < aBuffer->GetChannels(); ++i) {
chunk.mChannelData[i] = aBuffer->GetData(i) + offset;
}
chunk.mVolume = 1.0f;
chunk.mBufferFormat = AUDIO_FORMAT_FLOAT32;
} else {
chunk.SetNull(WEBAUDIO_BLOCK_SIZE);
}
}
}
// graph thread
AudioChunk GetOutputBuffer()
{
MOZ_ASSERT(!NS_IsMainThread());
AudioChunk buffer;
{
MutexAutoLock lock(mOutputQueue.Lock());
if (mOutputQueue.ReadyToConsume() > 0) {
if (mDelaySoFar == STREAM_TIME_MAX) {
mDelaySoFar = 0;
}
buffer = mOutputQueue.Consume();
} else {
// If we're out of buffers to consume, just output silence
buffer.SetNull(WEBAUDIO_BLOCK_SIZE);
if (mDelaySoFar != STREAM_TIME_MAX) {
// Remember the delay that we just hit
mDelaySoFar += WEBAUDIO_BLOCK_SIZE;
}
}
}
return buffer;
}
StreamTime DelaySoFar() const
{
MOZ_ASSERT(!NS_IsMainThread());
return mDelaySoFar == STREAM_TIME_MAX ? 0 : mDelaySoFar;
}
void Reset()
{
MOZ_ASSERT(!NS_IsMainThread());
mDelaySoFar = STREAM_TIME_MAX;
mLatency = 0.0f;
{
MutexAutoLock lock(mOutputQueue.Lock());
mOutputQueue.Clear();
}
mLastEventTime = TimeStamp();
}
private:
OutputQueue mOutputQueue;
// How much delay we've seen so far. This measures the amount of delay
// caused by the main thread lagging behind in producing output buffers.
// STREAM_TIME_MAX means that we have not received our first buffer yet.
StreamTime mDelaySoFar;
// The samplerate of the context.
float mSampleRate;
// This is the latency caused by the buffering. If this grows too high, we
// will drop buffers until it is acceptable.
float mLatency;
// This is the time at which we last produced a buffer, to detect if the main
// thread has been blocked.
TimeStamp mLastEventTime;
// True if we should be dropping buffers.
bool mDroppingBuffers;
};
void ScriptProcessorNodeEngine::SendBuffersToMainThread(AudioNodeStream* aStream)
{
MOZ_ASSERT(!NS_IsMainThread());
// we now have a full input buffer ready to be sent to the main thread.
StreamTime playbackTick = mSource->GetCurrentPosition();
// Add the duration of the current sample
playbackTick += WEBAUDIO_BLOCK_SIZE;
// Add the delay caused by the main thread
playbackTick += mSharedBuffers->DelaySoFar();
// Compute the playback time in the coordinate system of the destination
double playbackTime =
mSource->DestinationTimeFromTicks(mDestination, playbackTick);
class Command : public nsRunnable
{
public:
Command(AudioNodeStream* aStream,
InputChannels& aInputChannels,
double aPlaybackTime,
bool aNullInput)
: mStream(aStream)
, mPlaybackTime(aPlaybackTime)
, mNullInput(aNullInput)
{
mInputChannels.SetLength(aInputChannels.Length());
if (!aNullInput) {
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
mInputChannels[i] = aInputChannels[i].forget();
}
}
}
NS_IMETHODIMP Run()
{
nsRefPtr<ScriptProcessorNode> node = static_cast<ScriptProcessorNode*>
(mStream->Engine()->NodeMainThread());
if (!node) {
return NS_OK;
}
AudioContext* context = node->Context();
if (!context) {
return NS_OK;
}
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(node->GetOwner()))) {
return NS_OK;
}
JSContext* cx = jsapi.cx();
// Create the input buffer
nsRefPtr<AudioBuffer> inputBuffer;
if (!mNullInput) {
ErrorResult rv;
inputBuffer =
AudioBuffer::Create(context, mInputChannels.Length(),
node->BufferSize(),
context->SampleRate(), cx, rv);
if (rv.Failed()) {
return NS_OK;
}
// Put the channel data inside it
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
inputBuffer->SetRawChannelContents(i, mInputChannels[i]);
}
}
// Ask content to produce data in the output buffer
// Note that we always avoid creating the output buffer here, and we try to
// avoid creating the input buffer as well. The AudioProcessingEvent class
// knows how to lazily create them if needed once the script tries to access
// them. Otherwise, we may be able to get away without creating them!
nsRefPtr<AudioProcessingEvent> event = new AudioProcessingEvent(node, nullptr, nullptr);
event->InitEvent(inputBuffer,
mInputChannels.Length(),
context->StreamTimeToDOMTime(mPlaybackTime));
node->DispatchTrustedEvent(event);
// Steal the output buffers if they have been set.
// Don't create a buffer if it hasn't been used to return output;
// FinishProducingOutputBuffer() will optimize output = null.
// GetThreadSharedChannelsForRate() may also return null after OOM.
nsRefPtr<ThreadSharedFloatArrayBufferList> output;
if (event->HasOutputBuffer()) {
ErrorResult rv;
AudioBuffer* buffer = event->GetOutputBuffer(rv);
// HasOutputBuffer() returning true means that GetOutputBuffer()
// will not fail.
MOZ_ASSERT(!rv.Failed());
output = buffer->GetThreadSharedChannelsForRate(cx);
}
// Append it to our output buffer queue
node->GetSharedBuffers()->FinishProducingOutputBuffer(output, node->BufferSize());
return NS_OK;
}
private:
nsRefPtr<AudioNodeStream> mStream;
InputChannels mInputChannels;
double mPlaybackTime;
bool mNullInput;
};
NS_DispatchToMainThread(new Command(aStream, mInputChannels,
playbackTime,
!mSeenNonSilenceInput));
}
InstallEvent in ServiceWorker
void
ServiceWorkerManager::Install(ServiceWorkerRegistrationInfo* aRegistration,
ServiceWorkerInfo* aServiceWorkerInfo)
{
AssertIsOnMainThread();
aRegistration->mInstallingWorker = aServiceWorkerInfo;
MOZ_ASSERT(aRegistration->mInstallingWorker);
InvalidateServiceWorkerRegistrationWorker(aRegistration,
WhichServiceWorker::INSTALLING_WORKER);
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(aRegistration));
nsRefPtr<ServiceWorker> serviceWorker;
nsresult rv =
CreateServiceWorker(aServiceWorkerInfo->GetScriptSpec(),
aRegistration->mScope,
getter_AddRefs(serviceWorker));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRegistration->mInstallingWorker = nullptr;
// We don't need to invalidate here since the upper one will have done it.
return;
}
nsRefPtr<InstallEventRunnable> r =
new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
AutoSafeJSContext cx;
r->Dispatch(cx);
// When this function exits, although we've lost references to the ServiceWorker,
// which means the underlying WorkerPrivate has no references, the worker
// will stay alive due to the modified busy count until the install event has
// been dispatched.
// NOTE: The worker spec does not require Promises to keep a worker alive, so
// the waitUntil() construct by itself will not keep a worker alive beyond
// the event dispatch. On the other hand, networking, IDB and so on do keep
// the worker alive, so the waitUntil() is only relevant if the Promise is
// gated on those actions. I (nsm) am not sure if it is worth requiring
// a special spec mention saying the install event should keep the worker
// alive indefinitely purely on the basis of calling waitUntil(), since
// a wait is likely to be required only when performing networking or storage
// transactions in the first place.
FireEventOnServiceWorkerRegistrations(aRegistration,
NS_LITERAL_STRING("updatefound"));
}
/*
* Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
* since it fires the event. This is ok since there can't be nested
* ServiceWorkers, so the parent thread -> worker thread requirement for
* runnables is satisfied.
*/
class InstallEventRunnable MOZ_FINAL : public WorkerRunnable
{
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
nsCString mScope;
public:
InstallEventRunnable(
WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
mRegistration(aRegistration),
mScope(aRegistration.get()->mScope) // copied for access on worker thread.
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aWorkerPrivate);
return DispatchInstallEvent(aCx, aWorkerPrivate);
}
private:
bool
DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
InstallEventInit init;
init.mBubbles = false;
init.mCancelable = true;
// FIXME(nsm): Bug 982787 pass previous active worker.
nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
nsRefPtr<InstallEvent> event =
InstallEvent::Constructor(target, NS_LITERAL_STRING("install"), init);
event->SetTrusted(true);
nsRefPtr<Promise> waitUntilPromise;
nsresult rv = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
if (NS_SUCCEEDED(rv)) {
waitUntilPromise = event->GetPromise();
if (!waitUntilPromise) {
ErrorResult rv;
waitUntilPromise =
Promise::Resolve(sgo,
aCx, JS::UndefinedHandleValue, rv);
}
} else {
ErrorResult rv;
// Continue with a canceled install.
waitUntilPromise = Promise::Reject(sgo, aCx,
JS::UndefinedHandleValue, rv);
}
nsRefPtr<FinishInstallHandler> handler =
new FinishInstallHandler(mRegistration);
waitUntilPromise->AppendNativeHandler(handler);
return true;
}
};
class InstallEvent MOZ_FINAL : public InstallPhaseEvent
{
// FIXME(nsm): Bug 982787 will allow actually populating this.
nsRefPtr<ServiceWorker> mActiveWorker;
protected:
explicit InstallEvent(mozilla::dom::EventTarget* aOwner);
~InstallEvent() {}
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallEvent, InstallPhaseEvent)
NS_FORWARD_TO_EVENT
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
{
return mozilla::dom::InstallEventBinding_workers::Wrap(aCx, this);
}
static already_AddRefed<InstallEvent>
Constructor(mozilla::dom::EventTarget* aOwner,
const nsAString& aType,
const InstallEventInit& aOptions)
{
nsRefPtr<InstallEvent> e = new InstallEvent(aOwner);
bool trusted = e->Init(aOwner);
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
e->SetTrusted(trusted);
e->mActiveWorker = aOptions.mActiveWorker;
return e.forget();
}
static already_AddRefed<InstallEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const InstallEventInit& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
return Constructor(owner, aType, aOptions);
}
already_AddRefed<ServiceWorker>
GetActiveWorker() const
{
nsRefPtr<ServiceWorker> sw = mActiveWorker;
return sw.forget();
}
void
Replace()
{
// FIXME(nsm): Unspecced. Bug 982711
NS_WARNING("Not Implemented");
};
};
class InstallPhaseEvent : public Event
{
nsRefPtr<Promise> mPromise;
protected:
explicit InstallPhaseEvent(mozilla::dom::EventTarget* aOwner);
~InstallPhaseEvent() {}
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallPhaseEvent, Event)
NS_FORWARD_TO_EVENT
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
{
return mozilla::dom::InstallPhaseEventBinding_workers::Wrap(aCx, this);
}
static already_AddRefed<InstallPhaseEvent>
Constructor(mozilla::dom::EventTarget* aOwner,
const nsAString& aType,
const EventInit& aOptions)
{
nsRefPtr<InstallPhaseEvent> e = new InstallPhaseEvent(aOwner);
bool trusted = e->Init(aOwner);
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
e->SetTrusted(trusted);
return e.forget();
}
static already_AddRefed<InstallPhaseEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const EventInit& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
return Constructor(target, aType, aOptions);
}
void
WaitUntil(Promise& aPromise);
already_AddRefed<Promise>
GetPromise() const
{
nsRefPtr<Promise> p = mPromise;
return p.forget();
}
};