Source code

Revision control

Other Tools

/* -*- 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/. */
#include "WorkerPrivate.h"
#include <utility>
#include "js/CallAndConstruct.h" // JS_CallFunctionValue
#include "js/CompilationAndEvaluation.h"
#include "js/ContextOptions.h"
#include "js/Exception.h"
#include "js/friend/ErrorMessages.h" // JSMSG_OUT_OF_MEMORY
#include "js/LocaleSensitive.h"
#include "js/MemoryMetrics.h"
#include "js/SourceText.h"
#include "MessageEventRunnable.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/Result.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CallbackDebuggerNotification.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/IndexedDatabaseManager.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/nsCSPUtils.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceStorageWorker.h"
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/RemoteWorkerChild.h"
#include "mozilla/dom/RemoteWorkerService.h"
#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/JSExecutionManager.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ThreadEventQueue.h"
#include "mozilla/ThrottledEventQueue.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/WorkerTimelineMarker.h"
#include "nsCycleCollector.h"
#include "nsGlobalWindowInner.h"
#include "nsIDUtils.h"
#include "nsNetUtil.h"
#include "nsIFile.h"
#include "nsIMemoryReporter.h"
#include "nsIPermissionManager.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptError.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsIUUIDGenerator.h"
#include "nsPrintfCString.h"
#include "nsProxyRelease.h"
#include "nsQueryObject.h"
#include "nsRFPService.h"
#include "nsSandboxFlags.h"
#include "nsUTF8Utils.h"
#include "RuntimeService.h"
#include "ScriptLoader.h"
#include "mozilla/dom/ServiceWorkerEvents.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/net/CookieJarSettings.h"
#include "WorkerCSPEventListener.h"
#include "WorkerDebugger.h"
#include "WorkerDebuggerManager.h"
#include "WorkerError.h"
#include "WorkerEventTarget.h"
#include "WorkerNavigator.h"
#include "WorkerRef.h"
#include "WorkerRunnable.h"
#include "WorkerThread.h"
#include "nsThreadManager.h"
#ifdef XP_WIN
# undef PostMessage
#endif
// JS_MaybeGC will run once every second during normal execution.
#define PERIODIC_GC_TIMER_DELAY_SEC 1
// A shrinking GC will run five seconds after the last event is processed.
#define IDLE_GC_TIMER_DELAY_SEC 5
static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; }
mozilla::LogModule* TimeoutsLog() { return sWorkerTimeoutsLog; }
#ifdef LOG
# undef LOG
#endif
#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
namespace mozilla {
using namespace ipc;
namespace dom {
using namespace workerinternals;
MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
namespace {
#ifdef DEBUG
const nsIID kDEBUGWorkerEventTargetIID = {
0xccaba3fa,
0x5be2,
0x4de2,
{0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}};
#endif
// The number of nested timeouts before we start clamping. HTML says 5.
const uint32_t kClampTimeoutNestingLevel = 5u;
template <class T>
class UniquePtrComparator {
using A = UniquePtr<T>;
using B = T*;
public:
bool Equals(const A& a, const A& b) const {
return a && b ? *a == *b : !a && !b ? true : false;
}
bool LessThan(const A& a, const A& b) const {
return a && b ? *a < *b : b ? true : false;
}
};
template <class T>
inline UniquePtrComparator<T> GetUniquePtrComparator(
const nsTArray<UniquePtr<T>>&) {
return UniquePtrComparator<T>();
}
// This class is used to wrap any runnables that the worker receives via the
// nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
// from the worker's EventTarget).
class ExternalRunnableWrapper final : public WorkerRunnable {
nsCOMPtr<nsIRunnable> mWrappedRunnable;
public:
ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
nsIRunnable* aWrappedRunnable)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mWrappedRunnable(aWrappedRunnable) {
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aWrappedRunnable);
}
NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, WorkerRunnable)
private:
~ExternalRunnableWrapper() = default;
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
// Silence bad assertions.
return true;
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
// Silence bad assertions.
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
nsresult rv = mWrappedRunnable->Run();
if (NS_FAILED(rv)) {
if (!JS_IsExceptionPending(aCx)) {
Throw(aCx, rv);
}
return false;
}
return true;
}
nsresult Cancel() override {
nsCOMPtr<nsIDiscardableRunnable> doomed =
do_QueryInterface(mWrappedRunnable);
MOZ_ASSERT(doomed); // We checked this earlier!
doomed->OnDiscard();
return WorkerRunnable::Cancel();
}
};
struct WindowAction {
nsPIDOMWindowInner* mWindow;
bool mDefaultAction;
MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
: mWindow(aWindow), mDefaultAction(true) {}
bool operator==(const WindowAction& aOther) const {
return mWindow == aOther.mWindow;
}
};
class WorkerFinishedRunnable final : public WorkerControlRunnable {
WorkerPrivate* mFinishedWorker;
public:
WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
WorkerPrivate* aFinishedWorker)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mFinishedWorker(aFinishedWorker) {
aFinishedWorker->IncreaseWorkerFinishedRunnableCount();
}
private:
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
// Silence bad assertions.
return true;
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
// Silence bad assertions.
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
// This may block on the main thread.
AutoYieldJSThreadExecution yield;
mFinishedWorker->DecreaseWorkerFinishedRunnableCount();
if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
NS_WARNING("Failed to dispatch, going to leak!");
}
RuntimeService* runtime = RuntimeService::GetService();
NS_ASSERTION(runtime, "This should never be null!");
mFinishedWorker->DisableDebugger();
runtime->UnregisterWorker(*mFinishedWorker);
mFinishedWorker->ClearSelfAndParentEventTargetRef();
return true;
}
};
class TopLevelWorkerFinishedRunnable final : public Runnable {
WorkerPrivate* mFinishedWorker;
public:
explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
: mozilla::Runnable("TopLevelWorkerFinishedRunnable"),
mFinishedWorker(aFinishedWorker) {
aFinishedWorker->AssertIsOnWorkerThread();
aFinishedWorker->IncreaseTopLevelWorkerFinishedRunnableCount();
}
NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)
private:
~TopLevelWorkerFinishedRunnable() = default;
NS_IMETHOD
Run() override {
AssertIsOnMainThread();
mFinishedWorker->DecreaseTopLevelWorkerFinishedRunnableCount();
RuntimeService* runtime = RuntimeService::GetService();
MOZ_ASSERT(runtime);
mFinishedWorker->DisableDebugger();
runtime->UnregisterWorker(*mFinishedWorker);
if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
NS_WARNING("Failed to dispatch, going to leak!");
}
mFinishedWorker->ClearSelfAndParentEventTargetRef();
return NS_OK;
}
};
class ModifyBusyCountRunnable final : public WorkerControlRunnable {
bool mIncrease;
public:
ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
: WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
mIncrease(aIncrease) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->ModifyBusyCount(mIncrease);
}
virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {
if (mIncrease) {
WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
return;
}
// Don't do anything here as it's possible that aWorkerPrivate has been
// deleted.
}
};
class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
nsString mScriptURL;
UniquePtr<SerializedStackHolder> mOriginStack;
public:
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
UniquePtr<SerializedStackHolder> aOriginStack,
const nsAString& aScriptURL)
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
mScriptURL(aScriptURL),
mOriginStack(aOriginStack.release()) {}
private:
// We can't implement PreRun effectively, because at the point when that would
// run we have not yet done our load so don't know things like our final
// principal and whatnot.
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnWorkerThread();
WorkerGlobalScope* globalScope =
aWorkerPrivate->GetOrCreateGlobalScope(aCx);
if (NS_WARN_IF(!globalScope)) {
return false;
}
if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
return false;
}
ErrorResult rv;
workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack),
mScriptURL, WorkerScript, rv);
rv.WouldReportJSException();
// Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
// return false and don't SetWorkerScriptExecutedSuccessfully() in that
// case, but don't throw anything on aCx. The idea is to not dispatch error
// events if our load is canceled with that error code.
if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
rv.SuppressException();
return false;
}
// Make sure to propagate exceptions from rv onto aCx, so that they will get
// reported after we return. We want to propagate just JS exceptions,
// because all the other errors are handled when the script is loaded.
if (rv.Failed() && !rv.IsJSException()) {
WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
aWorkerPrivate);
rv.SuppressException();
return false;
}
// This is a little dumb, but aCx is in the null realm here because we
// set it up that way in our Run(), since we had not created the global at
// that point yet. So we need to enter the realm of our global,
// because setting a pending exception on aCx involves wrapping into its
// current compartment. Luckily we have a global now.
JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
if (rv.MaybeSetPendingException(aCx)) {
// In the event of an uncaught exception, the worker should still keep
// running (return true) but should not be marked as having executed
// successfully (which will cause ServiceWorker installation to fail).
// In previous error handling cases in this method, we return false (to
// trigger CloseInternal) because the global is not in an operable
// state at all.
//
// For ServiceWorkers, this would correspond to the "Run Service Worker"
// algorithm returning an "abrupt completion" and _not_ failure.
//
// For DedicatedWorkers and SharedWorkers, this would correspond to the
// "run a worker" algorithm disregarding the return value of "run the
// classic script"/"run the module script" in step 24:
//
// "If script is a classic script, then run the classic script script.
// Otherwise, it is a module script; run the module script script."
return true;
}
aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
return true;
}
void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {
if (!aRunResult) {
aWorkerPrivate->CloseInternal();
}
WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
}
};
class NotifyRunnable final : public WorkerControlRunnable {
WorkerStatus mStatus;
public:
NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mStatus(aStatus) {
MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
aStatus == Killing);
}
private:
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnParentThread();
return aWorkerPrivate->ModifyBusyCount(true);
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
aWorkerPrivate->AssertIsOnParentThread();
if (!aDispatchResult) {
// We couldn't dispatch to the worker, which means it's already dead.
// Undo the busy count modification.
aWorkerPrivate->ModifyBusyCount(false);
}
}
virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {
aWorkerPrivate->ModifyBusyCountFromWorker(false);
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->NotifyInternal(mStatus);
}
};
class FreezeRunnable final : public WorkerControlRunnable {
public:
explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->FreezeInternal();
}
};
class ThawRunnable final : public WorkerControlRunnable {
public:
explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->ThawInternal();
}
};
class PropagateStorageAccessPermissionGrantedRunnable final
: public WorkerControlRunnable {
public:
explicit PropagateStorageAccessPermissionGrantedRunnable(
WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
private:
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->PropagateStorageAccessPermissionGrantedInternal();
return true;
}
};
class ReportErrorToConsoleRunnable final : public WorkerRunnable {
const char* mMessage;
const nsTArray<nsString> mParams;
public:
// aWorkerPrivate is the worker thread we're on (or the main thread, if null)
static void Report(WorkerPrivate* aWorkerPrivate, const char* aMessage,
const nsTArray<nsString>& aParams) {
if (aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
} else {
AssertIsOnMainThread();
}
// Now fire a runnable to do the same on the parent's thread if we can.
if (aWorkerPrivate) {
RefPtr<ReportErrorToConsoleRunnable> runnable =
new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage, aParams);
runnable->Dispatch();
return;
}
// Log a warning to the console.
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
nullptr, nsContentUtils::eDOM_PROPERTIES,
aMessage, aParams);
}
private:
ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate,
const char* aMessage,
const nsTArray<nsString>& aParams)
: WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
mMessage(aMessage),
mParams(aParams.Clone()) {}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
aWorkerPrivate->AssertIsOnWorkerThread();
// Dispatch may fail if the worker was canceled, no need to report that as
// an error, so don't call base class PostDispatch.
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
WorkerPrivate* parent = aWorkerPrivate->GetParent();
MOZ_ASSERT_IF(!parent, NS_IsMainThread());
Report(parent, mMessage, mParams);
return true;
}
};
class TimerRunnable final : public WorkerRunnable,
public nsITimerCallback,
public nsINamed {
public:
NS_DECL_ISUPPORTS_INHERITED
explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
private:
~TimerRunnable() = default;
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
// Silence bad assertions.
return true;
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
// Silence bad assertions.
}
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until worker runnables are generally
// MOZ_CAN_RUN_SCRIPT.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->RunExpiredTimeouts(aCx);
}
NS_IMETHOD
Notify(nsITimer* aTimer) override { return Run(); }
NS_IMETHOD
GetName(nsACString& aName) override {
aName.AssignLiteral("TimerRunnable");
return NS_OK;
}
};
NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback,
nsINamed)
class DebuggerImmediateRunnable : public WorkerRunnable {
RefPtr<dom::Function> mHandler;
public:
explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
dom::Function& aHandler)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mHandler(&aHandler) {}
private:
virtual bool IsDebuggerRunnable() const override { return true; }
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
// Silence bad assertions.
return true;
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
// Silence bad assertions.
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JS::Value> callable(
aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
JS::HandleValueArray args = JS::HandleValueArray::empty();
JS::Rooted<JS::Value> rval(aCx);
if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
// Just return false; WorkerRunnable::Run will report the exception.
return false;
}
return true;
}
};
void PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure) {
auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
false /* shrinking */,
false /* collect children */);
}
void IdleGCTimerCallback(nsITimer* aTimer, void* aClosure) {
auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
true /* shrinking */,
false /* collect children */);
}
class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
JS::ContextOptions mContextOptions;
public:
UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
const JS::ContextOptions& aContextOptions)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mContextOptions(aContextOptions) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
return true;
}
};
class UpdateLanguagesRunnable final : public WorkerRunnable {
nsTArray<nsString> mLanguages;
public:
UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
const nsTArray<nsString>& aLanguages)
: WorkerRunnable(aWorkerPrivate), mLanguages(aLanguages.Clone()) {}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
return true;
}
};
class UpdateJSWorkerMemoryParameterRunnable final
: public WorkerControlRunnable {
Maybe<uint32_t> mValue;
JSGCParamKey mKey;
public:
UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
JSGCParamKey aKey,
Maybe<uint32_t> aValue)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mValue(aValue),
mKey(aKey) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
return true;
}
};
#ifdef JS_GC_ZEAL
class UpdateGCZealRunnable final : public WorkerControlRunnable {
uint8_t mGCZeal;
uint32_t mFrequency;
public:
UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal,
uint32_t aFrequency)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mGCZeal(aGCZeal),
mFrequency(aFrequency) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
return true;
}
};
#endif
class SetLowMemoryStateRunnable final : public WorkerControlRunnable {
bool mState;
public:
SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mState(aState) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->SetLowMemoryStateInternal(aCx, mState);
return true;
}
};
class GarbageCollectRunnable final : public WorkerControlRunnable {
bool mShrinking;
bool mCollectChildren;
public:
GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
bool aCollectChildren)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mShrinking(aShrinking),
mCollectChildren(aCollectChildren) {}
private:
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
// Silence bad assertions, this can be dispatched from either the main
// thread or the timer thread..
return true;
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
// Silence bad assertions, this can be dispatched from either the main
// thread or the timer thread..
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
return true;
}
};
class CycleCollectRunnable : public WorkerControlRunnable {
bool mCollectChildren;
public:
CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mCollectChildren(aCollectChildren) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->CycleCollectInternal(mCollectChildren);
return true;
}
};
class OfflineStatusChangeRunnable : public WorkerRunnable {
public:
OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
: WorkerRunnable(aWorkerPrivate), mIsOffline(aIsOffline) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
return true;
}
private:
bool mIsOffline;
};
class MemoryPressureRunnable : public WorkerControlRunnable {
public:
explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->MemoryPressureInternal();
return true;
}
};
#ifdef DEBUG
static bool StartsWithExplicit(nsACString& s) {
return StringBeginsWith(s, "explicit/"_ns);
}
#endif
PRThread* PRThreadFromThread(nsIThread* aThread) {
MOZ_ASSERT(aThread);
PRThread* result;
MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
MOZ_ASSERT(result);
return result;
}
// A runnable to cancel the worker from the parent thread when self.close() is
// called. This runnable is executed on the parent process in order to cancel
// the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be
// sure that all the pending WorkerDebuggeeRunnables are executed before this.
class CancelingOnParentRunnable final : public WorkerDebuggeeRunnable {
public:
explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerDebuggeeRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {
}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->Cancel();
return true;
}
};
// A runnable to cancel the worker from the parent process.
class CancelingWithTimeoutOnParentRunnable final
: public WorkerControlRunnable {
public:
explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnParentThread();
aWorkerPrivate->StartCancelingTimer();
return true;
}
};
class CancelingTimerCallback final : public nsITimerCallback {
public:
NS_DECL_ISUPPORTS
explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
: mWorkerPrivate(aWorkerPrivate) {}
NS_IMETHOD
Notify(nsITimer* aTimer) override {
mWorkerPrivate->AssertIsOnParentThread();
mWorkerPrivate->Cancel();
return NS_OK;
}
private:
~CancelingTimerCallback() = default;
// Raw pointer here is OK because the timer is canceled during the shutdown
// steps.
WorkerPrivate* mWorkerPrivate;
};
NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)
// This runnable starts the canceling of a worker after a self.close().
class CancelingRunnable final : public Runnable {
public:
CancelingRunnable() : Runnable("CancelingRunnable") {}
NS_IMETHOD
Run() override {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
// Now we can cancel the this worker from the parent process.
RefPtr<CancelingOnParentRunnable> r =
new CancelingOnParentRunnable(workerPrivate);
r->Dispatch();
return NS_OK;
}
};
} /* anonymous namespace */
nsString ComputeWorkerPrivateId() {
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsID uuid;
rv = uuidGenerator->GenerateUUIDInPlace(&uuid);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return NSID_TrimBracketsUTF16(uuid);
}
class WorkerPrivate::EventTarget final : public nsISerialEventTarget {
// This mutex protects mWorkerPrivate and must be acquired *before* the
// WorkerPrivate's mutex whenever they must both be held.
mozilla::Mutex mMutex;
WorkerPrivate* mWorkerPrivate;
nsIEventTarget* mWeakNestedEventTarget;
nsCOMPtr<nsIEventTarget> mNestedEventTarget;
public:
explicit EventTarget(WorkerPrivate* aWorkerPrivate)
: mMutex("WorkerPrivate::EventTarget::mMutex"),
mWorkerPrivate(aWorkerPrivate),
mWeakNestedEventTarget(nullptr) {
MOZ_ASSERT(aWorkerPrivate);
}
EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
: mMutex("WorkerPrivate::EventTarget::mMutex"),
mWorkerPrivate(aWorkerPrivate),
mWeakNestedEventTarget(aNestedEventTarget),
mNestedEventTarget(aNestedEventTarget) {
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aNestedEventTarget);
}
void Disable() {
nsCOMPtr<nsIEventTarget> nestedEventTarget;
{
MutexAutoLock lock(mMutex);
// Note, Disable() can be called more than once safely.
mWorkerPrivate = nullptr;
mNestedEventTarget.swap(nestedEventTarget);
}
}
nsIEventTarget* GetWeakNestedEventTarget() const {
MOZ_ASSERT(mWeakNestedEventTarget);
return mWeakNestedEventTarget;
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIEVENTTARGET_FULL
private:
~EventTarget() = default;
};
struct WorkerPrivate::TimeoutInfo {
TimeoutInfo()
: mId(0),
mNestingLevel(0),
mIsInterval(false),
mCanceled(false),
mOnChromeWorker(false) {
MOZ_COUNT_CTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
}
~TimeoutInfo() { MOZ_COUNT_DTOR(mozilla::dom::WorkerPrivate::TimeoutInfo); }
bool operator==(const TimeoutInfo& aOther) {
return mTargetTime == aOther.mTargetTime;
}
bool operator<(const TimeoutInfo& aOther) {
return mTargetTime < aOther.mTargetTime;
}
void AccumulateNestingLevel(const uint32_t& aBaseLevel) {
if (aBaseLevel < kClampTimeoutNestingLevel) {
mNestingLevel = aBaseLevel + 1;
return;
}
mNestingLevel = kClampTimeoutNestingLevel;
}
void CalculateTargetTime() {
auto target = mInterval;
// Don't clamp timeout for chrome workers
if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) {
target = TimeDuration::Max(
mInterval,
TimeDuration::FromMilliseconds(StaticPrefs::dom_min_timeout_value()));
}
mTargetTime = TimeStamp::Now() + target;
}
RefPtr<TimeoutHandler> mHandler;
mozilla::TimeStamp mTargetTime;
mozilla::TimeDuration mInterval;
int32_t mId;
uint32_t mNestingLevel;
bool mIsInterval;
bool mCanceled;
bool mOnChromeWorker;
};
class WorkerJSContextStats final : public JS::RuntimeStats {
const nsCString mRtPath;
public:
explicit WorkerJSContextStats(const nsACString& aRtPath)
: JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) {}
~WorkerJSContextStats() {
for (size_t i = 0; i != zoneStatsVector.length(); i++) {
delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
}
for (size_t i = 0; i != realmStatsVector.length(); i++) {
delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
}
}
const nsCString& Path() const { return mRtPath; }
virtual void initExtraZoneStats(JS::Zone* aZone, JS::ZoneStats* aZoneStats,
const JS::AutoRequireNoGC& nogc) override {
MOZ_ASSERT(!aZoneStats->extra);
// ReportJSRuntimeExplicitTreeStats expects that
// aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
extras->pathPrefix = mRtPath;
extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)aZone);
MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
aZoneStats->extra = extras;
}
virtual void initExtraRealmStats(JS::Realm* aRealm,
JS::RealmStats* aRealmStats,
const JS::AutoRequireNoGC& nogc) override {
MOZ_ASSERT(!aRealmStats->extra);
// ReportJSRuntimeExplicitTreeStats expects that
// aRealmStats->extra is a xpc::RealmStatsExtras pointer.
xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
// This is the |jsPathPrefix|. Each worker has exactly one realm.
extras->jsPathPrefix.Assign(mRtPath);
extras->jsPathPrefix +=
nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(aRealm));
extras->jsPathPrefix += "realm(web-worker)/"_ns;
// This should never be used when reporting with workers (hence the "?!").
extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
extras->location = nullptr;
aRealmStats->extra = extras;
}
};
class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter {
NS_DECL_THREADSAFE_ISUPPORTS
friend class WorkerPrivate;
SharedMutex mMutex;
WorkerPrivate* mWorkerPrivate;
public:
explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
: mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
}
NS_IMETHOD
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) override;
private:
class FinishCollectRunnable;
class CollectReportsRunnable final : public MainThreadWorkerControlRunnable {
RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
const bool mAnonymize;
public:
CollectReportsRunnable(WorkerPrivate* aWorkerPrivate,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandlerData, bool aAnonymize,
const nsACString& aPath);
private:
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
~CollectReportsRunnable() {
if (NS_IsMainThread()) {
mFinishCollectRunnable->Run();
return;
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThreadForMessaging(
mFinishCollectRunnable.forget()));
}
};
class FinishCollectRunnable final : public Runnable {
nsCOMPtr<nsIHandleReportCallback> mHandleReport;
nsCOMPtr<nsISupports> mHandlerData;
size_t mPerformanceUserEntries;
size_t mPerformanceResourceEntries;
const bool mAnonymize;
bool mSuccess;
public:
WorkerJSContextStats mCxStats;
explicit FinishCollectRunnable(nsIHandleReportCallback* aHandleReport,
nsISupports* aHandlerData, bool aAnonymize,
const nsACString& aPath);
NS_IMETHOD Run() override;
void SetPerformanceSizes(size_t userEntries, size_t resourceEntries) {
mPerformanceUserEntries = userEntries;
mPerformanceResourceEntries = resourceEntries;
}
void SetSuccess(bool success) { mSuccess = success; }
private:
~FinishCollectRunnable() {
// mHandleReport and mHandlerData are released on the main thread.
AssertIsOnMainThread();
}
FinishCollectRunnable(const FinishCollectRunnable&) = delete;
FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
};
~MemoryReporter() = default;
void Disable() {
// Called from WorkerPrivate::DisableMemoryReporter.
mMutex.AssertCurrentThreadOwns();
NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
mWorkerPrivate = nullptr;
}
};
NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
NS_IMETHODIMP
WorkerPrivate::MemoryReporter::CollectReports(
nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) {
AssertIsOnMainThread();
RefPtr<CollectReportsRunnable> runnable;
{
MutexAutoLock lock(mMutex);
if (!mWorkerPrivate) {
// This will effectively report 0 memory.
nsCOMPtr<nsIMemoryReporterManager> manager =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (manager) {
manager->EndReport();
}
return NS_OK;
}
nsAutoCString path;
path.AppendLiteral("explicit/workers/workers(");
if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
} else {
nsAutoCString escapedDomain(mWorkerPrivate->Domain());
if (escapedDomain.IsEmpty()) {
escapedDomain += "chrome";
} else {
escapedDomain.ReplaceChar('/', '\\');
}
path.Append(escapedDomain);
path.AppendLiteral(")/worker(");
NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
escapedURL.ReplaceChar('/', '\\');
path.Append(escapedURL);
}
path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
runnable = new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData,
aAnonymize, path);
}
if (!runnable->Dispatch()) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport,
nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath)
: MainThreadWorkerControlRunnable(aWorkerPrivate),
mFinishCollectRunnable(new FinishCollectRunnable(
aHandleReport, aHandlerData, aAnonymize, aPath)),
mAnonymize(aAnonymize) {}
bool WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(
JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
RefPtr<Performance> performance =
scope ? scope->GetPerformanceIfExists() : nullptr;
if (performance) {
size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
size_t resourceEntries =
performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
}
mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats(
&mFinishCollectRunnable->mCxStats, mAnonymize));
return true;
}
WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData,
bool aAnonymize, const nsACString& aPath)
: mozilla::Runnable(
"dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable"),
mHandleReport(aHandleReport),
mHandlerData(aHandlerData),
mPerformanceUserEntries(0),
mPerformanceResourceEntries(0),
mAnonymize(aAnonymize),
mSuccess(false),
mCxStats(aPath) {}
NS_IMETHODIMP
WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run() {
AssertIsOnMainThread();
nsCOMPtr<nsIMemoryReporterManager> manager =
do_GetService("@mozilla.org/memory-reporter-manager;1");
if (!manager) return NS_OK;
if (mSuccess) {
xpc::ReportJSRuntimeExplicitTreeStats(
mCxStats, mCxStats.Path(), mHandleReport, mHandlerData, mAnonymize);
if (mPerformanceUserEntries) {
nsCString path = mCxStats.Path();
path.AppendLiteral("dom/performance/user-entries");
mHandleReport->Callback(
""_ns, path, nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES, mPerformanceUserEntries,
"Memory used for performance user entries."_ns, mHandlerData);
}
if (mPerformanceResourceEntries) {
nsCString path = mCxStats.Path();
path.AppendLiteral("dom/performance/resource-entries");
mHandleReport->Callback(
""_ns, path, nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES, mPerformanceResourceEntries,
"Memory used for performance resource entries."_ns, mHandlerData);
}
}
manager->EndReport();
return NS_OK;
}
WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
: mEventTarget(aEventTarget),
mCompleted(false),
mResult(false)
#ifdef DEBUG
,
mHasRun(false)
#endif
{
}
Document* WorkerPrivate::GetDocument() const {
AssertIsOnMainThread();
if (mLoadInfo.mWindow) {
return mLoadInfo.mWindow->GetExtantDoc();
}
// if we don't have a document, we should query the document
// from the parent in case of a nested worker
WorkerPrivate* parent = mParent;
while (parent) {
if (parent->mLoadInfo.mWindow) {
return parent->mLoadInfo.mWindow->GetExtantDoc();
}
parent = parent->GetParent();
}
// couldn't query a document, give up and return nullptr
return nullptr;
}
void WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP) {
AssertIsOnMainThread();
if (!aCSP) {
return;
}
aCSP->EnsureEventTarget(mMainThreadEventTarget);
mLoadInfo.mCSP = aCSP;
mLoadInfo.mCSPInfo = MakeUnique<CSPInfo>();
nsresult rv = CSPToCSPInfo(mLoadInfo.mCSP, mLoadInfo.mCSPInfo.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
}
nsresult WorkerPrivate::SetCSPFromHeaderValues(
const nsACString& aCSPHeaderValue,
const nsACString& aCSPReportOnlyHeaderValue) {
AssertIsOnMainThread();
MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
nsresult rv;
nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext();
// First, we try to query the URI from the Principal, but
// in case selfURI remains empty (e.g in case the Principal
// is a SystemPrincipal) then we fall back and use the
// base URI as selfURI for CSP.
nsCOMPtr<nsIURI> selfURI;
// Its not recommended to use the BasePrincipal to get the URI
// but in this case we need to make an exception
auto* basePrin = BasePrincipal::Cast(mLoadInfo.mPrincipal);
if (basePrin) {
basePrin->GetURI(getter_AddRefs(selfURI));
}
if (!selfURI) {
selfURI = mLoadInfo.mBaseURI;
}
MOZ_ASSERT(selfURI, "need a self URI for CSP");
rv = csp->SetRequestContextWithPrincipal(mLoadInfo.mPrincipal, selfURI,
u""_ns, 0);
NS_ENSURE_SUCCESS(rv, rv);
csp->EnsureEventTarget(mMainThreadEventTarget);
// If there's a CSP header, apply it.
if (!cspHeaderValue.IsEmpty()) {
rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
NS_ENSURE_SUCCESS(rv, rv);
}
// If there's a report-only CSP header, apply it.
if (!cspROHeaderValue.IsEmpty()) {
rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
NS_ENSURE_SUCCESS(rv, rv);
}
// Set evalAllowed, default value is set in GetAllowsEval
bool evalAllowed = false;
bool reportEvalViolations = false;
rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
NS_ENSURE_SUCCESS(rv, rv);
mLoadInfo.mCSP = csp;
mLoadInfo.mEvalAllowed = evalAllowed;
mLoadInfo.mReportCSPViolations = reportEvalViolations;
mLoadInfo.mCSPInfo = MakeUnique<CSPInfo>();
rv = CSPToCSPInfo(csp, mLoadInfo.mCSPInfo.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void WorkerPrivate::StoreCSPOnClient() {
auto data = mWorkerThreadAccessible.Access();
MOZ_ASSERT(data->mScope);
if (mLoadInfo.mCSPInfo) {
data->mScope->MutableClientSourceRef().SetCspInfo(*mLoadInfo.mCSPInfo);
}
}
void WorkerPrivate::UpdateReferrerInfoFromHeader(
const nsACString& aReferrerPolicyHeaderValue) {
NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
if (headerValue.IsEmpty()) {
return;
}
ReferrerPolicy policy =
ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue);
if (policy == ReferrerPolicy::_empty) {
return;
}
nsCOMPtr<nsIReferrerInfo> referrerInfo =
static_cast<ReferrerInfo*>(GetReferrerInfo())->CloneWithNewPolicy(policy);
SetReferrerInfo(referrerInfo);
}
void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
AssertIsOnParentThread();
// The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
// Worker object, which is really held by the worker thread. We traverse this
// reference if and only if our busy count is zero and we have not released
// the main thread reference. We do not unlink it. This allows the CC to
// break cycles involving the Worker and begin shutting it down (which does
// happen in unlink) but ensures that the WorkerPrivate won't be deleted
// before we're done shutting down the thread.
if (!mBusyCount && !mMainThreadObjectsForgotten) {
nsCycleCollectionTraversalCallback& cb = aCb;
WorkerPrivate* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
}
}
nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
nsIEventTarget* aSyncLoopTarget) {
// May be called on any thread!
MutexAutoLock lock(mMutex);
return DispatchLockHeld(std::move(aRunnable), aSyncLoopTarget, lock);
}
nsresult WorkerPrivate::DispatchLockHeld(
already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget,
const MutexAutoLock& aProofOfLock) {
// May be called on any thread!
RefPtr<WorkerRunnable> runnable(aRunnable);
MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
NS_WARNING(
"A runnable was posted to a worker that is already shutting "
"down!");
return NS_ERROR_UNEXPECTED;
}
if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
mDelayedDebuggeeRunnables.AppendElement(runnable);
return NS_OK;
}
if (!mThread) {
if (ParentStatus() == Pending || mStatus == Pending) {
mPreStartRunnables.AppendElement(runnable);
return NS_OK;
}
NS_WARNING(
"Using a worker event target after the thread has already"
"been released!");
return NS_ERROR_UNEXPECTED;
}
nsresult rv;
if (aSyncLoopTarget) {
rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
} else {
// WorkerDebuggeeRunnables don't need any special treatment here. True,