1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef xpcpublic_h
8 #define xpcpublic_h
9
10 #include <cstddef>
11 #include <cstdint>
12 #include "ErrorList.h"
13 #include "js/BuildId.h"
14 #include "js/ErrorReport.h"
15 #include "js/GCAPI.h"
16 #include "js/Object.h"
17 #include "js/RootingAPI.h"
18 #include "js/String.h"
19 #include "js/TypeDecls.h"
20 #include "js/Utility.h"
21 #include "js/Value.h"
22 #include "jsapi.h"
23 #include "mozilla/AlreadyAddRefed.h"
24 #include "mozilla/Assertions.h"
25 #include "mozilla/Attributes.h"
26 #include "mozilla/Maybe.h"
27 #include "mozilla/MemoryReporting.h"
28 #include "mozilla/dom/DOMString.h"
29 #include "mozilla/fallible.h"
30 #include "nsAtom.h"
31 #include "nsCOMPtr.h"
32 #include "nsISupports.h"
33 #include "nsIURI.h"
34 #include "nsStringBuffer.h"
35 #include "nsStringFwd.h"
36 #include "nsTArray.h"
37 #include "nsWrapperCache.h"
38
39 // XXX only for NukeAllWrappersForRealm, which is only used in
40 // dom/base/WindowDestroyedEvent.cpp outside of js
41 #include "jsfriendapi.h"
42
43 class JSObject;
44 class JSString;
45 class JSTracer;
46 class nsGlobalWindowInner;
47 class nsIAddonInterposition;
48 class nsIGlobalObject;
49 class nsIHandleReportCallback;
50 class nsIPrincipal;
51 class nsPIDOMWindowInner;
52 struct JSContext;
53 struct nsID;
54 struct nsXPTInterfaceInfo;
55
56 namespace JS {
57 class Compartment;
58 class ContextOptions;
59 class Realm;
60 class RealmOptions;
61 class Value;
62 struct RuntimeStats;
63 } // namespace JS
64
65 namespace mozilla {
66 class BasePrincipal;
67
68 namespace dom {
69 class Exception;
70 } // namespace dom
71 } // namespace mozilla
72
73 using xpcGCCallback = void (*)(JSGCStatus);
74
75 namespace xpc {
76
77 class Scriptability {
78 public:
79 explicit Scriptability(JS::Realm* realm);
80 bool Allowed();
81 bool IsImmuneToScriptPolicy();
82
83 void Block();
84 void Unblock();
85 void SetWindowAllowsScript(bool aAllowed);
86
87 static Scriptability& Get(JSObject* aScope);
88
89 private:
90 // Whenever a consumer wishes to prevent script from running on a global,
91 // it increments this value with a call to Block(). When it wishes to
92 // re-enable it (if ever), it decrements this value with a call to Unblock().
93 // Script may not run if this value is non-zero.
94 uint32_t mScriptBlocks;
95
96 // Whether the DOM window allows javascript in this scope. If this scope
97 // doesn't have a window, this value is always true.
98 bool mWindowAllowsScript;
99
100 // Whether this scope is immune to user-defined or addon-defined script
101 // policy.
102 bool mImmuneToScriptPolicy;
103
104 // Whether the new-style domain policy when this compartment was created
105 // forbids script execution.
106 bool mScriptBlockedByPolicy;
107 };
108
109 JSObject* TransplantObject(JSContext* cx, JS::HandleObject origobj,
110 JS::HandleObject target);
111
112 JSObject* TransplantObjectRetainingXrayExpandos(JSContext* cx,
113 JS::HandleObject origobj,
114 JS::HandleObject target);
115
116 // If origObj has an xray waiver, nuke it before transplant.
117 JSObject* TransplantObjectNukingXrayWaiver(JSContext* cx,
118 JS::HandleObject origObj,
119 JS::HandleObject target);
120
121 bool IsUAWidgetCompartment(JS::Compartment* compartment);
122 bool IsUAWidgetScope(JS::Realm* realm);
123 bool IsInUAWidgetScope(JSObject* obj);
124
125 bool MightBeWebContentCompartment(JS::Compartment* compartment);
126
127 void SetCompartmentChangedDocumentDomain(JS::Compartment* compartment);
128
129 JSObject* GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal);
130
131 JSObject* GetUAWidgetScope(JSContext* cx, JSObject* contentScope);
132
133 // Returns whether XBL scopes have been explicitly disabled for code running
134 // in this compartment. See the comment around mAllowContentXBLScope.
135 bool AllowContentXBLScope(JS::Realm* realm);
136
137 // Get the scope for creating reflectors for native anonymous content
138 // whose normal global would be the given global.
139 JSObject* NACScope(JSObject* global);
140
141 bool IsSandboxPrototypeProxy(JSObject* obj);
142 bool IsWebExtensionContentScriptSandbox(JSObject* obj);
143
144 // The JSContext argument represents the Realm that's asking the question. This
145 // is needed to properly answer without exposing information unnecessarily
146 // from behind security wrappers. There will be no exceptions thrown on this
147 // JSContext.
148 bool IsReflector(JSObject* obj, JSContext* cx);
149
150 bool IsXrayWrapper(JSObject* obj);
151
152 // If this function was created for a given XrayWrapper, returns the global of
153 // the Xrayed object. Otherwise, returns the global of the function.
154 //
155 // To emphasize the obvious: the return value here is not necessarily same-
156 // compartment with the argument.
157 JSObject* XrayAwareCalleeGlobal(JSObject* fun);
158
159 void TraceXPCGlobal(JSTracer* trc, JSObject* obj);
160
161 /**
162 * Creates a new global object using the given aCOMObj as the global
163 * object. The object will be set up according to the flags (defined
164 * below). If you do not pass INIT_JS_STANDARD_CLASSES, then aCOMObj
165 * must implement nsIXPCScriptable so it can resolve the standard
166 * classes when asked by the JS engine.
167 *
168 * @param aJSContext the context to use while creating the global object.
169 * @param aCOMObj the native object that represents the global object.
170 * @param aPrincipal the principal of the code that will run in this
171 * compartment. Can be null if not on the main thread.
172 * @param aFlags one of the flags below specifying what options this
173 * global object wants.
174 * @param aOptions JSAPI-specific options for the new compartment.
175 */
176 nsresult InitClassesWithNewWrappedGlobal(JSContext* aJSContext,
177 nsISupports* aCOMObj,
178 nsIPrincipal* aPrincipal,
179 uint32_t aFlags,
180 JS::RealmOptions& aOptions,
181 JS::MutableHandleObject aNewGlobal);
182
183 enum InitClassesFlag {
184 INIT_JS_STANDARD_CLASSES = 1 << 0,
185 DONT_FIRE_ONNEWGLOBALHOOK = 1 << 1,
186 OMIT_COMPONENTS_OBJECT = 1 << 2,
187 };
188
189 } /* namespace xpc */
190
191 namespace JS {
192
193 struct RuntimeStats;
194
195 } // namespace JS
196
197 static_assert(JSCLASS_GLOBAL_APPLICATION_SLOTS > 0,
198 "Need at least one slot for JSCLASS_SLOT0_IS_NSISUPPORTS");
199
200 #define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n) \
201 JSCLASS_DOM_GLOBAL | JSCLASS_SLOT0_IS_NSISUPPORTS | \
202 JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n)
203
204 #define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET \
205 (JSCLASS_GLOBAL_SLOT_COUNT + DOM_GLOBAL_SLOTS)
206
207 #define XPCONNECT_GLOBAL_FLAGS XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(0)
208
xpc_FastGetCachedWrapper(JSContext * cx,nsWrapperCache * cache,JS::MutableHandleValue vp)209 inline JSObject* xpc_FastGetCachedWrapper(JSContext* cx, nsWrapperCache* cache,
210 JS::MutableHandleValue vp) {
211 if (cache) {
212 JSObject* wrapper = cache->GetWrapper();
213 if (wrapper &&
214 JS::GetCompartment(wrapper) == js::GetContextCompartment(cx)) {
215 vp.setObject(*wrapper);
216 return wrapper;
217 }
218 }
219
220 return nullptr;
221 }
222
223 // If aWrappedJS is a JS wrapper, unmark its JSObject.
224 extern void xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS);
225
226 extern void xpc_UnmarkSkippableJSHolders();
227
228 // Defined in XPCDebug.cpp.
229 extern bool xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps);
230
231 // Return a newly-allocated string containing a representation of the
232 // current JS stack. Defined in XPCDebug.cpp.
233 extern JS::UniqueChars xpc_PrintJSStack(JSContext* cx, bool showArgs,
234 bool showLocals, bool showThisProps);
235
236 // readable string conversions, static methods and members only
237 class XPCStringConvert {
238 public:
239 // If the string shares the readable's buffer, that buffer will
240 // get assigned to *sharedBuffer. Otherwise null will be
241 // assigned.
242 static bool ReadableToJSVal(JSContext* cx, const nsAString& readable,
243 nsStringBuffer** sharedBuffer,
244 JS::MutableHandleValue vp);
245
246 // Convert the given stringbuffer/length pair to a jsval
StringBufferToJSVal(JSContext * cx,nsStringBuffer * buf,uint32_t length,JS::MutableHandleValue rval,bool * sharedBuffer)247 static MOZ_ALWAYS_INLINE bool StringBufferToJSVal(JSContext* cx,
248 nsStringBuffer* buf,
249 uint32_t length,
250 JS::MutableHandleValue rval,
251 bool* sharedBuffer) {
252 JSString* str = JS_NewMaybeExternalString(
253 cx, static_cast<char16_t*>(buf->Data()), length,
254 &sDOMStringExternalString, sharedBuffer);
255 if (!str) {
256 return false;
257 }
258 rval.setString(str);
259 return true;
260 }
261
StringLiteralToJSVal(JSContext * cx,const char16_t * literal,uint32_t length,JS::MutableHandleValue rval)262 static inline bool StringLiteralToJSVal(JSContext* cx,
263 const char16_t* literal,
264 uint32_t length,
265 JS::MutableHandleValue rval) {
266 bool ignored;
267 JSString* str = JS_NewMaybeExternalString(
268 cx, literal, length, &sLiteralExternalString, &ignored);
269 if (!str) {
270 return false;
271 }
272 rval.setString(str);
273 return true;
274 }
275
DynamicAtomToJSVal(JSContext * cx,nsDynamicAtom * atom,JS::MutableHandleValue rval)276 static inline bool DynamicAtomToJSVal(JSContext* cx, nsDynamicAtom* atom,
277 JS::MutableHandleValue rval) {
278 bool sharedAtom;
279 JSString* str =
280 JS_NewMaybeExternalString(cx, atom->GetUTF16String(), atom->GetLength(),
281 &sDynamicAtomExternalString, &sharedAtom);
282 if (!str) {
283 return false;
284 }
285 if (sharedAtom) {
286 // We only have non-owning atoms in DOMString for now.
287 // nsDynamicAtom::AddRef is always-inline and defined in a
288 // translation unit we can't get to here. So we need to go through
289 // nsAtom::AddRef to call it.
290 static_cast<nsAtom*>(atom)->AddRef();
291 }
292 rval.setString(str);
293 return true;
294 }
295
MaybeGetExternalStringChars(JSString * str,const JSExternalStringCallbacks * desiredCallbacks,const char16_t ** chars)296 static MOZ_ALWAYS_INLINE bool MaybeGetExternalStringChars(
297 JSString* str, const JSExternalStringCallbacks* desiredCallbacks,
298 const char16_t** chars) {
299 const JSExternalStringCallbacks* callbacks;
300 return JS::IsExternalString(str, &callbacks, chars) &&
301 callbacks == desiredCallbacks;
302 }
303
304 // Returns non-null chars if the given string is a literal external string.
MaybeGetLiteralStringChars(JSString * str,const char16_t ** chars)305 static MOZ_ALWAYS_INLINE bool MaybeGetLiteralStringChars(
306 JSString* str, const char16_t** chars) {
307 return MaybeGetExternalStringChars(str, &sLiteralExternalString, chars);
308 }
309
310 // Returns non-null chars if the given string is a DOM external string.
MaybeGetDOMStringChars(JSString * str,const char16_t ** chars)311 static MOZ_ALWAYS_INLINE bool MaybeGetDOMStringChars(JSString* str,
312 const char16_t** chars) {
313 return MaybeGetExternalStringChars(str, &sDOMStringExternalString, chars);
314 }
315
316 private:
317 struct LiteralExternalString : public JSExternalStringCallbacks {
318 void finalize(char16_t* aChars) const override;
319 size_t sizeOfBuffer(const char16_t* aChars,
320 mozilla::MallocSizeOf aMallocSizeOf) const override;
321 };
322 struct DOMStringExternalString : public JSExternalStringCallbacks {
323 void finalize(char16_t* aChars) const override;
324 size_t sizeOfBuffer(const char16_t* aChars,
325 mozilla::MallocSizeOf aMallocSizeOf) const override;
326 };
327 struct DynamicAtomExternalString : public JSExternalStringCallbacks {
328 void finalize(char16_t* aChars) const override;
329 size_t sizeOfBuffer(const char16_t* aChars,
330 mozilla::MallocSizeOf aMallocSizeOf) const override;
331 };
332 static const LiteralExternalString sLiteralExternalString;
333 static const DOMStringExternalString sDOMStringExternalString;
334 static const DynamicAtomExternalString sDynamicAtomExternalString;
335
336 XPCStringConvert() = delete;
337 };
338
339 class nsIAddonInterposition;
340
341 namespace xpc {
342
343 // If these functions return false, then an exception will be set on cx.
344 bool Base64Encode(JSContext* cx, JS::HandleValue val,
345 JS::MutableHandleValue out);
346 bool Base64Decode(JSContext* cx, JS::HandleValue val,
347 JS::MutableHandleValue out);
348
349 /**
350 * Convert an nsString to jsval, returning true on success.
351 * Note, the ownership of the string buffer may be moved from str to rval.
352 * If that happens, str will point to an empty string after this call.
353 */
354 bool NonVoidStringToJsval(JSContext* cx, nsAString& str,
355 JS::MutableHandleValue rval);
StringToJsval(JSContext * cx,nsAString & str,JS::MutableHandleValue rval)356 inline bool StringToJsval(JSContext* cx, nsAString& str,
357 JS::MutableHandleValue rval) {
358 // From the T_ASTRING case in XPCConvert::NativeData2JS.
359 if (str.IsVoid()) {
360 rval.setNull();
361 return true;
362 }
363 return NonVoidStringToJsval(cx, str, rval);
364 }
365
NonVoidStringToJsval(JSContext * cx,const nsAString & str,JS::MutableHandleValue rval)366 inline bool NonVoidStringToJsval(JSContext* cx, const nsAString& str,
367 JS::MutableHandleValue rval) {
368 nsString mutableCopy;
369 if (!mutableCopy.Assign(str, mozilla::fallible)) {
370 JS_ReportOutOfMemory(cx);
371 return false;
372 }
373 return NonVoidStringToJsval(cx, mutableCopy, rval);
374 }
375
StringToJsval(JSContext * cx,const nsAString & str,JS::MutableHandleValue rval)376 inline bool StringToJsval(JSContext* cx, const nsAString& str,
377 JS::MutableHandleValue rval) {
378 nsString mutableCopy;
379 if (!mutableCopy.Assign(str, mozilla::fallible)) {
380 JS_ReportOutOfMemory(cx);
381 return false;
382 }
383 return StringToJsval(cx, mutableCopy, rval);
384 }
385
386 /**
387 * As above, but for mozilla::dom::DOMString.
388 */
NonVoidStringToJsval(JSContext * cx,mozilla::dom::DOMString & str,JS::MutableHandleValue rval)389 inline bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
390 JS::MutableHandleValue rval) {
391 if (str.IsEmpty()) {
392 rval.set(JS_GetEmptyStringValue(cx));
393 return true;
394 }
395
396 if (str.HasStringBuffer()) {
397 uint32_t length = str.StringBufferLength();
398 nsStringBuffer* buf = str.StringBuffer();
399 bool shared;
400 if (!XPCStringConvert::StringBufferToJSVal(cx, buf, length, rval,
401 &shared)) {
402 return false;
403 }
404 if (shared) {
405 // JS now needs to hold a reference to the buffer
406 str.RelinquishBufferOwnership();
407 }
408 return true;
409 }
410
411 if (str.HasLiteral()) {
412 return XPCStringConvert::StringLiteralToJSVal(cx, str.Literal(),
413 str.LiteralLength(), rval);
414 }
415
416 if (str.HasAtom()) {
417 return XPCStringConvert::DynamicAtomToJSVal(cx, str.Atom(), rval);
418 }
419
420 // It's an actual XPCOM string
421 return NonVoidStringToJsval(cx, str.AsAString(), rval);
422 }
423
424 MOZ_ALWAYS_INLINE
StringToJsval(JSContext * cx,mozilla::dom::DOMString & str,JS::MutableHandleValue rval)425 bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
426 JS::MutableHandleValue rval) {
427 if (str.IsNull()) {
428 rval.setNull();
429 return true;
430 }
431 return NonVoidStringToJsval(cx, str, rval);
432 }
433
434 mozilla::BasePrincipal* GetRealmPrincipal(JS::Realm* realm);
435
436 void NukeAllWrappersForRealm(JSContext* cx, JS::Realm* realm,
437 js::NukeReferencesToWindow nukeReferencesToWindow =
438 js::NukeWindowReferences);
439
440 void SetLocationForGlobal(JSObject* global, const nsACString& location);
441 void SetLocationForGlobal(JSObject* global, nsIURI* locationURI);
442
443 // ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member
444 // of JS::ZoneStats.
445 class ZoneStatsExtras {
446 public:
447 ZoneStatsExtras() = default;
448
449 nsCString pathPrefix;
450
451 private:
452 ZoneStatsExtras(const ZoneStatsExtras& other) = delete;
453 ZoneStatsExtras& operator=(const ZoneStatsExtras& other) = delete;
454 };
455
456 // ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member
457 // of JS::RealmStats.
458 class RealmStatsExtras {
459 public:
460 RealmStatsExtras() = default;
461
462 nsCString jsPathPrefix;
463 nsCString domPathPrefix;
464 nsCOMPtr<nsIURI> location;
465
466 private:
467 RealmStatsExtras(const RealmStatsExtras& other) = delete;
468 RealmStatsExtras& operator=(const RealmStatsExtras& other) = delete;
469 };
470
471 // This reports all the stats in |rtStats| that belong in the "explicit" tree,
472 // (which isn't all of them).
473 // @see ZoneStatsExtras
474 // @see RealmStatsExtras
475 void ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
476 const nsACString& rtPath,
477 nsIHandleReportCallback* handleReport,
478 nsISupports* data, bool anonymize,
479 size_t* rtTotal = nullptr);
480
481 /**
482 * Throws an exception on cx and returns false.
483 */
484 bool Throw(JSContext* cx, nsresult rv);
485
486 /**
487 * Returns the nsISupports native behind a given reflector (either DOM or
488 * XPCWN). If a non-reflector object is passed in, null will be returned.
489 *
490 * This function will not correctly handle Window or Location objects behind
491 * cross-compartment wrappers: it will return null. If you care about getting
492 * non-null for Window or Location, use ReflectorToISupportsDynamic.
493 */
494 already_AddRefed<nsISupports> ReflectorToISupportsStatic(JSObject* reflector);
495
496 /**
497 * Returns the nsISupports native behind a given reflector (either DOM or
498 * XPCWN). If a non-reflector object is passed in, null will be returned.
499 *
500 * The JSContext argument represents the Realm that's asking for the
501 * nsISupports. This is needed to properly handle Window and Location objects,
502 * which do dynamic security checks.
503 */
504 already_AddRefed<nsISupports> ReflectorToISupportsDynamic(JSObject* reflector,
505 JSContext* cx);
506
507 /**
508 * Singleton scopes for stuff that really doesn't fit anywhere else.
509 *
510 * If you find yourself wanting to use these compartments, you're probably doing
511 * something wrong. Callers MUST consult with the XPConnect module owner before
512 * using this compartment. If you don't, bholley will hunt you down.
513 */
514 JSObject* UnprivilegedJunkScope();
515
516 JSObject* UnprivilegedJunkScope(const mozilla::fallible_t&);
517
518 bool IsUnprivilegedJunkScope(JSObject*);
519
520 /**
521 * This will generally be the shared JSM global, but callers should not depend
522 * on that fact.
523 */
524 JSObject* PrivilegedJunkScope();
525
526 /**
527 * Shared compilation scope for XUL prototype documents and XBL
528 * precompilation.
529 */
530 JSObject* CompilationScope();
531
532 /**
533 * Returns the nsIGlobalObject corresponding to |obj|'s JS global. |obj| must
534 * not be a cross-compartment wrapper: CCWs are not associated with a single
535 * global.
536 */
537 nsIGlobalObject* NativeGlobal(JSObject* obj);
538
539 /**
540 * Returns the nsIGlobalObject corresponding to |cx|'s JS global. Must not be
541 * called when |cx| is not in a Realm.
542 */
543 nsIGlobalObject* CurrentNativeGlobal(JSContext* cx);
544
545 /**
546 * If |aObj| is a window, returns the associated nsGlobalWindow.
547 * Otherwise, returns null.
548 */
549 nsGlobalWindowInner* WindowOrNull(JSObject* aObj);
550
551 /**
552 * If |aObj| has a window for a global, returns the associated nsGlobalWindow.
553 * Otherwise, returns null. Note: aObj must not be a cross-compartment wrapper
554 * because CCWs are not associated with a single global/realm.
555 */
556 nsGlobalWindowInner* WindowGlobalOrNull(JSObject* aObj);
557
558 /**
559 * If |aObj| is a Sandbox object associated with a DOMWindow via a
560 * sandboxPrototype, then return that DOMWindow.
561 * |aCx| is used for checked unwrapping of the Window.
562 */
563 nsGlobalWindowInner* SandboxWindowOrNull(JSObject* aObj, JSContext* aCx);
564
565 /**
566 * If |cx| is in a realm whose global is a window, returns the associated
567 * nsGlobalWindow. Otherwise, returns null.
568 */
569 nsGlobalWindowInner* CurrentWindowOrNull(JSContext* cx);
570
571 class MOZ_RAII AutoScriptActivity {
572 bool mActive;
573 bool mOldValue;
574
575 public:
576 explicit AutoScriptActivity(bool aActive);
577 ~AutoScriptActivity();
578 };
579
580 // This function may be used off-main-thread, in which case it is benignly
581 // racey.
582 bool ShouldDiscardSystemSource();
583
584 void SetPrefableRealmOptions(JS::RealmOptions& options);
585 void SetPrefableContextOptions(JS::ContextOptions& options);
586
587 class ErrorBase {
588 public:
589 nsString mErrorMsg;
590 nsString mFileName;
591 uint32_t mSourceId;
592 uint32_t mLineNumber;
593 uint32_t mColumn;
594
ErrorBase()595 ErrorBase() : mSourceId(0), mLineNumber(0), mColumn(0) {}
596
597 void Init(JSErrorBase* aReport);
598
599 void AppendErrorDetailsTo(nsCString& error);
600 };
601
602 class ErrorNote : public ErrorBase {
603 public:
604 void Init(JSErrorNotes::Note* aNote);
605
606 // Produce an error event message string from the given JSErrorNotes::Note.
607 // This may produce an empty string if aNote doesn't have a message
608 // attached.
609 static void ErrorNoteToMessageString(JSErrorNotes::Note* aNote,
610 nsAString& aString);
611
612 // Log the error note to the stderr.
613 void LogToStderr();
614 };
615
616 class ErrorReport : public ErrorBase {
617 public:
618 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport);
619
620 nsTArray<ErrorNote> mNotes;
621
622 nsCString mCategory;
623 nsString mSourceLine;
624 nsString mErrorMsgName;
625 uint64_t mWindowID;
626 bool mIsWarning;
627 bool mIsMuted;
628 bool mIsPromiseRejection;
629
ErrorReport()630 ErrorReport()
631 : mWindowID(0),
632 mIsWarning(false),
633 mIsMuted(false),
634 mIsPromiseRejection(false) {}
635
636 void Init(JSErrorReport* aReport, const char* aToStringResult, bool aIsChrome,
637 uint64_t aWindowID);
638 void Init(JSContext* aCx, mozilla::dom::Exception* aException, bool aIsChrome,
639 uint64_t aWindowID);
640
641 // Log the error report to the console. Which console will depend on the
642 // window id it was initialized with.
643 void LogToConsole();
644 // Log to console, using the given stack object (which should be a stack of
645 // the sort that JS::CaptureCurrentStack produces). aStack is allowed to be
646 // null. If aStack is non-null, aStackGlobal must be a non-null global
647 // object that's same-compartment with aStack. Note that aStack might be a
648 // CCW.
649 void LogToConsoleWithStack(nsGlobalWindowInner* aWin,
650 JS::Handle<mozilla::Maybe<JS::Value>> aException,
651 JS::HandleObject aStack,
652 JS::HandleObject aStackGlobal);
653
654 // Produce an error event message string from the given JSErrorReport. Note
655 // that this may produce an empty string if aReport doesn't have a
656 // message attached.
657 static void ErrorReportToMessageString(JSErrorReport* aReport,
658 nsAString& aString);
659
660 // Log the error report to the stderr.
661 void LogToStderr();
662
IsWarning()663 bool IsWarning() const { return mIsWarning; };
664
665 private:
666 ~ErrorReport() = default;
667 };
668
669 void DispatchScriptErrorEvent(nsPIDOMWindowInner* win,
670 JS::RootingContext* rootingCx,
671 xpc::ErrorReport* xpcReport,
672 JS::Handle<JS::Value> exception,
673 JS::Handle<JSObject*> exceptionStack);
674
675 // Get a stack (as stackObj outparam) of the sort that can be passed to
676 // xpc::ErrorReport::LogToConsoleWithStack from the given exception value. Can
677 // be nullptr if the exception value doesn't have an associated stack, and if
678 // there is no stack supplied by the JS engine in exceptionStack. The
679 // returned stack, if any, may also not be in the same compartment as
680 // exceptionValue.
681 //
682 // The "win" argument passed in here should be the same as the window whose
683 // WindowID() is used to initialize the xpc::ErrorReport. This may be null, of
684 // course. If it's not null, this function may return a null stack object if
685 // the window is far enough gone, because in those cases we don't want to have
686 // the stack in the console message keeping the window alive.
687 //
688 // If this function sets stackObj to a non-null value, stackGlobal is set to
689 // either the JS exception object's global or the global of the SavedFrame we
690 // got from a DOM or XPConnect exception. In all cases, stackGlobal is an
691 // unwrapped global object and is same-compartment with stackObj.
692 void FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
693 JS::HandleValue exceptionValue,
694 JS::HandleObject exceptionStack,
695 JS::MutableHandleObject stackObj,
696 JS::MutableHandleObject stackGlobal);
697
698 // Return a name for the realm.
699 // This function makes reasonable efforts to make this name both mostly
700 // human-readable and unique. However, there are no guarantees of either
701 // property.
702 extern void GetCurrentRealmName(JSContext*, nsCString& name);
703
704 void AddGCCallback(xpcGCCallback cb);
705 void RemoveGCCallback(xpcGCCallback cb);
706
707 // We need an exact page size only if we run the binary in automation.
708 #if defined(XP_DARWIN) && defined(__aarch64__)
709 const size_t kAutomationPageSize = 16384;
710 #else
711 const size_t kAutomationPageSize = 4096;
712 #endif
713
alignas(kAutomationPageSize)714 struct alignas(kAutomationPageSize) ReadOnlyPage final {
715 bool mNonLocalConnectionsDisabled = false;
716 bool mTurnOffAllSecurityPref = false;
717
718 static void Init();
719
720 #ifdef MOZ_TSAN
721 // TSan is confused by write access to read-only section.
722 static ReadOnlyPage sInstance;
723 #else
724 static const volatile ReadOnlyPage sInstance;
725 #endif
726
727 private:
728 constexpr ReadOnlyPage() = default;
729 ReadOnlyPage(const ReadOnlyPage&) = delete;
730 void operator=(const ReadOnlyPage&) = delete;
731
732 static void Write(const volatile bool* aPtr, bool aValue);
733 };
734
AreNonLocalConnectionsDisabled()735 inline bool AreNonLocalConnectionsDisabled() {
736 return ReadOnlyPage::sInstance.mNonLocalConnectionsDisabled;
737 }
738
IsInAutomation()739 inline bool IsInAutomation() {
740 if (!ReadOnlyPage::sInstance.mTurnOffAllSecurityPref) {
741 return false;
742 }
743 MOZ_RELEASE_ASSERT(AreNonLocalConnectionsDisabled());
744 return true;
745 }
746
747 void InitializeJSContext();
748
749 /**
750 * Extract the native nsID object from a JS ID, IfaceID, ClassID, or ContractID
751 * value.
752 *
753 * Returns 'Nothing()' if 'aVal' does is not one of the supported ID types.
754 */
755 mozilla::Maybe<nsID> JSValue2ID(JSContext* aCx, JS::HandleValue aVal);
756
757 /**
758 * Reflect an ID into JS
759 */
760 bool ID2JSValue(JSContext* aCx, const nsID& aId, JS::MutableHandleValue aVal);
761
762 /**
763 * Reflect an IfaceID into JS
764 *
765 * This object will expose constants from the selected interface, and support
766 * 'instanceof', in addition to the other methods available on JS ID objects.
767 *
768 * Use 'xpc::JSValue2ID' to unwrap JS::Values created with this function.
769 */
770 bool IfaceID2JSValue(JSContext* aCx, const nsXPTInterfaceInfo& aInfo,
771 JS::MutableHandleValue aVal);
772
773 /**
774 * Reflect a ContractID into JS
775 *
776 * This object will expose 'getService' and 'createInstance' methods in addition
777 * to the other methods available on nsID objects.
778 *
779 * Use 'xpc::JSValue2ID' to unwrap JS::Values created with this function.
780 */
781 bool ContractID2JSValue(JSContext* aCx, JSString* aContract,
782 JS::MutableHandleValue aVal);
783
784 class JSStackFrameBase {
785 public:
786 virtual void Clear() = 0;
787 };
788
789 void RegisterJSStackFrame(JS::Realm* aRealm, JSStackFrameBase* aStackFrame);
790 void UnregisterJSStackFrame(JS::Realm* aRealm, JSStackFrameBase* aStackFrame);
791 void NukeJSStackFrames(JS::Realm* aRealm);
792
793 // Check whether the given jsid is a property name (string or symbol) whose
794 // value can be gotten cross-origin. Cross-origin gets always return undefined
795 // as the value, unless the Xray actually provides a different value.
796 bool IsCrossOriginWhitelistedProp(JSContext* cx, JS::HandleId id);
797
798 // Appends to props the jsids for property names (strings or symbols) whose
799 // value can be gotten cross-origin.
800 bool AppendCrossOriginWhitelistedPropNames(JSContext* cx,
801 JS::MutableHandleIdVector props);
802 } // namespace xpc
803
804 namespace mozilla {
805 namespace dom {
806
807 /**
808 * This is used to prevent UA widget code from directly creating and adopting
809 * nodes via the content document, since they should use the special
810 * create-and-insert apis instead.
811 */
812 bool IsNotUAWidget(JSContext* cx, JSObject* /* unused */);
813
814 /**
815 * A test for whether WebIDL methods that should only be visible to
816 * chrome, XBL scopes, or UA Widget scopes.
817 */
818 bool IsChromeOrUAWidget(JSContext* cx, JSObject* /* unused */);
819
820 /**
821 * Same as IsChromeOrUAWidget but can be used in worker threads as well.
822 */
823 bool ThreadSafeIsChromeOrUAWidget(JSContext* cx, JSObject* obj);
824
825 } // namespace dom
826
827 /**
828 * Fill the given vector with the buildid.
829 */
830 bool GetBuildId(JS::BuildIdCharVector* aBuildID);
831
832 } // namespace mozilla
833
834 #endif
835