--- a/caps/nsJSPrincipals.cpp
+++ b/caps/nsJSPrincipals.cpp
@@ -10,18 +10,23 @@
#include "nsJSPrincipals.h"
#include "plstr.h"
#include "nsXPIDLString.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsStringBuffer.h"
+#include "mozilla/dom/StructuredCloneTags.h"
// for mozilla::dom::workers::kJSPrincipalsDebugToken
#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
NS_IMETHODIMP_(MozExternalRefCountType)
nsJSPrincipals::AddRef()
{
MOZ_ASSERT(NS_IsMainThread());
NS_PRECONDITION(int32_t(refcount) >= 0, "illegal refcnt");
nsrefcnt count = ++refcount;
NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
@@ -80,19 +85,119 @@ nsJSPrincipals::Destroy(JSPrincipals *js
// Defined here so one can do principals->dump() in the debugger
JS_EXPORT_API(void)
JSPrincipals::dump()
{
if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
nsAutoCString str;
static_cast<nsJSPrincipals *>(this)->GetScriptLocation(str);
fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this), str.get());
- } else if (debugToken == mozilla::dom::workers::kJSPrincipalsDebugToken) {
+ } else if (debugToken == dom::workers::kJSPrincipalsDebugToken) {
fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
} else {
fprintf(stderr,
"!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
"actual=0x%x expected=0x%x\n",
this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
}
}
-#endif
+#endif
+
+/* static */ bool
+nsJSPrincipals::ReadPrincipals(JSContext* aCx, JSStructuredCloneReader* aReader,
+ JSPrincipals** aOutPrincipals)
+{
+ uint32_t tag;
+ uint32_t unused;
+ if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
+ return false;
+ }
+
+ if (!(tag == SCTAG_DOM_NULL_PRINCIPAL ||
+ tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+ tag == SCTAG_DOM_CONTENT_PRINCIPAL)) {
+ xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ return false;
+ }
+
+ return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
+}
+
+/* static */ bool
+nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ JSPrincipals** aOutPrincipals)
+{
+ MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
+ aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+ aTag == SCTAG_DOM_CONTENT_PRINCIPAL);
+
+ if (NS_WARN_IF(!NS_IsMainThread())) {
+ xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
+ return false;
+ }
+
+ PrincipalInfo info;
+ if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
+ info = SystemPrincipalInfo();
+ } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
+ info = NullPrincipalInfo();
+ } else {
+ uint32_t suffixLength, specLength;
+ if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
+ return false;
+ }
+
+ nsAutoCString suffix;
+ suffix.SetLength(suffixLength);
+ if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
+ return false;
+ }
+
+ nsAutoCString spec;
+ spec.SetLength(specLength);
+ if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
+ return false;
+ }
+
+ OriginAttributes attrs;
+ attrs.PopulateFromSuffix(suffix);
+ info = ContentPrincipalInfo(attrs, spec);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> prin = PrincipalInfoToPrincipal(info, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ return false;
+ }
+
+ *aOutPrincipals = get(prin.forget().take());
+ return true;
+}
+
+bool
+nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter)
+{
+ PrincipalInfo info;
+ if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
+ xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ return false;
+ }
+
+ if (info.type() == PrincipalInfo::TNullPrincipalInfo) {
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
+ }
+ if (info.type() == PrincipalInfo::TSystemPrincipalInfo) {
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
+ }
+
+ MOZ_ASSERT(info.type() == PrincipalInfo::TContentPrincipalInfo);
+ const ContentPrincipalInfo& cInfo = info;
+ nsAutoCString suffix;
+ cInfo.attrs().CreateSuffix(suffix);
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
+ JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
+ JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
+ JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+}
--- a/caps/nsJSPrincipals.h
+++ b/caps/nsJSPrincipals.h
@@ -11,16 +11,27 @@
class nsJSPrincipals : public nsIPrincipal, public JSPrincipals
{
public:
/* SpiderMonkey security callbacks. */
static bool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
static void Destroy(JSPrincipals *jsprin);
+ /* JSReadPrincipalsOp for nsJSPrincipals */
+ static bool ReadPrincipals(JSContext* aCx, JSStructuredCloneReader* aReader,
+ JSPrincipals** aOutPrincipals);
+
+ static bool ReadKnownPrincipalType(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ JSPrincipals** aOutPrincipals);
+
+ bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) final;
+
/*
* Get a weak reference to nsIPrincipal associated with the given JS
* principal, and vice-versa.
*/
static nsJSPrincipals* get(JSPrincipals *principals) {
nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principals);
MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
return self;
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -413,59 +413,29 @@ StructuredCloneHolder::ReadFullySerializ
}
}
return result;
}
if (aTag == SCTAG_DOM_NULL_PRINCIPAL ||
aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
- if (!NS_IsMainThread()) {
+ JSPrincipals* prin;
+ if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) {
return nullptr;
}
-
- mozilla::ipc::PrincipalInfo info;
- if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
- info = mozilla::ipc::SystemPrincipalInfo();
- } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
- info = mozilla::ipc::NullPrincipalInfo();
- } else {
-
- uint32_t suffixLength, specLength;
- if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
- return nullptr;
- }
-
- nsAutoCString suffix;
- suffix.SetLength(suffixLength);
- if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
- return nullptr;
- }
-
- nsAutoCString spec;
- spec.SetLength(specLength);
- if (!JS_ReadBytes(aReader, spec.BeginWriting(), specLength)) {
- return nullptr;
- }
-
- OriginAttributes attrs;
- attrs.PopulateFromSuffix(suffix);
- info = mozilla::ipc::ContentPrincipalInfo(attrs, spec);
- }
-
- nsresult rv;
- nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(info, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
- return nullptr;
- }
+ // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of the
+ // casting between JSPrincipals* and nsIPrincipal* we can't use
+ // getter_AddRefs above and have to already_AddRefed here.
+ nsCOMPtr<nsIPrincipal> principal = already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
JS::RootedValue result(aCx);
- rv = nsContentUtils::WrapNative(aCx, principal, &NS_GET_IID(nsIPrincipal),
- &result);
+ nsresult rv = nsContentUtils::WrapNative(aCx, principal,
+ &NS_GET_IID(nsIPrincipal),
+ &result);
if (NS_FAILED(rv)) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return nullptr;
}
return result.toObjectOrNull();
}
@@ -555,37 +525,18 @@ StructuredCloneHolder::WriteFullySeriali
}
}
#endif
if (NS_IsMainThread() && xpc::IsReflector(aObj)) {
nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(aObj);
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
if (principal) {
- mozilla::ipc::PrincipalInfo info;
- if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
- xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
- return false;
- }
-
- if (info.type() == mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
- return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0);
- }
- if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
- return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
- }
-
- MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
- const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
- nsAutoCString suffix;
- cInfo.attrs().CreateSuffix(suffix);
- return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
- JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
- JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
- JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
+ auto nsjsprincipals = nsJSPrincipals::get(principal);
+ return nsjsprincipals->write(aCx, aWriter);
}
}
#ifdef MOZ_NFC
{
MozNDEFRecord* ndefRecord;
if (NS_SUCCEEDED(UNWRAP_OBJECT(MozNDEFRecord, aObj, ndefRecord))) {
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/workers/Principal.cpp
+++ b/dom/workers/Principal.cpp
@@ -6,20 +6,28 @@
#include "Principal.h"
#include "jsapi.h"
#include "mozilla/Assertions.h"
BEGIN_WORKERS_NAMESPACE
+struct WorkerPrincipal final : public JSPrincipals
+{
+ bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override {
+ MOZ_CRASH("WorkerPrincipal::write not implemented");
+ return false;
+ }
+};
+
JSPrincipals*
GetWorkerPrincipal()
{
- static JSPrincipals sPrincipal;
+ static WorkerPrincipal sPrincipal;
/*
* To make sure the the principals refcount is initialized to one, atomically
* increment it on every pass though this function. If we discover this wasn't
* the first time, decrement it again. This avoids the need for
* synchronization.
*/
int32_t prevRefcount = sPrincipal.refcount++;
--- a/js/public/Principals.h
+++ b/js/public/Principals.h
@@ -10,16 +10,18 @@
#define js_Principals_h
#include "mozilla/Atomics.h"
#include <stdint.h>
#include "jspubtd.h"
+#include "js/StructuredClone.h"
+
namespace js {
struct PerformanceGroup;
} // namespace js
struct JSPrincipals {
/* Don't call "destroy"; use reference counting macros below. */
mozilla::Atomic<int32_t> refcount;
@@ -32,16 +34,22 @@ struct JSPrincipals {
void setDebugToken(uint32_t token) {
# ifdef JS_DEBUG
debugToken = token;
# endif
}
/*
+ * Write the principals with the given |writer|. Return false on failure,
+ * true on success.
+ */
+ virtual bool write(JSContext* cx, JSStructuredCloneWriter* writer) = 0;
+
+ /*
* This is not defined by the JS engine but should be provided by the
* embedding.
*/
JS_PUBLIC_API(void) dump();
};
extern JS_PUBLIC_API(void)
JS_HoldPrincipals(JSPrincipals* principals);
@@ -94,9 +102,31 @@ typedef void
/*
* Initialize the callback that is called to destroy JSPrincipals instance
* when its reference counter drops to zero. The initialization can be done
* only once per JS runtime.
*/
extern JS_PUBLIC_API(void)
JS_InitDestroyPrincipalsCallback(JSRuntime* rt, JSDestroyPrincipalsOp destroyPrincipals);
+/*
+ * Read a JSPrincipals instance from the given |reader| and initialize the out
+ * paratemer |outPrincipals| to the JSPrincipals instance read.
+ *
+ * Return false on failure, true on success. The |outPrincipals| parameter
+ * should not be modified if false is returned.
+ *
+ * The caller is not responsible for calling JS_HoldPrincipals on the resulting
+ * JSPrincipals instance, the JSReadPrincipalsOp must increment the refcount of
+ * the resulting JSPrincipals on behalf of the caller.
+ */
+using JSReadPrincipalsOp = bool (*)(JSContext* cx, JSStructuredCloneReader* reader,
+ JSPrincipals** outPrincipals);
+
+/*
+ * Initialize the callback that is called to read JSPrincipals instances from a
+ * buffer. The initialization can be done only once per JS runtime.
+ */
+extern JS_PUBLIC_API(void)
+JS_InitReadPrincipalsCallback(JSRuntime* rt, JSReadPrincipalsOp read);
+
+
#endif /* js_Principals_h */
--- a/js/src/jsapi-tests/testCloneScript.cpp
+++ b/js/src/jsapi-tests/testCloneScript.cpp
@@ -46,29 +46,28 @@ BEGIN_TEST(test_cloneScript)
JSAutoCompartment b(cx, B);
CHECK(JS::CloneFunctionObject(cx, obj));
}
return true;
}
END_TEST(test_cloneScript)
-static void
-DestroyPrincipals(JSPrincipals* principals)
-{
- delete principals;
-}
-
-struct Principals : public JSPrincipals
+struct Principals final : public JSPrincipals
{
public:
Principals()
{
refcount = 0;
}
+
+ bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+ MOZ_ASSERT(false, "not imlemented");
+ return false;
+ }
};
class AutoDropPrincipals
{
JSRuntime* rt;
JSPrincipals* principals;
public:
@@ -79,16 +78,23 @@ class AutoDropPrincipals
}
~AutoDropPrincipals()
{
JS_DropPrincipals(rt, principals);
}
};
+static void
+DestroyPrincipals(JSPrincipals* principals)
+{
+ auto p = static_cast<Principals*>(principals);
+ delete p;
+}
+
BEGIN_TEST(test_cloneScriptWithPrincipals)
{
JS_InitDestroyPrincipalsCallback(rt, DestroyPrincipals);
JSPrincipals* principalsA = new Principals();
AutoDropPrincipals dropA(rt, principalsA);
JSPrincipals* principalsB = new Principals();
AutoDropPrincipals dropB(rt, principalsB);
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -404,16 +404,21 @@ class TempFile {
class TestJSPrincipals : public JSPrincipals
{
public:
explicit TestJSPrincipals(int rc = 0)
: JSPrincipals()
{
refcount = rc;
}
+
+ bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+ MOZ_ASSERT(false, "not implemented");
+ return false;
+ }
};
#ifdef JS_GC_ZEAL
/*
* Temporarily disable the GC zeal setting. This is only useful in tests that
* need very explicit GC behavior and should not be used elsewhere.
*/
class AutoLeaveZeal
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3333,16 +3333,24 @@ JS_SetTrustedPrincipals(JSRuntime* rt, J
extern JS_PUBLIC_API(void)
JS_InitDestroyPrincipalsCallback(JSRuntime* rt, JSDestroyPrincipalsOp destroyPrincipals)
{
MOZ_ASSERT(destroyPrincipals);
MOZ_ASSERT(!rt->destroyPrincipals);
rt->destroyPrincipals = destroyPrincipals;
}
+extern JS_PUBLIC_API(void)
+JS_InitReadPrincipalsCallback(JSRuntime* rt, JSReadPrincipalsOp read)
+{
+ MOZ_ASSERT(read);
+ MOZ_ASSERT(!rt->readPrincipals);
+ rt->readPrincipals = read;
+}
+
JS_PUBLIC_API(JSFunction*)
JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
const char* name)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -209,30 +209,35 @@ NewGlobalObject(JSContext* cx, JS::Compa
* set bits in P are a superset of those in Q. Thus, the principal 0 is
* subsumed by everything, and the principal ~0 subsumes everything.
*
* As a special case, a null pointer as a principal is treated like 0xffff.
*
* The 'newGlobal' function takes an option indicating which principal the
* new global should have; 'evaluate' does for the new code.
*/
-class ShellPrincipals: public JSPrincipals {
+class ShellPrincipals final : public JSPrincipals {
uint32_t bits;
static uint32_t getBits(JSPrincipals* p) {
if (!p)
return 0xffff;
return static_cast<ShellPrincipals*>(p)->bits;
}
public:
explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) {
this->refcount = refcount;
}
+ bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+ MOZ_ASSERT(false, "not implemented");
+ return false;
+ }
+
static void destroy(JSPrincipals* principals) {
MOZ_ASSERT(principals != &fullyTrusted);
MOZ_ASSERT(principals->refcount == 0);
js_delete(static_cast<const ShellPrincipals*>(principals));
}
static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
uint32_t firstBits = getBits(first);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -191,16 +191,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
data(nullptr),
signalHandlersInstalled_(false),
canUseSignalHandlers_(false),
defaultFreeOp_(thisFromCtor()),
debuggerMutations(0),
securityCallbacks(const_cast<JSSecurityCallbacks*>(&NullSecurityCallbacks)),
DOMcallbacks(nullptr),
destroyPrincipals(nullptr),
+ readPrincipals(nullptr),
errorReporter(nullptr),
linkedAsmJSModules(nullptr),
propertyRemovals(0),
#if !EXPOSE_INTL_API
thousandsSeparator(0),
decimalSeparator(0),
numGrouping(0),
#endif
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1153,16 +1153,17 @@ struct JSRuntime : public JS::shadow::Ru
return &defaultFreeOp_;
}
uint32_t debuggerMutations;
const JSSecurityCallbacks* securityCallbacks;
const js::DOMCallbacks* DOMcallbacks;
JSDestroyPrincipalsOp destroyPrincipals;
+ JSReadPrincipalsOp readPrincipals;
/* Optional error reporter. */
JSErrorReporter errorReporter;
/* AsmJSCache callbacks are runtime-wide. */
JS::AsmJSCacheOps asmJSCacheOps;
/* Head of the linked list of linked asm.js modules. */
--- a/js/src/vm/SavedFrame.h
+++ b/js/src/vm/SavedFrame.h
@@ -190,16 +190,21 @@ struct ReconstructedSavedFramePrincipals
{
explicit ReconstructedSavedFramePrincipals()
: JSPrincipals()
{
MOZ_ASSERT(is(this));
this->refcount = 1;
}
+ bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+ MOZ_ASSERT(false, "ReconstructedSavedFramePrincipals should never be exposed to embedders");
+ return false;
+ }
+
static ReconstructedSavedFramePrincipals IsSystem;
static ReconstructedSavedFramePrincipals IsNotSystem;
// Return true if the given JSPrincipals* points to one of the
// ReconstructedSavedFramePrincipals singletons, false otherwise.
static bool is(JSPrincipals* p) { return p == &IsSystem || p == &IsNotSystem;}
// Get the appropriate ReconstructedSavedFramePrincipals singleton for the