summaryrefslogtreecommitdiffstats
path: root/dom/workers/WorkerPrivate.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/WorkerPrivate.h')
-rw-r--r--dom/workers/WorkerPrivate.h1594
1 files changed, 1594 insertions, 0 deletions
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
new file mode 100644
index 000000000..ad906b054
--- /dev/null
+++ b/dom/workers/WorkerPrivate.h
@@ -0,0 +1,1594 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "js/CharacterEncoding.h"
+#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsILoadGroup.h"
+#include "nsIWorkerDebugger.h"
+#include "nsPIDOMWindow.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/ConsoleReportCollector.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/Move.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "nsTObserverArray.h"
+
+#include "Queue.h"
+#include "WorkerHolder.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
+class nsIChannel;
+class nsIConsoleReportCollector;
+class nsIDocument;
+class nsIEventTarget;
+class nsIPrincipal;
+class nsIScriptContext;
+class nsIScriptTimeoutHandler;
+class nsISerializable;
+class nsIThread;
+class nsIThreadInternal;
+class nsITimer;
+class nsIURI;
+template<class T> class nsMainThreadPtrHandle;
+
+namespace JS {
+struct RuntimeStats;
+} // namespace JS
+
+namespace mozilla {
+class ThrottledEventQueue;
+namespace dom {
+class Function;
+class MessagePort;
+class MessagePortIdentifier;
+class PromiseNativeHandler;
+class StructuredCloneHolder;
+class WorkerDebuggerGlobalScope;
+class WorkerGlobalScope;
+} // namespace dom
+namespace ipc {
+class PrincipalInfo;
+} // namespace ipc
+} // namespace mozilla
+
+struct PRThread;
+
+class ReportDebuggerErrorRunnable;
+class PostDebuggerMessageRunnable;
+
+BEGIN_WORKERS_NAMESPACE
+
+class AutoSyncLoopHolder;
+class SharedWorker;
+class ServiceWorkerClientInfo;
+class WorkerControlRunnable;
+class WorkerDebugger;
+class WorkerPrivate;
+class WorkerRunnable;
+class WorkerThread;
+
+// 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 final : public Mutex
+ {
+ public:
+ explicit RefCountedMutex(const char* aName)
+ : Mutex(aName)
+ { }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMutex)
+
+ private:
+ ~RefCountedMutex()
+ { }
+ };
+
+ RefPtr<RefCountedMutex> mMutex;
+
+public:
+ explicit 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 Derived>
+class WorkerPrivateParent : public DOMEventTargetHelper
+{
+protected:
+ class EventTarget;
+ friend class EventTarget;
+
+ typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
+
+public:
+ struct LocationInfo
+ {
+ nsCString mHref;
+ nsCString mProtocol;
+ nsCString mHost;
+ nsCString mHostname;
+ nsCString mPort;
+ nsCString mPathname;
+ nsCString mSearch;
+ nsCString mHash;
+ nsString mOrigin;
+ };
+
+protected:
+ typedef mozilla::ErrorResult ErrorResult;
+
+ SharedMutex mMutex;
+ mozilla::CondVar mCondVar;
+
+ // Protected by mMutex.
+ RefPtr<EventTarget> mEventTarget;
+ nsTArray<RefPtr<WorkerRunnable>> mPreStartRunnables;
+
+private:
+ WorkerPrivate* mParent;
+ nsString mScriptURL;
+ // This is the worker name for shared workers or the worker scope
+ // for service workers.
+ nsCString mWorkerName;
+ LocationInfo mLocationInfo;
+ // The lifetime of these objects within LoadInfo is managed explicitly;
+ // they do not need to be cycle collected.
+ WorkerLoadInfo mLoadInfo;
+
+ Atomic<bool> mLoadingWorkerScript;
+
+ // Only used for top level workers.
+ nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
+
+ // Protected by mMutex.
+ JSSettings mJSSettings;
+
+ // Only touched on the parent thread (currently this is always the main
+ // thread as SharedWorkers are always top-level).
+ nsTArray<RefPtr<SharedWorker>> mSharedWorkers;
+
+ uint64_t mBusyCount;
+ // SharedWorkers may have multiple windows paused, so this must be
+ // a count instead of just a boolean.
+ uint32_t mParentWindowPausedDepth;
+ Status mParentStatus;
+ bool mParentFrozen;
+ bool mIsChromeWorker;
+ bool mMainThreadObjectsForgotten;
+ // mIsSecureContext is set once in our constructor; after that it can be read
+ // from various threads. We could make this const if we were OK with setting
+ // it in the initializer list via calling some function that takes all sorts
+ // of state (loadinfo, worker type, parent).
+ //
+ // It's a bit unfortunate that we have to have an out-of-band boolean for
+ // this, but we need access to this state from the parent thread, and we can't
+ // use our global object's secure state there.
+ bool mIsSecureContext;
+ WorkerType mWorkerType;
+ TimeStamp mCreationTimeStamp;
+ DOMHighResTimeStamp mCreationTimeHighRes;
+ TimeStamp mNowBaseTimeStamp;
+ DOMHighResTimeStamp mNowBaseTimeHighRes;
+
+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.
+ RefPtr<WorkerPrivate> mSelfRef;
+
+ WorkerPrivateParent(WorkerPrivate* aParent,
+ const nsAString& aScriptURL, bool aIsChromeWorker,
+ WorkerType aWorkerType,
+ const nsACString& aSharedWorkerName,
+ WorkerLoadInfo& aLoadInfo);
+
+ ~WorkerPrivateParent();
+
+private:
+ Derived*
+ ParentAsWorkerPrivate() const
+ {
+ return static_cast<Derived*>(const_cast<WorkerPrivateParent*>(this));
+ }
+
+ bool
+ NotifyPrivate(Status aStatus);
+
+ bool
+ TerminatePrivate()
+ {
+ return NotifyPrivate(Terminating);
+ }
+
+ void
+ PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+ PromiseNativeHandler* aHandler,
+ ErrorResult& aRv);
+
+ nsresult
+ DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget);
+
+public:
+ virtual JSObject*
+ WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent,
+ DOMEventTargetHelper)
+
+ void
+ EnableDebugger();
+
+ void
+ DisableDebugger();
+
+ void
+ ClearSelfRef()
+ {
+ AssertIsOnParentThread();
+ MOZ_ASSERT(mSelfRef);
+ mSelfRef = nullptr;
+ }
+
+ nsresult
+ Dispatch(already_AddRefed<WorkerRunnable> aRunnable)
+ {
+ return DispatchPrivate(Move(aRunnable), nullptr);
+ }
+
+ nsresult
+ DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable);
+
+ nsresult
+ DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable);
+
+ already_AddRefed<WorkerRunnable>
+ MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable);
+
+ already_AddRefed<nsIEventTarget>
+ GetEventTarget();
+
+ // May be called on any thread...
+ bool
+ Start();
+
+ // Called on the parent thread.
+ bool
+ Notify(Status aStatus)
+ {
+ return NotifyPrivate(aStatus);
+ }
+
+ bool
+ Cancel()
+ {
+ return Notify(Canceling);
+ }
+
+ bool
+ Kill()
+ {
+ return Notify(Killing);
+ }
+
+ // We can assume that an nsPIDOMWindow will be available for Freeze, Thaw
+ // as these are only used for globals going in and out of the bfcache.
+ //
+ // XXXbz: This is a bald-faced lie given the uses in RegisterSharedWorker and
+ // CloseSharedWorkersForWindow, which pass null for aWindow to Thaw and Freeze
+ // respectively. See bug 1251722.
+ bool
+ Freeze(nsPIDOMWindowInner* aWindow);
+
+ bool
+ Thaw(nsPIDOMWindowInner* aWindow);
+
+ // When we debug a worker, we want to disconnect the window and the worker
+ // communication. This happens calling this method.
+ // Note: this method doesn't suspend the worker! Use Freeze/Thaw instead.
+ void
+ ParentWindowPaused();
+
+ void
+ ParentWindowResumed();
+
+ bool
+ Terminate()
+ {
+ AssertIsOnParentThread();
+ return TerminatePrivate();
+ }
+
+ bool
+ Close();
+
+ bool
+ ModifyBusyCount(bool aIncrease);
+
+ void
+ ForgetOverridenLoadGroup(nsCOMPtr<nsILoadGroup>& aLoadGroupOut);
+
+ void
+ ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
+
+ void
+ PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+ PromiseNativeHandler* aHandler,
+ ErrorResult& aRv);
+
+ void
+ UpdateContextOptions(const JS::ContextOptions& aContextOptions);
+
+ void
+ UpdateLanguages(const nsTArray<nsString>& aLanguages);
+
+ void
+ UpdatePreference(WorkerPreference aPref, bool aValue);
+
+ void
+ UpdateJSWorkerMemoryParameter(JSGCParamKey key, uint32_t value);
+
+#ifdef JS_GC_ZEAL
+ void
+ UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency);
+#endif
+
+ void
+ GarbageCollect(bool aShrinking);
+
+ void
+ CycleCollect(bool aDummy);
+
+ void
+ OfflineStatusChangeEvent(bool aIsOffline);
+
+ void
+ MemoryPressure(bool aDummy);
+
+ bool
+ RegisterSharedWorker(SharedWorker* aSharedWorker, MessagePort* aPort);
+
+ 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)
+ {
+ AssertIsOnParentThread();
+ mQueuedRunnables.AppendElement(aRunnable);
+ }
+
+ WorkerPrivate*
+ GetParent() const
+ {
+ return mParent;
+ }
+
+ bool
+ IsFrozen() const
+ {
+ AssertIsOnParentThread();
+ return mParentFrozen;
+ }
+
+ bool
+ IsParentWindowPaused() const
+ {
+ AssertIsOnParentThread();
+ return mParentWindowPausedDepth > 0;
+ }
+
+ bool
+ IsAcceptingEvents()
+ {
+ AssertIsOnParentThread();
+
+ MutexAutoLock lock(mMutex);
+ return mParentStatus < Terminating;
+ }
+
+ Status
+ ParentStatus() const
+ {
+ mMutex.AssertCurrentThreadOwns();
+ return mParentStatus;
+ }
+
+ nsIScriptContext*
+ GetScriptContext() const
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mScriptContext;
+ }
+
+ const nsString&
+ ScriptURL() const
+ {
+ return mScriptURL;
+ }
+
+ const nsCString&
+ Domain() const
+ {
+ return mLoadInfo.mDomain;
+ }
+
+ bool
+ IsFromWindow() const
+ {
+ return mLoadInfo.mFromWindow;
+ }
+
+ uint64_t
+ WindowID() const
+ {
+ return mLoadInfo.mWindowID;
+ }
+
+ uint64_t
+ ServiceWorkerID() const
+ {
+ return mLoadInfo.mServiceWorkerID;
+ }
+
+ nsIURI*
+ GetBaseURI() const
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mBaseURI;
+ }
+
+ void
+ SetBaseURI(nsIURI* aBaseURI);
+
+ nsIURI*
+ GetResolvedScriptURI() const
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mResolvedScriptURI;
+ }
+
+ const nsString&
+ ServiceWorkerCacheName() const
+ {
+ MOZ_ASSERT(IsServiceWorker());
+ AssertIsOnMainThread();
+ return mLoadInfo.mServiceWorkerCacheName;
+ }
+
+ const ChannelInfo&
+ GetChannelInfo() const
+ {
+ return mLoadInfo.mChannelInfo;
+ }
+
+ void
+ SetChannelInfo(const ChannelInfo& aChannelInfo)
+ {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!mLoadInfo.mChannelInfo.IsInitialized());
+ MOZ_ASSERT(aChannelInfo.IsInitialized());
+ mLoadInfo.mChannelInfo = aChannelInfo;
+ }
+
+ void
+ InitChannelInfo(nsIChannel* aChannel)
+ {
+ mLoadInfo.mChannelInfo.InitFromChannel(aChannel);
+ }
+
+ void
+ InitChannelInfo(const ChannelInfo& aChannelInfo)
+ {
+ mLoadInfo.mChannelInfo = aChannelInfo;
+ }
+
+ // This is used to handle importScripts(). When the worker is first loaded
+ // and executed, it happens in a sync loop. At this point it sets
+ // mLoadingWorkerScript to true. importScripts() calls that occur during the
+ // execution run in nested sync loops and so this continues to return true,
+ // leading to these scripts being cached offline.
+ // mLoadingWorkerScript is set to false when the top level loop ends.
+ // importScripts() in function calls or event handlers are always fetched
+ // from the network.
+ bool
+ LoadScriptAsPartOfLoadingServiceWorkerScript()
+ {
+ MOZ_ASSERT(IsServiceWorker());
+ return mLoadingWorkerScript;
+ }
+
+ void
+ SetLoadingWorkerScript(bool aLoadingWorkerScript)
+ {
+ // any thread
+ MOZ_ASSERT(IsServiceWorker());
+ mLoadingWorkerScript = aLoadingWorkerScript;
+ }
+
+ TimeStamp CreationTimeStamp() const
+ {
+ return mCreationTimeStamp;
+ }
+
+ DOMHighResTimeStamp CreationTime() const
+ {
+ return mCreationTimeHighRes;
+ }
+
+ TimeStamp NowBaseTimeStamp() const
+ {
+ return mNowBaseTimeStamp;
+ }
+
+ DOMHighResTimeStamp NowBaseTime() const
+ {
+ return mNowBaseTimeHighRes;
+ }
+
+ nsIPrincipal*
+ GetPrincipal() const
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mPrincipal;
+ }
+
+ nsILoadGroup*
+ GetLoadGroup() const
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mLoadGroup;
+ }
+
+ // 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, nsILoadGroup* aLoadGroup);
+
+ bool
+ UsesSystemPrincipal() const
+ {
+ return mLoadInfo.mPrincipalIsSystem;
+ }
+
+ const PrincipalInfo&
+ GetPrincipalInfo() const
+ {
+ return *mLoadInfo.mPrincipalInfo;
+ }
+
+ already_AddRefed<nsIChannel>
+ ForgetWorkerChannel()
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mChannel.forget();
+ }
+
+ nsIDocument* GetDocument() const;
+
+ nsPIDOMWindowInner*
+ GetWindow()
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mWindow;
+ }
+
+ nsIContentSecurityPolicy*
+ GetCSP() const
+ {
+ AssertIsOnMainThread();
+ return mLoadInfo.mCSP;
+ }
+
+ void
+ SetCSP(nsIContentSecurityPolicy* aCSP)
+ {
+ AssertIsOnMainThread();
+ mLoadInfo.mCSP = aCSP;
+ }
+
+ net::ReferrerPolicy
+ GetReferrerPolicy() const
+ {
+ return mLoadInfo.mReferrerPolicy;
+ }
+
+ void
+ SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
+ {
+ mLoadInfo.mReferrerPolicy = aReferrerPolicy;
+ }
+
+ bool
+ IsEvalAllowed() const
+ {
+ return mLoadInfo.mEvalAllowed;
+ }
+
+ void
+ SetEvalAllowed(bool aEvalAllowed)
+ {
+ mLoadInfo.mEvalAllowed = aEvalAllowed;
+ }
+
+ bool
+ GetReportCSPViolations() const
+ {
+ return mLoadInfo.mReportCSPViolations;
+ }
+
+ void
+ SetReportCSPViolations(bool aReport)
+ {
+ mLoadInfo.mReportCSPViolations = aReport;
+ }
+
+ 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|Service].
+ bool
+ IsChromeWorker() const
+ {
+ return mIsChromeWorker;
+ }
+
+ WorkerType
+ Type() const
+ {
+ return mWorkerType;
+ }
+
+ bool
+ IsDedicatedWorker() const
+ {
+ return mWorkerType == WorkerTypeDedicated;
+ }
+
+ bool
+ IsSharedWorker() const
+ {
+ return mWorkerType == WorkerTypeShared;
+ }
+
+ bool
+ IsServiceWorker() const
+ {
+ return mWorkerType == WorkerTypeService;
+ }
+
+ nsContentPolicyType
+ ContentPolicyType() const
+ {
+ return ContentPolicyType(mWorkerType);
+ }
+
+ static nsContentPolicyType
+ ContentPolicyType(WorkerType aWorkerType)
+ {
+ switch (aWorkerType) {
+ case WorkerTypeDedicated:
+ return nsIContentPolicy::TYPE_INTERNAL_WORKER;
+ case WorkerTypeShared:
+ return nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+ case WorkerTypeService:
+ return nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid worker type");
+ return nsIContentPolicy::TYPE_INVALID;
+ }
+ }
+
+ const nsCString&
+ WorkerName() const
+ {
+ MOZ_ASSERT(IsServiceWorker() || IsSharedWorker());
+ return mWorkerName;
+ }
+
+ bool
+ IsStorageAllowed() const
+ {
+ return mLoadInfo.mStorageAllowed;
+ }
+
+ const PrincipalOriginAttributes&
+ GetOriginAttributes() const
+ {
+ return mLoadInfo.mOriginAttributes;
+ }
+
+ // Determine if the SW testing per-window flag is set by devtools
+ bool
+ ServiceWorkersTestingInWindow() const
+ {
+ return mLoadInfo.mServiceWorkersTestingInWindow;
+ }
+
+ void
+ GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>& aSharedWorkers);
+
+ void
+ CloseSharedWorkersForWindow(nsPIDOMWindowInner* aWindow);
+
+ void
+ CloseAllSharedWorkers();
+
+ void
+ UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
+
+ already_AddRefed<nsIRunnable>
+ StealLoadFailedAsyncRunnable()
+ {
+ return mLoadInfo.mLoadFailedAsyncRunnable.forget();
+ }
+
+ void
+ FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter);
+
+ IMPL_EVENT_HANDLER(message)
+ IMPL_EVENT_HANDLER(error)
+
+ // Check whether this worker is a secure context. For use from the parent
+ // thread only; the canonical "is secure context" boolean is stored on the
+ // compartment of the worker global. The only reason we don't
+ // AssertIsOnParentThread() here is so we can assert that this value matches
+ // the one on the compartment, which has to be done from the worker thread.
+ bool IsSecureContext() const
+ {
+ return mIsSecureContext;
+ }
+
+#ifdef DEBUG
+ void
+ AssertIsOnParentThread() const;
+
+ void
+ AssertInnerWindowIsCorrect() const;
+#else
+ void
+ AssertIsOnParentThread() const
+ { }
+
+ void
+ AssertInnerWindowIsCorrect() const
+ { }
+#endif
+};
+
+class WorkerDebugger : public nsIWorkerDebugger {
+ friend class ::ReportDebuggerErrorRunnable;
+ friend class ::PostDebuggerMessageRunnable;
+
+ WorkerPrivate* mWorkerPrivate;
+ bool mIsInitialized;
+ nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners;
+
+public:
+ explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWORKERDEBUGGER
+
+ void
+ AssertIsOnParentThread();
+
+ void
+ Close();
+
+ void
+ PostMessageToDebugger(const nsAString& aMessage);
+
+ void
+ ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
+ const nsAString& aMessage);
+
+private:
+ virtual
+ ~WorkerDebugger();
+
+ void
+ PostMessageToDebuggerOnMainThread(const nsAString& aMessage);
+
+ void
+ ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
+ uint32_t aLineno,
+ const nsAString& aMessage);
+};
+
+class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
+{
+ friend class WorkerHolder;
+ friend class WorkerPrivateParent<WorkerPrivate>;
+ typedef WorkerPrivateParent<WorkerPrivate> ParentType;
+ friend class AutoSyncLoopHolder;
+
+ struct TimeoutInfo;
+
+ class MemoryReporter;
+ friend class MemoryReporter;
+
+ friend class WorkerThread;
+
+ enum GCTimerMode
+ {
+ PeriodicTimer = 0,
+ IdleTimer,
+ NoTimer
+ };
+
+ bool mDebuggerRegistered;
+ WorkerDebugger* mDebugger;
+
+ Queue<WorkerControlRunnable*, 4> mControlQueue;
+ Queue<WorkerRunnable*, 4> mDebuggerQueue;
+
+ // Touched on multiple threads, protected with mMutex.
+ JSContext* mJSContext;
+ RefPtr<WorkerCrossThreadDispatcher> mCrossThreadDispatcher;
+ nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
+ RefPtr<WorkerThread> mThread;
+ PRThread* mPRThread;
+
+ // Things touched on worker thread only.
+ RefPtr<WorkerGlobalScope> mScope;
+ RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
+ nsTArray<ParentType*> mChildWorkers;
+ nsTObserverArray<WorkerHolder*> mHolders;
+ nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
+ uint32_t mDebuggerEventLoopLevel;
+ RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue;
+ nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
+
+ struct SyncLoopInfo
+ {
+ explicit SyncLoopInfo(EventTarget* aEventTarget);
+
+ RefPtr<EventTarget> 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<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
+
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<nsITimerCallback> mTimerRunnable;
+
+ nsCOMPtr<nsITimer> mGCTimer;
+ nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
+ nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
+
+ RefPtr<MemoryReporter> mMemoryReporter;
+
+ // fired on the main thread if the worker script fails to load
+ nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
+
+ JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
+ TimeStamp mKillTime;
+ uint32_t mErrorHandlerRecursionCount;
+ uint32_t mNextTimeoutId;
+ Status mStatus;
+ bool mFrozen;
+ bool mTimerRunning;
+ bool mRunningExpiredTimeouts;
+ bool mPendingEventQueueClearing;
+ bool mCancelAllPendingRunnables;
+ bool mPeriodicGCTimerRunning;
+ bool mIdleGCTimerRunning;
+ bool mWorkerScriptExecutedSuccessfully;
+ bool mPreferences[WORKERPREF_COUNT];
+ bool mOnLine;
+
+protected:
+ ~WorkerPrivate();
+
+public:
+ static already_AddRefed<WorkerPrivate>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
+ ErrorResult& aRv);
+
+ static already_AddRefed<WorkerPrivate>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
+ bool aIsChromeWorker, WorkerType aWorkerType,
+ const nsACString& aSharedWorkerName,
+ WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
+
+ static already_AddRefed<WorkerPrivate>
+ Constructor(JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
+ WorkerType aWorkerType, const nsACString& aSharedWorkerName,
+ WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
+
+ static bool
+ WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
+
+ enum LoadGroupBehavior
+ {
+ InheritLoadGroup,
+ OverrideLoadGroup
+ };
+
+ static nsresult
+ GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
+ WorkerPrivate* aParent,
+ const nsAString& aScriptURL, bool aIsChromeWorker,
+ LoadGroupBehavior aLoadGroupBehavior, WorkerType aWorkerType,
+ WorkerLoadInfo* aLoadInfo);
+
+ static void
+ OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo);
+
+ bool
+ IsDebuggerRegistered()
+ {
+ AssertIsOnMainThread();
+
+ // No need to lock here since this is only ever modified by the same thread.
+ return mDebuggerRegistered;
+ }
+
+ void
+ SetIsDebuggerRegistered(bool aDebuggerRegistered)
+ {
+ AssertIsOnMainThread();
+
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mDebuggerRegistered != aDebuggerRegistered);
+ mDebuggerRegistered = aDebuggerRegistered;
+
+ mCondVar.Notify();
+ }
+
+ void
+ WaitForIsDebuggerRegistered(bool aDebuggerRegistered)
+ {
+ AssertIsOnParentThread();
+
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ MutexAutoLock lock(mMutex);
+
+ while (mDebuggerRegistered != aDebuggerRegistered) {
+ mCondVar.Wait();
+ }
+ }
+
+ WorkerDebugger*
+ Debugger() const
+ {
+ AssertIsOnMainThread();
+
+ MOZ_ASSERT(mDebugger);
+ return mDebugger;
+ }
+
+ void
+ SetDebugger(WorkerDebugger* aDebugger)
+ {
+ AssertIsOnMainThread();
+
+ MOZ_ASSERT(mDebugger != aDebugger);
+ mDebugger = aDebugger;
+ }
+
+ JS::UniqueChars
+ AdoptDefaultLocale()
+ {
+ MOZ_ASSERT(mDefaultLocale,
+ "the default locale must have been successfully set for anyone "
+ "to be trying to adopt it");
+ return Move(mDefaultLocale);
+ }
+
+ void
+ DoRunLoop(JSContext* aCx);
+
+ bool
+ InterruptCallback(JSContext* aCx);
+
+ nsresult
+ IsOnCurrentThread(bool* aIsOnCurrentThread);
+
+ bool
+ CloseInternal(JSContext* aCx)
+ {
+ AssertIsOnWorkerThread();
+ return NotifyInternal(aCx, Closing);
+ }
+
+ bool
+ FreezeInternal();
+
+ bool
+ ThawInternal();
+
+ void
+ TraverseTimeouts(nsCycleCollectionTraversalCallback& aCallback);
+
+ void
+ UnlinkTimeouts();
+
+ bool
+ ModifyBusyCountFromWorker(bool aIncrease);
+
+ bool
+ AddChildWorker(ParentType* aChildWorker);
+
+ void
+ RemoveChildWorker(ParentType* aChildWorker);
+
+ void
+ PostMessageToParent(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv)
+ {
+ PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
+ }
+
+ void
+ PostMessageToParentMessagePort(
+ JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ EnterDebuggerEventLoop();
+
+ void
+ LeaveDebuggerEventLoop();
+
+ void
+ PostMessageToDebugger(const nsAString& aMessage);
+
+ void
+ SetDebuggerImmediate(Function& aHandler, ErrorResult& aRv);
+
+ void
+ ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
+ const nsAString& aMessage);
+
+ bool
+ NotifyInternal(JSContext* aCx, Status aStatus);
+
+ void
+ ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
+ JSErrorReport* aReport);
+
+ static void
+ ReportErrorToConsole(const char* aMessage);
+
+ int32_t
+ SetTimeout(JSContext* aCx, nsIScriptTimeoutHandler* aHandler,
+ int32_t aTimeout, bool aIsInterval,
+ ErrorResult& aRv);
+
+ void
+ ClearTimeout(int32_t aId);
+
+ bool
+ RunExpiredTimeouts(JSContext* aCx);
+
+ bool
+ RescheduleTimeoutTimer(JSContext* aCx);
+
+ void
+ UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions);
+
+ void
+ UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
+
+ void
+ UpdatePreferenceInternal(WorkerPreference aPref, bool aValue);
+
+ void
+ UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
+
+ enum WorkerRanOrNot {
+ WorkerNeverRan = 0,
+ WorkerRan
+ };
+
+ void
+ ScheduleDeletion(WorkerRanOrNot aRanOrNot);
+
+ bool
+ CollectRuntimeStats(JS::RuntimeStats* aRtStats, bool aAnonymize);
+
+#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(bool aCollectChildren);
+
+ void
+ OfflineStatusChangeEventInternal(bool aIsOffline);
+
+ void
+ MemoryPressureInternal();
+
+ JSContext*
+ GetJSContext() const
+ {
+ AssertIsOnWorkerThread();
+ return mJSContext;
+ }
+
+ WorkerGlobalScope*
+ GlobalScope() const
+ {
+ AssertIsOnWorkerThread();
+ return mScope;
+ }
+
+ WorkerDebuggerGlobalScope*
+ DebuggerGlobalScope() const
+ {
+ AssertIsOnWorkerThread();
+ return mDebuggerScope;
+ }
+
+ void
+ SetThread(WorkerThread* 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, MessagePortIdentifier& aIdentifier);
+
+ WorkerGlobalScope*
+ GetOrCreateGlobalScope(JSContext* aCx);
+
+ WorkerDebuggerGlobalScope*
+ CreateDebuggerGlobalScope(JSContext* aCx);
+
+ bool
+ RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+
+ bool
+ RegisterDebuggerBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+
+#define WORKER_SIMPLE_PREF(name, getter, NAME) \
+ bool \
+ getter() const \
+ { \
+ AssertIsOnWorkerThread(); \
+ return mPreferences[WORKERPREF_##NAME]; \
+ }
+#define WORKER_PREF(name, callback)
+#include "WorkerPrefs.h"
+#undef WORKER_SIMPLE_PREF
+#undef WORKER_PREF
+
+ bool
+ OnLine() const
+ {
+ AssertIsOnWorkerThread();
+ return mOnLine;
+ }
+
+ void
+ StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);
+
+ bool
+ AllPendingRunnablesShouldBeCanceled() const
+ {
+ return mCancelAllPendingRunnables;
+ }
+
+ void
+ ClearMainEventQueue(WorkerRanOrNot aRanOrNot);
+
+ void
+ ClearDebuggerEventQueue();
+
+ void
+ OnProcessNextEvent();
+
+ void
+ AfterProcessNextEvent();
+
+ void
+ AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+ void
+ SetWorkerScriptExecutedSuccessfully()
+ {
+ AssertIsOnWorkerThread();
+ // Should only be called once!
+ MOZ_ASSERT(!mWorkerScriptExecutedSuccessfully);
+ mWorkerScriptExecutedSuccessfully = true;
+ }
+
+ // Only valid after CompileScriptRunnable has finished running!
+ bool
+ WorkerScriptExecutedSuccessfully() const
+ {
+ AssertIsOnWorkerThread();
+ return mWorkerScriptExecutedSuccessfully;
+ }
+
+ void
+ MaybeDispatchLoadFailedRunnable();
+
+ // Get the event target to use when dispatching to the main thread
+ // from this Worker thread. This may be the main thread itself or
+ // a ThrottledEventQueue to the main thread.
+ nsIEventTarget*
+ MainThreadEventTarget();
+
+ nsresult
+ DispatchToMainThread(nsIRunnable* aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ nsresult
+ DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+private:
+ WorkerPrivate(WorkerPrivate* aParent,
+ const nsAString& aScriptURL, bool aIsChromeWorker,
+ WorkerType aWorkerType, const nsACString& aSharedWorkerName,
+ WorkerLoadInfo& aLoadInfo);
+
+ bool
+ MayContinueRunning()
+ {
+ AssertIsOnWorkerThread();
+
+ Status status;
+ {
+ MutexAutoLock lock(mMutex);
+ status = mStatus;
+ }
+
+ if (status < Terminating) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void
+ CancelAllTimeouts();
+
+ enum class ProcessAllControlRunnablesResult
+ {
+ // We did not process anything.
+ Nothing,
+ // We did process something, states may have changed, but we can keep
+ // executing script.
+ MayContinue,
+ // We did process something, and should not continue executing script.
+ Abort
+ };
+
+ ProcessAllControlRunnablesResult
+ ProcessAllControlRunnables()
+ {
+ MutexAutoLock lock(mMutex);
+ return ProcessAllControlRunnablesLocked();
+ }
+
+ ProcessAllControlRunnablesResult
+ ProcessAllControlRunnablesLocked();
+
+ void
+ EnableMemoryReporter();
+
+ void
+ DisableMemoryReporter();
+
+ void
+ WaitForWorkerEvents(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT);
+
+ void
+ PostMessageToParentInternal(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ GetAllPreferences(bool aPreferences[WORKERPREF_COUNT]) const
+ {
+ AssertIsOnWorkerThread();
+ memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool));
+ }
+
+ already_AddRefed<nsIEventTarget>
+ CreateNewSyncLoop();
+
+ bool
+ RunCurrentSyncLoop();
+
+ bool
+ DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread = nullptr);
+
+ void
+ InitializeGCTimers();
+
+ void
+ SetGCTimerMode(GCTimerMode aMode);
+
+ void
+ ShutdownGCTimers();
+
+ bool
+ AddHolder(WorkerHolder* aHolder, Status aFailStatus);
+
+ void
+ RemoveHolder(WorkerHolder* aHolder);
+
+ void
+ NotifyHolders(JSContext* aCx, Status aStatus);
+
+ bool
+ HasActiveHolders()
+ {
+ return !(mChildWorkers.IsEmpty() && mTimeouts.IsEmpty() &&
+ mHolders.IsEmpty());
+ }
+};
+
+// 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<ChromeWorkerPrivate>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
+ ErrorResult& rv);
+
+ static bool
+ WorkerAvailable(JSContext* aCx, JSObject* /* unused */);
+
+private:
+ ChromeWorkerPrivate() = delete;
+ ChromeWorkerPrivate(const ChromeWorkerPrivate& aRHS) = delete;
+ ChromeWorkerPrivate& operator =(const ChromeWorkerPrivate& aRHS) = delete;
+};
+
+WorkerPrivate*
+GetWorkerPrivateFromContext(JSContext* aCx);
+
+WorkerPrivate*
+GetCurrentThreadWorkerPrivate();
+
+bool
+IsCurrentThreadRunningChromeWorker();
+
+JSContext*
+GetCurrentThreadJSContext();
+
+JSObject*
+GetCurrentThreadWorkerGlobal();
+
+class AutoSyncLoopHolder
+{
+ WorkerPrivate* mWorkerPrivate;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ uint32_t mIndex;
+
+public:
+ explicit 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;
+ }
+};
+
+class TimerThreadEventTarget final : public nsIEventTarget
+{
+ ~TimerThreadEventTarget();
+
+ WorkerPrivate* mWorkerPrivate;
+ RefPtr<WorkerRunnable> mWorkerRunnable;
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
+ WorkerRunnable* aWorkerRunnable);
+
+protected:
+ NS_IMETHOD
+ DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
+
+
+ NS_IMETHOD
+ Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override;
+
+ NS_IMETHOD
+ DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override;
+
+ NS_IMETHOD
+ IsOnCurrentThread(bool* aIsOnCurrentThread) override;
+};
+
+END_WORKERS_NAMESPACE
+
+#endif /* mozilla_dom_workers_workerprivate_h__ */