Bug 1752645 [wpt PR 32604] - App history: fix DCHECK crash on navigating from about...
[gecko.git] / caps / nsJSPrincipals.cpp
bloba5d91708c1ed635d1acff51948ecfc06dc4b8159
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsIPrincipal.h"
7 #include "xpcpublic.h"
8 #include "nsString.h"
9 #include "nsJSPrincipals.h"
10 #include "plstr.h"
11 #include "nsCOMPtr.h"
12 #include "nsMemory.h"
13 #include "nsStringBuffer.h"
14 #include "mozilla/BasePrincipal.h"
15 #include "mozilla/StaticPtr.h"
16 #include "mozilla/dom/StructuredCloneTags.h"
17 // for mozilla::dom::workerinternals::kJSPrincipalsDebugToken
18 #include "mozilla/dom/workerinternals/JSSettings.h"
19 // for mozilla::dom::worklet::kJSPrincipalsDebugToken
20 #include "mozilla/dom/WorkletPrincipals.h"
21 #include "mozilla/ipc/BackgroundUtils.h"
22 #include "mozilla/ipc/PBackgroundSharedTypes.h"
24 using namespace mozilla;
25 using namespace mozilla::dom;
26 using namespace mozilla::ipc;
28 NS_IMETHODIMP_(MozExternalRefCountType)
29 nsJSPrincipals::AddRef() {
30 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
31 MOZ_ASSERT(int32_t(refcount) >= 0, "illegal refcnt");
32 nsrefcnt count = ++refcount;
33 NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
34 return count;
37 NS_IMETHODIMP_(MozExternalRefCountType)
38 nsJSPrincipals::Release() {
39 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
40 MOZ_ASSERT(0 != refcount, "dup release");
41 nsrefcnt count = --refcount;
42 NS_LOG_RELEASE(this, count, "nsJSPrincipals");
43 if (count == 0) {
44 delete this;
47 return count;
50 /* static */
51 bool nsJSPrincipals::Subsume(JSPrincipals* jsprin, JSPrincipals* other) {
52 bool result;
53 nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(
54 nsJSPrincipals::get(other), &result);
55 return NS_SUCCEEDED(rv) && result;
58 /* static */
59 void nsJSPrincipals::Destroy(JSPrincipals* jsprin) {
60 // The JS runtime can call this method during the last GC when
61 // nsScriptSecurityManager is destroyed. So we must not assume here that
62 // the security manager still exists.
64 nsJSPrincipals* nsjsprin = nsJSPrincipals::get(jsprin);
66 // We need to destroy the nsIPrincipal. We'll do this by adding
67 // to the refcount and calling release
69 #ifdef NS_BUILD_REFCNT_LOGGING
70 // The refcount logging considers AddRef-to-1 to indicate creation,
71 // so trick it into thinking it's otherwise, but balance the
72 // Release() we do below.
73 nsjsprin->refcount++;
74 nsjsprin->AddRef();
75 nsjsprin->refcount--;
76 #else
77 nsjsprin->refcount++;
78 #endif
79 nsjsprin->Release();
82 #ifdef DEBUG
84 // Defined here so one can do principals->dump() in the debugger
85 JS_PUBLIC_API void JSPrincipals::dump() {
86 if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
87 nsAutoCString str;
88 nsresult rv = static_cast<nsJSPrincipals*>(this)->GetScriptLocation(str);
89 fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this),
90 NS_SUCCEEDED(rv) ? str.get() : "(unknown)");
91 } else if (debugToken == dom::workerinternals::kJSPrincipalsDebugToken) {
92 fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
93 } else if (debugToken == dom::WorkletPrincipals::kJSPrincipalsDebugToken) {
94 fprintf(stderr, "Web Worklet principal (%p)\n", this);
95 } else {
96 fprintf(stderr,
97 "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
98 "actual=0x%x expected=0x%x\n",
99 this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
103 #endif
105 /* static */
106 bool nsJSPrincipals::ReadPrincipals(JSContext* aCx,
107 JSStructuredCloneReader* aReader,
108 JSPrincipals** aOutPrincipals) {
109 uint32_t tag;
110 uint32_t unused;
111 if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
112 return false;
115 if (!(tag == SCTAG_DOM_NULL_PRINCIPAL || tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
116 tag == SCTAG_DOM_CONTENT_PRINCIPAL ||
117 tag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
118 tag == SCTAG_DOM_WORKER_PRINCIPAL)) {
119 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
120 return false;
123 return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
126 static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
127 OriginAttributes& aAttrs, nsACString& aSpec,
128 nsACString& aOriginNoSuffix,
129 nsACString& aBaseDomain) {
130 uint32_t suffixLength, specLength;
131 if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
132 return false;
135 nsAutoCString suffix;
136 if (!suffix.SetLength(suffixLength, fallible)) {
137 return false;
140 if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
141 return false;
144 if (!aAttrs.PopulateFromSuffix(suffix)) {
145 return false;
148 if (!aSpec.SetLength(specLength, fallible)) {
149 return false;
152 if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
153 return false;
156 uint32_t originNoSuffixLength, dummy;
157 if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) {
158 return false;
161 MOZ_ASSERT(dummy == 0);
162 if (dummy != 0) {
163 return false;
166 if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) {
167 return false;
170 if (!JS_ReadBytes(aReader, aOriginNoSuffix.BeginWriting(),
171 originNoSuffixLength)) {
172 return false;
175 uint32_t baseDomainIsVoid, baseDomainLength;
176 if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) {
177 return false;
180 if (baseDomainIsVoid != 0 && baseDomainIsVoid != 1) {
181 return false;
184 if (baseDomainIsVoid) {
185 if (baseDomainLength != 0) {
186 return false;
189 aBaseDomain.SetIsVoid(true);
190 return true;
193 if (!aBaseDomain.SetLength(baseDomainLength, fallible)) {
194 return false;
197 if (!JS_ReadBytes(aReader, aBaseDomain.BeginWriting(), baseDomainLength)) {
198 return false;
201 return true;
204 static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
205 PrincipalInfo& aInfo) {
206 if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
207 aInfo = SystemPrincipalInfo();
208 } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
209 OriginAttributes attrs;
210 nsAutoCString spec;
211 nsAutoCString originNoSuffix;
212 nsAutoCString baseDomain;
213 if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
214 return false;
216 aInfo = NullPrincipalInfo(attrs, spec);
217 } else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
218 uint32_t length, unused;
219 if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
220 return false;
223 ExpandedPrincipalInfo expanded;
225 for (uint32_t i = 0; i < length; i++) {
226 uint32_t tag;
227 if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
228 return false;
231 PrincipalInfo sub;
232 if (!ReadPrincipalInfo(aReader, tag, sub)) {
233 return false;
235 expanded.allowlist().AppendElement(sub);
238 aInfo = expanded;
239 } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
240 OriginAttributes attrs;
241 nsAutoCString spec;
242 nsAutoCString originNoSuffix;
243 nsAutoCString baseDomain;
244 if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
245 return false;
248 #ifdef FUZZING
249 if (originNoSuffix.IsEmpty()) {
250 return false;
252 #endif
254 MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
256 // XXX: Do we care about mDomain for structured clone?
257 aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
258 baseDomain);
259 } else {
260 #ifdef FUZZING
261 return false;
262 #else
263 MOZ_CRASH("unexpected principal structured clone tag");
264 #endif
267 return true;
270 static StaticRefPtr<nsIPrincipal> sActiveWorkerPrincipal;
272 nsJSPrincipals::AutoSetActiveWorkerPrincipal::AutoSetActiveWorkerPrincipal(
273 nsIPrincipal* aPrincipal) {
274 MOZ_ASSERT(NS_IsMainThread());
275 MOZ_RELEASE_ASSERT(!sActiveWorkerPrincipal);
276 sActiveWorkerPrincipal = aPrincipal;
279 nsJSPrincipals::AutoSetActiveWorkerPrincipal::~AutoSetActiveWorkerPrincipal() {
280 sActiveWorkerPrincipal = nullptr;
283 /* static */
284 bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
285 JSStructuredCloneReader* aReader,
286 uint32_t aTag,
287 JSPrincipals** aOutPrincipals) {
288 MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
289 aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
290 aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
291 aTag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
292 aTag == SCTAG_DOM_WORKER_PRINCIPAL);
294 if (NS_WARN_IF(!NS_IsMainThread())) {
295 xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
296 return false;
299 if (aTag == SCTAG_DOM_WORKER_PRINCIPAL) {
300 // When reading principals which were written on a worker thread, we need to
301 // know the principal of the worker which did the write.
302 if (!sActiveWorkerPrincipal) {
303 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
304 return false;
306 RefPtr<nsJSPrincipals> retval = get(sActiveWorkerPrincipal);
307 retval.forget(aOutPrincipals);
308 return true;
311 PrincipalInfo info;
312 if (!ReadPrincipalInfo(aReader, aTag, info)) {
313 return false;
316 auto principalOrErr = PrincipalInfoToPrincipal(info);
317 if (NS_WARN_IF(principalOrErr.isErr())) {
318 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
319 return false;
322 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
324 *aOutPrincipals = get(principal.forget().take());
325 return true;
328 static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
329 const OriginAttributes& aAttrs,
330 const nsCString& aSpec,
331 const nsCString& aOriginNoSuffix,
332 const nsCString& aBaseDomain) {
333 nsAutoCString suffix;
334 aAttrs.CreateSuffix(suffix);
336 if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
337 JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
338 JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
339 JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) &&
340 JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
341 aOriginNoSuffix.Length()))) {
342 return false;
345 if (aBaseDomain.IsVoid()) {
346 return JS_WriteUint32Pair(aWriter, 1, 0);
349 return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) &&
350 JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length());
353 /* static */
354 bool nsJSPrincipals::WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
355 const PrincipalInfo& aInfo) {
356 if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
357 const NullPrincipalInfo& nullInfo = aInfo;
358 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
359 ::WritePrincipalInfo(aWriter, nullInfo.attrs(), nullInfo.spec(),
360 ""_ns, ""_ns);
362 if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
363 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
365 if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
366 const ExpandedPrincipalInfo& expanded = aInfo;
367 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
368 !JS_WriteUint32Pair(aWriter, expanded.allowlist().Length(), 0)) {
369 return false;
372 for (uint32_t i = 0; i < expanded.allowlist().Length(); i++) {
373 if (!WritePrincipalInfo(aWriter, expanded.allowlist()[i])) {
374 return false;
377 return true;
380 MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
381 const ContentPrincipalInfo& cInfo = aInfo;
382 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
383 ::WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
384 cInfo.originNoSuffix(), cInfo.baseDomain());
387 bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) {
388 PrincipalInfo info;
389 if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
390 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
391 return false;
394 return WritePrincipalInfo(aWriter, info);
397 bool nsJSPrincipals::isSystemOrAddonPrincipal() {
398 JS::AutoSuppressGCAnalysis suppress;
399 return this->IsSystemPrincipal() ||
400 this->GetIsAddonOrExpandedAddonPrincipal();