diff -r 000000000000 -r 6474c204b198 dom/workers/WorkerPrivate.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/workers/WorkerPrivate.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1239 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_workers_workerprivate_h__ +#define mozilla_dom_workers_workerprivate_h__ + +#include "Workers.h" + +#include "nsIContentSecurityPolicy.h" +#include "nsPIDOMWindow.h" + +#include "mozilla/CondVar.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsRefPtrHashtable.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "StructuredCloneTags.h" + +#include "Queue.h" +#include "WorkerFeature.h" + +class JSAutoStructuredCloneBuffer; +class nsIChannel; +class nsIDocument; +class nsIEventTarget; +class nsIPrincipal; +class nsIScriptContext; +class nsIThread; +class nsIThreadInternal; +class nsITimer; +class nsIURI; + +namespace JS { +class RuntimeStats; +} + +namespace mozilla { +namespace dom { +class Function; +} +} + +#ifdef DEBUG +struct PRThread; +#endif + +BEGIN_WORKERS_NAMESPACE + +class AutoSyncLoopHolder; +class MessagePort; +class SharedWorker; +class WorkerControlRunnable; +class WorkerGlobalScope; +class WorkerPrivate; +class WorkerRunnable; + +// SharedMutex is a small wrapper around an (internal) reference-counted Mutex +// object. It exists to avoid changing a lot of code to use Mutex* instead of +// Mutex&. +class SharedMutex +{ + typedef mozilla::Mutex Mutex; + + class RefCountedMutex MOZ_FINAL : public Mutex + { + public: + RefCountedMutex(const char* aName) + : Mutex(aName) + { } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMutex) + + private: + ~RefCountedMutex() + { } + }; + + nsRefPtr mMutex; + +public: + SharedMutex(const char* aName) + : mMutex(new RefCountedMutex(aName)) + { } + + SharedMutex(SharedMutex& aOther) + : mMutex(aOther.mMutex) + { } + + operator Mutex&() + { + return *mMutex; + } + + operator const Mutex&() const + { + return *mMutex; + } + + void + AssertCurrentThreadOwns() const + { + mMutex->AssertCurrentThreadOwns(); + } +}; + +template +class WorkerPrivateParent : public DOMEventTargetHelper +{ + class SynchronizeAndResumeRunnable; + +protected: + class EventTarget; + friend class EventTarget; + +public: + struct LocationInfo + { + nsCString mHref; + nsCString mProtocol; + nsCString mHost; + nsCString mHostname; + nsCString mPort; + nsCString mPathname; + nsCString mSearch; + nsCString mHash; + nsString mOrigin; + }; + + struct LoadInfo + { + // All of these should be released in ForgetMainThreadObjects. + nsCOMPtr mBaseURI; + nsCOMPtr mResolvedScriptURI; + nsCOMPtr mPrincipal; + nsCOMPtr mScriptContext; + nsCOMPtr mWindow; + nsCOMPtr mCSP; + nsCOMPtr mChannel; + + nsCString mDomain; + + bool mEvalAllowed; + bool mReportCSPViolations; + bool mXHRParamsAllowed; + bool mPrincipalIsSystem; + bool mIsInPrivilegedApp; + bool mIsInCertifiedApp; + + LoadInfo() + : mEvalAllowed(false), mReportCSPViolations(false), + mXHRParamsAllowed(false), mPrincipalIsSystem(false), + mIsInPrivilegedApp(false), mIsInCertifiedApp(false) + { } + + void + StealFrom(LoadInfo& aOther) + { + MOZ_ASSERT(!mBaseURI); + aOther.mBaseURI.swap(mBaseURI); + + MOZ_ASSERT(!mResolvedScriptURI); + aOther.mResolvedScriptURI.swap(mResolvedScriptURI); + + MOZ_ASSERT(!mPrincipal); + aOther.mPrincipal.swap(mPrincipal); + + MOZ_ASSERT(!mScriptContext); + aOther.mScriptContext.swap(mScriptContext); + + MOZ_ASSERT(!mWindow); + aOther.mWindow.swap(mWindow); + + MOZ_ASSERT(!mCSP); + aOther.mCSP.swap(mCSP); + + MOZ_ASSERT(!mChannel); + aOther.mChannel.swap(mChannel); + + mDomain = aOther.mDomain; + mEvalAllowed = aOther.mEvalAllowed; + mReportCSPViolations = aOther.mReportCSPViolations; + mXHRParamsAllowed = aOther.mXHRParamsAllowed; + mPrincipalIsSystem = aOther.mPrincipalIsSystem; + mIsInPrivilegedApp = aOther.mIsInPrivilegedApp; + mIsInCertifiedApp = aOther.mIsInCertifiedApp; + } + }; + + enum WorkerType + { + WorkerTypeDedicated, + WorkerTypeShared + }; + +protected: + typedef mozilla::ErrorResult ErrorResult; + + SharedMutex mMutex; + mozilla::CondVar mCondVar; + mozilla::CondVar mMemoryReportCondVar; + + // Protected by mMutex. + nsRefPtr mEventTarget; + nsTArray> mPreStartRunnables; + +private: + WorkerPrivate* mParent; + nsString mScriptURL; + nsCString mSharedWorkerName; + LocationInfo mLocationInfo; + // The lifetime of these objects within LoadInfo is managed explicitly; + // they do not need to be cycle collected. + LoadInfo mLoadInfo; + + // Only used for top level workers. + nsTArray> mQueuedRunnables; + nsRevocableEventPtr mSynchronizeRunnable; + + // Only for ChromeWorkers without window and only touched on the main thread. + nsTArray mHostObjectURIs; + + // Protected by mMutex. + JSSettings mJSSettings; + + // Only touched on the parent thread (currently this is always the main + // thread as SharedWorkers are always top-level). + nsDataHashtable mSharedWorkers; + + uint64_t mBusyCount; + uint64_t mMessagePortSerial; + Status mParentStatus; + bool mParentSuspended; + bool mIsChromeWorker; + bool mMainThreadObjectsForgotten; + WorkerType mWorkerType; + TimeStamp mCreationTimeStamp; + +protected: + // The worker is owned by its thread, which is represented here. This is set + // in Construct() and emptied by WorkerFinishedRunnable, and conditionally + // traversed by the cycle collector if the busy count is zero. + nsRefPtr mSelfRef; + + WorkerPrivateParent(JSContext* aCx, WorkerPrivate* aParent, + const nsAString& aScriptURL, bool aIsChromeWorker, + WorkerType aWorkerType, + const nsACString& aSharedWorkerName, + LoadInfo& aLoadInfo); + + ~WorkerPrivateParent(); + +private: + Derived* + ParentAsWorkerPrivate() const + { + return static_cast(const_cast(this)); + } + + // aCx is null when called from the finalizer + bool + NotifyPrivate(JSContext* aCx, Status aStatus); + + // aCx is null when called from the finalizer + bool + TerminatePrivate(JSContext* aCx) + { + return NotifyPrivate(aCx, Terminating); + } + + void + PostMessageInternal(JSContext* aCx, JS::Handle aMessage, + const Optional >& aTransferable, + bool aToMessagePort, uint64_t aMessagePortSerial, + ErrorResult& aRv); + + nsresult + DispatchPrivate(WorkerRunnable* aRunnable, nsIEventTarget* aSyncLoopTarget); + +public: + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent, + DOMEventTargetHelper) + + void + ClearSelfRef() + { + AssertIsOnParentThread(); + MOZ_ASSERT(mSelfRef); + mSelfRef = nullptr; + } + + nsresult + Dispatch(WorkerRunnable* aRunnable) + { + return DispatchPrivate(aRunnable, nullptr); + } + + nsresult + DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable); + + already_AddRefed + MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable); + + already_AddRefed + GetEventTarget(); + + // May be called on any thread... + bool + Start(); + + // Called on the parent thread. + bool + Notify(JSContext* aCx, Status aStatus) + { + return NotifyPrivate(aCx, aStatus); + } + + bool + Cancel(JSContext* aCx) + { + return Notify(aCx, Canceling); + } + + bool + Kill(JSContext* aCx) + { + return Notify(aCx, Killing); + } + + bool + Suspend(JSContext* aCx, nsPIDOMWindow* aWindow); + + bool + Resume(JSContext* aCx, nsPIDOMWindow* aWindow); + + bool + SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow, + nsIScriptContext* aScriptContext); + + bool + Terminate(JSContext* aCx) + { + AssertIsOnParentThread(); + return TerminatePrivate(aCx); + } + + bool + Close(JSContext* aCx); + + bool + ModifyBusyCount(JSContext* aCx, bool aIncrease); + + void + ForgetMainThreadObjects(nsTArray >& aDoomed); + + void + PostMessage(JSContext* aCx, JS::Handle aMessage, + const Optional >& aTransferable, + ErrorResult& aRv) + { + PostMessageInternal(aCx, aMessage, aTransferable, false, 0, aRv); + } + + void + PostMessageToMessagePort(JSContext* aCx, + uint64_t aMessagePortSerial, + JS::Handle aMessage, + const Optional >& aTransferable, + ErrorResult& aRv); + + bool + DispatchMessageEventToMessagePort( + JSContext* aCx, + uint64_t aMessagePortSerial, + JSAutoStructuredCloneBuffer&& aBuffer, + nsTArray>& aClonedObjects); + + uint64_t + GetInnerWindowId(); + + void + UpdateRuntimeAndContextOptions(JSContext* aCx, + const JS::RuntimeOptions& aRuntimeOptions, + const JS::ContextOptions& aContentCxOptions, + const JS::ContextOptions& aChromeCxOptions); + + void + UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue); + + void + UpdateJSWorkerMemoryParameter(JSContext* aCx, JSGCParamKey key, + uint32_t value); + +#ifdef JS_GC_ZEAL + void + UpdateGCZeal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency); +#endif + + void + GarbageCollect(JSContext* aCx, bool aShrinking); + + void + CycleCollect(JSContext* aCx, bool aDummy); + + void + OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline); + + bool + RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker); + + void + UnregisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker); + + void + BroadcastErrorToSharedWorkers(JSContext* aCx, + const nsAString& aMessage, + const nsAString& aFilename, + const nsAString& aLine, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aFlags); + + void + WorkerScriptLoaded(); + + void + QueueRunnable(nsIRunnable* aRunnable) + { + AssertIsOnMainThread(); + mQueuedRunnables.AppendElement(aRunnable); + } + + WorkerPrivate* + GetParent() const + { + return mParent; + } + + bool + IsSuspended() const + { + AssertIsOnParentThread(); + return mParentSuspended; + } + + bool + IsAcceptingEvents() + { + AssertIsOnParentThread(); + + MutexAutoLock lock(mMutex); + return mParentStatus < Terminating; + } + + Status + ParentStatus() const + { + mMutex.AssertCurrentThreadOwns(); + return mParentStatus; + } + + JSContext* + ParentJSContext() const; + + nsIScriptContext* + GetScriptContext() const + { + AssertIsOnMainThread(); + return mLoadInfo.mScriptContext; + } + + const nsString& + ScriptURL() const + { + return mScriptURL; + } + + const nsCString& + Domain() const + { + return mLoadInfo.mDomain; + } + + nsIURI* + GetBaseURI() const + { + AssertIsOnMainThread(); + return mLoadInfo.mBaseURI; + } + + void + SetBaseURI(nsIURI* aBaseURI); + + nsIURI* + GetResolvedScriptURI() const + { + AssertIsOnMainThread(); + return mLoadInfo.mResolvedScriptURI; + } + + TimeStamp CreationTimeStamp() const + { + return mCreationTimeStamp; + } + + nsIPrincipal* + GetPrincipal() const + { + AssertIsOnMainThread(); + return mLoadInfo.mPrincipal; + } + + // This method allows the principal to be retrieved off the main thread. + // Principals are main-thread objects so the caller must ensure that all + // access occurs on the main thread. + nsIPrincipal* + GetPrincipalDontAssertMainThread() const + { + return mLoadInfo.mPrincipal; + } + + void + SetPrincipal(nsIPrincipal* aPrincipal); + + bool + UsesSystemPrincipal() const + { + return mLoadInfo.mPrincipalIsSystem; + } + + bool + IsInPrivilegedApp() const + { + return mLoadInfo.mIsInPrivilegedApp; + } + + bool + IsInCertifiedApp() const + { + return mLoadInfo.mIsInCertifiedApp; + } + + already_AddRefed + ForgetWorkerChannel() + { + AssertIsOnMainThread(); + return mLoadInfo.mChannel.forget(); + } + + nsIDocument* + GetDocument() const + { + AssertIsOnMainThread(); + return mLoadInfo.mWindow ? mLoadInfo.mWindow->GetExtantDoc() : nullptr; + } + + nsPIDOMWindow* + GetWindow() + { + AssertIsOnMainThread(); + return mLoadInfo.mWindow; + } + + nsIContentSecurityPolicy* + GetCSP() const + { + AssertIsOnMainThread(); + return mLoadInfo.mCSP; + } + + void + SetCSP(nsIContentSecurityPolicy* aCSP) + { + AssertIsOnMainThread(); + mLoadInfo.mCSP = aCSP; + } + + bool + IsEvalAllowed() const + { + return mLoadInfo.mEvalAllowed; + } + + void + SetEvalAllowed(bool aEvalAllowed) + { + mLoadInfo.mEvalAllowed = aEvalAllowed; + } + + bool + GetReportCSPViolations() const + { + return mLoadInfo.mReportCSPViolations; + } + + bool + XHRParamsAllowed() const + { + return mLoadInfo.mXHRParamsAllowed; + } + + void + SetXHRParamsAllowed(bool aAllowed) + { + mLoadInfo.mXHRParamsAllowed = aAllowed; + } + + LocationInfo& + GetLocationInfo() + { + return mLocationInfo; + } + + void + CopyJSSettings(JSSettings& aSettings) + { + mozilla::MutexAutoLock lock(mMutex); + aSettings = mJSSettings; + } + + void + CopyJSCompartmentOptions(JS::CompartmentOptions& aOptions) + { + mozilla::MutexAutoLock lock(mMutex); + aOptions = IsChromeWorker() ? mJSSettings.chrome.compartmentOptions + : mJSSettings.content.compartmentOptions; + } + + // The ability to be a chrome worker is orthogonal to the type of + // worker [Dedicated|Shared]. + bool + IsChromeWorker() const + { + return mIsChromeWorker; + } + + bool + IsDedicatedWorker() const + { + return mWorkerType == WorkerTypeDedicated; + } + + bool + IsSharedWorker() const + { + return mWorkerType == WorkerTypeShared; + } + + const nsCString& + SharedWorkerName() const + { + return mSharedWorkerName; + } + + uint64_t + NextMessagePortSerial() + { + AssertIsOnMainThread(); + return mMessagePortSerial++; + } + + void + GetAllSharedWorkers(nsTArray>& aSharedWorkers); + + void + CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow); + + void + RegisterHostObjectURI(const nsACString& aURI); + + void + UnregisterHostObjectURI(const nsACString& aURI); + + void + StealHostObjectURIs(nsTArray& aArray); + + IMPL_EVENT_HANDLER(message) + IMPL_EVENT_HANDLER(error) + +#ifdef DEBUG + void + AssertIsOnParentThread() const; + + void + AssertInnerWindowIsCorrect() const; +#else + void + AssertIsOnParentThread() const + { } + + void + AssertInnerWindowIsCorrect() const + { } +#endif +}; + +class WorkerPrivate : public WorkerPrivateParent +{ + friend class WorkerPrivateParent; + typedef WorkerPrivateParent ParentType; + friend class AutoSyncLoopHolder; + + struct TimeoutInfo; + + class MemoryReporter; + friend class MemoryReporter; + + enum GCTimerMode + { + PeriodicTimer = 0, + IdleTimer, + NoTimer + }; + + Queue mControlQueue; + + // Touched on multiple threads, protected with mMutex. + JSContext* mJSContext; + nsRefPtr mCrossThreadDispatcher; + nsTArray> mUndispatchedRunnablesForSyncLoop; + nsCOMPtr mThread; + + // Things touched on worker thread only. + nsRefPtr mScope; + nsTArray mChildWorkers; + nsTArray mFeatures; + nsTArray> mTimeouts; + + struct SyncLoopInfo + { + SyncLoopInfo(EventTarget* aEventTarget); + + nsRefPtr mEventTarget; + bool mCompleted; + bool mResult; +#ifdef DEBUG + bool mHasRun; +#endif + }; + + // This is only modified on the worker thread, but in DEBUG builds + // AssertValidSyncLoop function iterates it on other threads. Therefore + // modifications are done with mMutex held *only* in DEBUG builds. + nsTArray> mSyncLoopStack; + + nsCOMPtr mTimer; + + nsCOMPtr mGCTimer; + nsCOMPtr mPeriodicGCTimerTarget; + nsCOMPtr mIdleGCTimerTarget; + + nsRefPtr mMemoryReporter; + + nsRefPtrHashtable mWorkerPorts; + + TimeStamp mKillTime; + uint32_t mErrorHandlerRecursionCount; + uint32_t mNextTimeoutId; + Status mStatus; + bool mSuspended; + bool mTimerRunning; + bool mRunningExpiredTimeouts; + bool mCloseHandlerStarted; + bool mCloseHandlerFinished; + bool mMemoryReporterRunning; + bool mBlockedForMemoryReporter; + bool mCancelAllPendingRunnables; + bool mPeriodicGCTimerRunning; + bool mIdleGCTimerRunning; + +#ifdef DEBUG + PRThread* mPRThread; +#endif + + bool mPreferences[WORKERPREF_COUNT]; + bool mOnLine; + +protected: + ~WorkerPrivate(); + +public: + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL, + ErrorResult& aRv); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL, + bool aIsChromeWorker, WorkerType aWorkerType, + const nsACString& aSharedWorkerName, + LoadInfo* aLoadInfo, ErrorResult& aRv); + + static bool + WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */); + + static nsresult + GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent, + const nsAString& aScriptURL, bool aIsChromeWorker, + LoadInfo* aLoadInfo); + + void + DoRunLoop(JSContext* aCx); + + bool + InterruptCallback(JSContext* aCx); + + nsresult + IsOnCurrentThread(bool* aIsOnCurrentThread); + + bool + CloseInternal(JSContext* aCx) + { + AssertIsOnWorkerThread(); + return NotifyInternal(aCx, Closing); + } + + bool + SuspendInternal(JSContext* aCx); + + bool + ResumeInternal(JSContext* aCx); + + void + TraceTimeouts(const TraceCallbacks& aCallbacks, void* aClosure) const; + + bool + ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease); + + bool + AddChildWorker(JSContext* aCx, ParentType* aChildWorker); + + void + RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker); + + bool + AddFeature(JSContext* aCx, WorkerFeature* aFeature); + + void + RemoveFeature(JSContext* aCx, WorkerFeature* aFeature); + + void + NotifyFeatures(JSContext* aCx, Status aStatus); + + bool + HasActiveFeatures() + { + return !(mChildWorkers.IsEmpty() && mTimeouts.IsEmpty() && + mFeatures.IsEmpty()); + } + + void + PostMessageToParent(JSContext* aCx, + JS::Handle aMessage, + const Optional>& aTransferable, + ErrorResult& aRv) + { + PostMessageToParentInternal(aCx, aMessage, aTransferable, false, 0, aRv); + } + + void + PostMessageToParentMessagePort( + JSContext* aCx, + uint64_t aMessagePortSerial, + JS::Handle aMessage, + const Optional>& aTransferable, + ErrorResult& aRv); + + bool + NotifyInternal(JSContext* aCx, Status aStatus); + + void + ReportError(JSContext* aCx, const char* aMessage, JSErrorReport* aReport); + + int32_t + SetTimeout(JSContext* aCx, + Function* aHandler, + const nsAString& aStringHandler, + int32_t aTimeout, + const Sequence& aArguments, + bool aIsInterval, + ErrorResult& aRv); + + void + ClearTimeout(int32_t aId); + + bool + RunExpiredTimeouts(JSContext* aCx); + + bool + RescheduleTimeoutTimer(JSContext* aCx); + + void + CloseHandlerStarted() + { + AssertIsOnWorkerThread(); + mCloseHandlerStarted = true; + } + + void + CloseHandlerFinished() + { + AssertIsOnWorkerThread(); + mCloseHandlerFinished = true; + } + + void + UpdateRuntimeAndContextOptionsInternal( + JSContext* aCx, + const JS::RuntimeOptions& aRuntimeOptions, + const JS::ContextOptions& aContentCxOptions, + const JS::ContextOptions& aChromeCxOptions); + + void + UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue); + + void + UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue); + + enum WorkerRanOrNot { + WorkerNeverRan = 0, + WorkerRan + }; + + void + ScheduleDeletion(WorkerRanOrNot aRanOrNot); + + bool + BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats); + +#ifdef JS_GC_ZEAL + void + UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency); +#endif + + void + GarbageCollectInternal(JSContext* aCx, bool aShrinking, + bool aCollectChildren); + + void + CycleCollectInternal(JSContext* aCx, bool aCollectChildren); + + void + OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline); + + JSContext* + GetJSContext() const + { + AssertIsOnWorkerThread(); + return mJSContext; + } + + WorkerGlobalScope* + GlobalScope() const + { + AssertIsOnWorkerThread(); + return mScope; + } + + void + SetThread(nsIThread* aThread); + + void + AssertIsOnWorkerThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + WorkerCrossThreadDispatcher* + GetCrossThreadDispatcher(); + + // This may block! + void + BeginCTypesCall(); + + // This may block! + void + EndCTypesCall(); + + void + BeginCTypesCallback() + { + // If a callback is beginning then we need to do the exact same thing as + // when a ctypes call ends. + EndCTypesCall(); + } + + void + EndCTypesCallback() + { + // If a callback is ending then we need to do the exact same thing as + // when a ctypes call begins. + BeginCTypesCall(); + } + + bool + ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial); + + void + DisconnectMessagePort(uint64_t aMessagePortSerial); + + MessagePort* + GetMessagePort(uint64_t aMessagePortSerial); + + JSObject* + CreateGlobalScope(JSContext* aCx); + + bool + RegisterBindings(JSContext* aCx, JS::Handle aGlobal); + + bool + DumpEnabled() const + { + AssertIsOnWorkerThread(); + return mPreferences[WORKERPREF_DUMP]; + } + + bool + OnLine() const + { + AssertIsOnWorkerThread(); + return mOnLine; + } + + void + StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult); + + bool + AllPendingRunnablesShouldBeCanceled() const + { + return mCancelAllPendingRunnables; + } + + void + OnProcessNextEvent(uint32_t aRecursionDepth); + + void + AfterProcessNextEvent(uint32_t aRecursionDepth); + + void + AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) +#ifdef DEBUG + ; +#else + { } +#endif + +private: + WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent, + const nsAString& aScriptURL, bool aIsChromeWorker, + WorkerType aWorkerType, const nsACString& aSharedWorkerName, + LoadInfo& aLoadInfo); + + void + ClearMainEventQueue(WorkerRanOrNot aRanOrNot); + + bool + MayContinueRunning() + { + AssertIsOnWorkerThread(); + + Status status; + { + MutexAutoLock lock(mMutex); + status = mStatus; + } + + if (status >= Killing) { + return false; + } + if (status >= Running) { + return mKillTime.IsNull() || RemainingRunTimeMS() > 0; + } + return true; + } + + uint32_t + RemainingRunTimeMS() const; + + void + CancelAllTimeouts(JSContext* aCx); + + bool + ScheduleKillCloseEventRunnable(JSContext* aCx); + + bool + ProcessAllControlRunnables() + { + MutexAutoLock lock(mMutex); + return ProcessAllControlRunnablesLocked(); + } + + bool + ProcessAllControlRunnablesLocked(); + + void + EnableMemoryReporter(); + + void + DisableMemoryReporter(); + + void + WaitForWorkerEvents(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT); + + void + PostMessageToParentInternal(JSContext* aCx, + JS::Handle aMessage, + const Optional>& aTransferable, + bool aToMessagePort, + uint64_t aMessagePortSerial, + ErrorResult& aRv); + + void + GetAllPreferences(bool aPreferences[WORKERPREF_COUNT]) const + { + AssertIsOnWorkerThread(); + memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool)); + } + + already_AddRefed + CreateNewSyncLoop(); + + bool + RunCurrentSyncLoop(); + + bool + DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread = nullptr); + + void + InitializeGCTimers(); + + void + SetGCTimerMode(GCTimerMode aMode); + + void + ShutdownGCTimers(); +}; + +// This class is only used to trick the DOM bindings. We never create +// instances of it, and static_casting to it is fine since it doesn't add +// anything to WorkerPrivate. +class ChromeWorkerPrivate : public WorkerPrivate +{ +public: + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL, + ErrorResult& rv); + + static bool + WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */); + +private: + ChromeWorkerPrivate() MOZ_DELETE; + ChromeWorkerPrivate(const ChromeWorkerPrivate& aRHS) MOZ_DELETE; + ChromeWorkerPrivate& operator =(const ChromeWorkerPrivate& aRHS) MOZ_DELETE; +}; + +WorkerPrivate* +GetWorkerPrivateFromContext(JSContext* aCx); + +WorkerPrivate* +GetCurrentThreadWorkerPrivate(); + +bool +IsCurrentThreadRunningChromeWorker(); + +JSContext* +GetCurrentThreadJSContext(); + +enum WorkerStructuredDataType +{ + DOMWORKER_SCTAG_FILE = SCTAG_DOM_MAX, + DOMWORKER_SCTAG_BLOB, + + DOMWORKER_SCTAG_END +}; + +JSStructuredCloneCallbacks* +WorkerStructuredCloneCallbacks(bool aMainRuntime); + +JSStructuredCloneCallbacks* +ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime); + +class AutoSyncLoopHolder +{ + WorkerPrivate* mWorkerPrivate; + nsCOMPtr mTarget; + uint32_t mIndex; + +public: + AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate) + : mWorkerPrivate(aWorkerPrivate) + , mTarget(aWorkerPrivate->CreateNewSyncLoop()) + , mIndex(aWorkerPrivate->mSyncLoopStack.Length() - 1) + { + aWorkerPrivate->AssertIsOnWorkerThread(); + } + + ~AutoSyncLoopHolder() + { + if (mWorkerPrivate) { + mWorkerPrivate->AssertIsOnWorkerThread(); + mWorkerPrivate->StopSyncLoop(mTarget, false); + mWorkerPrivate->DestroySyncLoop(mIndex); + } + } + + bool + Run() + { + WorkerPrivate* workerPrivate = mWorkerPrivate; + mWorkerPrivate = nullptr; + + workerPrivate->AssertIsOnWorkerThread(); + + return workerPrivate->RunCurrentSyncLoop(); + } + + nsIEventTarget* + EventTarget() const + { + return mTarget; + } +}; + +END_WORKERS_NAMESPACE + +#endif /* mozilla_dom_workers_workerprivate_h__ */