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