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 #include "ChromeUtils.h"
8
9 #include "jsfriendapi.h"
10 #include "WrapperFactory.h"
11
12 #include "mozilla/Base64.h"
13 #include "mozilla/BasePrincipal.h"
14 #include "mozilla/CycleCollectedJSRuntime.h"
15 #include "mozilla/TimeStamp.h"
16 #include "mozilla/dom/IdleDeadline.h"
17 #include "mozilla/dom/UnionTypes.h"
18 #include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options
19 #include "nsThreadUtils.h"
20 #include "mozJSComponentLoader.h"
21 #include "GeckoProfiler.h"
22
23 namespace mozilla {
24 namespace dom {
25
NondeterministicGetWeakMapKeys(GlobalObject & aGlobal,JS::Handle<JS::Value> aMap,JS::MutableHandle<JS::Value> aRetval,ErrorResult & aRv)26 /* static */ void ChromeUtils::NondeterministicGetWeakMapKeys(
27 GlobalObject& aGlobal, JS::Handle<JS::Value> aMap,
28 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
29 if (!aMap.isObject()) {
30 aRetval.setUndefined();
31 } else {
32 JSContext* cx = aGlobal.Context();
33 JS::Rooted<JSObject*> objRet(cx);
34 JS::Rooted<JSObject*> mapObj(cx, &aMap.toObject());
35 if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &objRet)) {
36 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
37 } else {
38 aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
39 }
40 }
41 }
42
NondeterministicGetWeakSetKeys(GlobalObject & aGlobal,JS::Handle<JS::Value> aSet,JS::MutableHandle<JS::Value> aRetval,ErrorResult & aRv)43 /* static */ void ChromeUtils::NondeterministicGetWeakSetKeys(
44 GlobalObject& aGlobal, JS::Handle<JS::Value> aSet,
45 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
46 if (!aSet.isObject()) {
47 aRetval.setUndefined();
48 } else {
49 JSContext* cx = aGlobal.Context();
50 JS::Rooted<JSObject*> objRet(cx);
51 JS::Rooted<JSObject*> setObj(cx, &aSet.toObject());
52 if (!JS_NondeterministicGetWeakSetKeys(cx, setObj, &objRet)) {
53 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
54 } else {
55 aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
56 }
57 }
58 }
59
Base64URLEncode(GlobalObject & aGlobal,const ArrayBufferViewOrArrayBuffer & aSource,const Base64URLEncodeOptions & aOptions,nsACString & aResult,ErrorResult & aRv)60 /* static */ void ChromeUtils::Base64URLEncode(
61 GlobalObject& aGlobal, const ArrayBufferViewOrArrayBuffer& aSource,
62 const Base64URLEncodeOptions& aOptions, nsACString& aResult,
63 ErrorResult& aRv) {
64 size_t length = 0;
65 uint8_t* data = nullptr;
66 if (aSource.IsArrayBuffer()) {
67 const ArrayBuffer& buffer = aSource.GetAsArrayBuffer();
68 buffer.ComputeLengthAndData();
69 length = buffer.Length();
70 data = buffer.Data();
71 } else if (aSource.IsArrayBufferView()) {
72 const ArrayBufferView& view = aSource.GetAsArrayBufferView();
73 view.ComputeLengthAndData();
74 length = view.Length();
75 data = view.Data();
76 } else {
77 MOZ_CRASH("Uninitialized union: expected buffer or view");
78 }
79
80 auto paddingPolicy = aOptions.mPad ? Base64URLEncodePaddingPolicy::Include
81 : Base64URLEncodePaddingPolicy::Omit;
82 nsresult rv = mozilla::Base64URLEncode(length, data, paddingPolicy, aResult);
83 if (NS_WARN_IF(NS_FAILED(rv))) {
84 aResult.Truncate();
85 aRv.Throw(rv);
86 }
87 }
88
Base64URLDecode(GlobalObject & aGlobal,const nsACString & aString,const Base64URLDecodeOptions & aOptions,JS::MutableHandle<JSObject * > aRetval,ErrorResult & aRv)89 /* static */ void ChromeUtils::Base64URLDecode(
90 GlobalObject& aGlobal, const nsACString& aString,
91 const Base64URLDecodeOptions& aOptions,
92 JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) {
93 Base64URLDecodePaddingPolicy paddingPolicy;
94 switch (aOptions.mPadding) {
95 case Base64URLDecodePadding::Require:
96 paddingPolicy = Base64URLDecodePaddingPolicy::Require;
97 break;
98
99 case Base64URLDecodePadding::Ignore:
100 paddingPolicy = Base64URLDecodePaddingPolicy::Ignore;
101 break;
102
103 case Base64URLDecodePadding::Reject:
104 paddingPolicy = Base64URLDecodePaddingPolicy::Reject;
105 break;
106
107 default:
108 aRv.Throw(NS_ERROR_INVALID_ARG);
109 return;
110 }
111 FallibleTArray<uint8_t> data;
112 nsresult rv = mozilla::Base64URLDecode(aString, paddingPolicy, data);
113 if (NS_WARN_IF(NS_FAILED(rv))) {
114 aRv.Throw(rv);
115 return;
116 }
117
118 JS::Rooted<JSObject*> buffer(
119 aGlobal.Context(),
120 ArrayBuffer::Create(aGlobal.Context(), data.Length(), data.Elements()));
121 if (NS_WARN_IF(!buffer)) {
122 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
123 return;
124 }
125 aRetval.set(buffer);
126 }
127
WaiveXrays(GlobalObject & aGlobal,JS::HandleValue aVal,JS::MutableHandleValue aRetval,ErrorResult & aRv)128 /* static */ void ChromeUtils::WaiveXrays(GlobalObject& aGlobal,
129 JS::HandleValue aVal,
130 JS::MutableHandleValue aRetval,
131 ErrorResult& aRv) {
132 JS::RootedValue value(aGlobal.Context(), aVal);
133 if (!xpc::WrapperFactory::WaiveXrayAndWrap(aGlobal.Context(), &value)) {
134 aRv.NoteJSContextException(aGlobal.Context());
135 } else {
136 aRetval.set(value);
137 }
138 }
139
UnwaiveXrays(GlobalObject & aGlobal,JS::HandleValue aVal,JS::MutableHandleValue aRetval,ErrorResult & aRv)140 /* static */ void ChromeUtils::UnwaiveXrays(GlobalObject& aGlobal,
141 JS::HandleValue aVal,
142 JS::MutableHandleValue aRetval,
143 ErrorResult& aRv) {
144 if (!aVal.isObject()) {
145 aRetval.set(aVal);
146 return;
147 }
148
149 JS::RootedObject obj(aGlobal.Context(),
150 js::UncheckedUnwrap(&aVal.toObject()));
151 if (!JS_WrapObject(aGlobal.Context(), &obj)) {
152 aRv.NoteJSContextException(aGlobal.Context());
153 } else {
154 aRetval.setObject(*obj);
155 }
156 }
157
GetClassName(GlobalObject & aGlobal,JS::HandleObject aObj,bool aUnwrap,nsAString & aRetval)158 /* static */ void ChromeUtils::GetClassName(GlobalObject& aGlobal,
159 JS::HandleObject aObj, bool aUnwrap,
160 nsAString& aRetval) {
161 JS::RootedObject obj(aGlobal.Context(), aObj);
162 if (aUnwrap) {
163 obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
164 }
165
166 aRetval =
167 NS_ConvertUTF8toUTF16(nsDependentCString(js::GetObjectClass(obj)->name));
168 }
169
ShallowClone(GlobalObject & aGlobal,JS::HandleObject aObj,JS::HandleObject aTarget,JS::MutableHandleObject aRetval,ErrorResult & aRv)170 /* static */ void ChromeUtils::ShallowClone(GlobalObject& aGlobal,
171 JS::HandleObject aObj,
172 JS::HandleObject aTarget,
173 JS::MutableHandleObject aRetval,
174 ErrorResult& aRv) {
175 JSContext* cx = aGlobal.Context();
176
177 auto cleanup = MakeScopeExit([&]() { aRv.NoteJSContextException(cx); });
178
179 JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
180 JS::AutoValueVector values(cx);
181
182 {
183 JS::RootedObject obj(cx, js::CheckedUnwrap(aObj));
184 if (!obj) {
185 js::ReportAccessDenied(cx);
186 return;
187 }
188
189 if (js::IsScriptedProxy(obj)) {
190 JS_ReportErrorASCII(cx, "Shallow cloning a proxy object is not allowed");
191 return;
192 }
193
194 JSAutoCompartment ac(cx, obj);
195
196 if (!JS_Enumerate(cx, obj, &ids) || !values.reserve(ids.length())) {
197 return;
198 }
199
200 JS::Rooted<JS::PropertyDescriptor> desc(cx);
201 JS::RootedId id(cx);
202 for (jsid idVal : ids) {
203 id = idVal;
204 if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) {
205 continue;
206 }
207 if (desc.setter() || desc.getter()) {
208 continue;
209 }
210 values.infallibleAppend(desc.value());
211 }
212 }
213
214 JS::RootedObject obj(cx);
215 {
216 Maybe<JSAutoCompartment> ac;
217 if (aTarget) {
218 JS::RootedObject target(cx, js::CheckedUnwrap(aTarget));
219 if (!target) {
220 js::ReportAccessDenied(cx);
221 return;
222 }
223 ac.emplace(cx, target);
224 }
225
226 obj = JS_NewPlainObject(cx);
227 if (!obj) {
228 return;
229 }
230
231 JS::RootedValue value(cx);
232 JS::RootedId id(cx);
233 for (uint32_t i = 0; i < ids.length(); i++) {
234 id = ids[i];
235 value = values[i];
236
237 JS_MarkCrossZoneId(cx, id);
238 if (!JS_WrapValue(cx, &value) ||
239 !JS_SetPropertyById(cx, obj, id, value)) {
240 return;
241 }
242 }
243 }
244
245 if (aTarget && !JS_WrapObject(cx, &obj)) {
246 return;
247 }
248
249 cleanup.release();
250 aRetval.set(obj);
251 }
252
253 namespace {
254 class IdleDispatchRunnable final : public IdleRunnable,
255 public nsITimerCallback {
256 public:
257 NS_DECL_ISUPPORTS_INHERITED
258
IdleDispatchRunnable(nsIGlobalObject * aParent,IdleRequestCallback & aCallback)259 IdleDispatchRunnable(nsIGlobalObject* aParent, IdleRequestCallback& aCallback)
260 : IdleRunnable("ChromeUtils::IdleDispatch"),
261 mCallback(&aCallback),
262 mParent(aParent) {}
263
Run()264 NS_IMETHOD Run() override {
265 if (mCallback) {
266 CancelTimer();
267
268 auto deadline = mDeadline - TimeStamp::ProcessCreation();
269
270 ErrorResult rv;
271 RefPtr<IdleDeadline> idleDeadline =
272 new IdleDeadline(mParent, mTimedOut, deadline.ToMilliseconds());
273
274 mCallback->Call(*idleDeadline, rv, "ChromeUtils::IdleDispatch handler");
275 mCallback = nullptr;
276 mParent = nullptr;
277
278 rv.SuppressException();
279 return rv.StealNSResult();
280 }
281 return NS_OK;
282 }
283
SetDeadline(TimeStamp aDeadline)284 void SetDeadline(TimeStamp aDeadline) override { mDeadline = aDeadline; }
285
Notify(nsITimer * aTimer)286 NS_IMETHOD Notify(nsITimer* aTimer) override {
287 mTimedOut = true;
288 SetDeadline(TimeStamp::Now());
289 return Run();
290 }
291
SetTimer(uint32_t aDelay,nsIEventTarget * aTarget)292 void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override {
293 MOZ_ASSERT(aTarget);
294 MOZ_ASSERT(!mTimer);
295 NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aDelay,
296 nsITimer::TYPE_ONE_SHOT, aTarget);
297 }
298
299 protected:
~IdleDispatchRunnable()300 virtual ~IdleDispatchRunnable() { CancelTimer(); }
301
302 private:
CancelTimer()303 void CancelTimer() {
304 if (mTimer) {
305 mTimer->Cancel();
306 mTimer = nullptr;
307 }
308 }
309
310 RefPtr<IdleRequestCallback> mCallback;
311 nsCOMPtr<nsIGlobalObject> mParent;
312
313 nsCOMPtr<nsITimer> mTimer;
314
315 TimeStamp mDeadline{};
316 bool mTimedOut = false;
317 };
318
319 NS_IMPL_ISUPPORTS_INHERITED(IdleDispatchRunnable, IdleRunnable,
320 nsITimerCallback)
321 } // anonymous namespace
322
IdleDispatch(const GlobalObject & aGlobal,IdleRequestCallback & aCallback,const IdleRequestOptions & aOptions,ErrorResult & aRv)323 /* static */ void ChromeUtils::IdleDispatch(const GlobalObject& aGlobal,
324 IdleRequestCallback& aCallback,
325 const IdleRequestOptions& aOptions,
326 ErrorResult& aRv) {
327 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
328 MOZ_ASSERT(global);
329
330 auto runnable = MakeRefPtr<IdleDispatchRunnable>(global, aCallback);
331
332 if (aOptions.mTimeout.WasPassed()) {
333 aRv = NS_IdleDispatchToCurrentThread(runnable.forget(),
334 aOptions.mTimeout.Value());
335 } else {
336 aRv = NS_IdleDispatchToCurrentThread(runnable.forget());
337 }
338 }
339
Import(const GlobalObject & aGlobal,const nsAString & aResourceURI,const Optional<JS::Handle<JSObject * >> & aTargetObj,JS::MutableHandle<JSObject * > aRetval,ErrorResult & aRv)340 /* static */ void ChromeUtils::Import(
341 const GlobalObject& aGlobal, const nsAString& aResourceURI,
342 const Optional<JS::Handle<JSObject*>>& aTargetObj,
343 JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) {
344 RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
345 MOZ_ASSERT(moduleloader);
346
347 NS_ConvertUTF16toUTF8 registryLocation(aResourceURI);
348
349 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("ChromeUtils::Import", OTHER,
350 registryLocation);
351
352 JSContext* cx = aGlobal.Context();
353 JS::Rooted<JS::Value> targetObj(cx);
354 uint8_t optionalArgc;
355 if (aTargetObj.WasPassed()) {
356 targetObj.setObjectOrNull(aTargetObj.Value());
357 optionalArgc = 1;
358 } else {
359 targetObj.setUndefined();
360 optionalArgc = 0;
361 }
362
363 JS::Rooted<JS::Value> retval(cx);
364 nsresult rv = moduleloader->ImportInto(registryLocation, targetObj, cx,
365 optionalArgc, &retval);
366 if (NS_FAILED(rv)) {
367 aRv.Throw(rv);
368 return;
369 }
370
371 // Import() on the component loader can return NS_OK while leaving an
372 // exception on the JSContext. Check for that case.
373 if (JS_IsExceptionPending(cx)) {
374 aRv.NoteJSContextException(cx);
375 return;
376 }
377
378 // Now we better have an object.
379 MOZ_ASSERT(retval.isObject());
380 aRetval.set(&retval.toObject());
381 }
382
383 namespace module_getter {
384 static const size_t SLOT_ID = 0;
385 static const size_t SLOT_URI = 1;
386
ExtractArgs(JSContext * aCx,JS::CallArgs & aArgs,JS::MutableHandle<JSObject * > aCallee,JS::MutableHandle<JSObject * > aThisObj,JS::MutableHandle<jsid> aId)387 static bool ExtractArgs(JSContext* aCx, JS::CallArgs& aArgs,
388 JS::MutableHandle<JSObject*> aCallee,
389 JS::MutableHandle<JSObject*> aThisObj,
390 JS::MutableHandle<jsid> aId) {
391 aCallee.set(&aArgs.callee());
392
393 JS::Handle<JS::Value> thisv = aArgs.thisv();
394 if (!thisv.isObject()) {
395 JS_ReportErrorASCII(aCx, "Invalid target object");
396 return false;
397 }
398
399 aThisObj.set(&thisv.toObject());
400
401 JS::Rooted<JS::Value> id(aCx,
402 js::GetFunctionNativeReserved(aCallee, SLOT_ID));
403 MOZ_ALWAYS_TRUE(JS_ValueToId(aCx, id, aId));
404 return true;
405 }
406
ModuleGetter(JSContext * aCx,unsigned aArgc,JS::Value * aVp)407 static bool ModuleGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
408 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
409
410 JS::Rooted<JSObject*> callee(aCx);
411 JS::Rooted<JSObject*> thisObj(aCx);
412 JS::Rooted<jsid> id(aCx);
413 if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
414 return false;
415 }
416
417 JS::Rooted<JSString*> moduleURI(
418 aCx, js::GetFunctionNativeReserved(callee, SLOT_URI).toString());
419 JSAutoByteString bytes;
420 if (!bytes.encodeUtf8(aCx, moduleURI)) {
421 return false;
422 }
423 nsDependentCString uri(bytes.ptr());
424
425 RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
426 MOZ_ASSERT(moduleloader);
427
428 JS::Rooted<JSObject*> moduleGlobal(aCx);
429 JS::Rooted<JSObject*> moduleExports(aCx);
430 nsresult rv = moduleloader->Import(aCx, uri, &moduleGlobal, &moduleExports);
431 if (NS_FAILED(rv)) {
432 Throw(aCx, rv);
433 return false;
434 }
435
436 JS::RootedValue value(aCx);
437 {
438 JSAutoCompartment ac(aCx, moduleExports);
439
440 if (!JS_GetPropertyById(aCx, moduleExports, id, &value)) {
441 return false;
442 }
443 }
444
445 if (!JS_WrapValue(aCx, &value) ||
446 !JS_DefinePropertyById(aCx, thisObj, id, value, JSPROP_ENUMERATE)) {
447 return false;
448 }
449
450 args.rval().set(value);
451 return true;
452 }
453
ModuleSetter(JSContext * aCx,unsigned aArgc,JS::Value * aVp)454 static bool ModuleSetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
455 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
456
457 JS::Rooted<JSObject*> callee(aCx);
458 JS::Rooted<JSObject*> thisObj(aCx);
459 JS::Rooted<jsid> id(aCx);
460 if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
461 return false;
462 }
463
464 return JS_DefinePropertyById(aCx, thisObj, id, args.get(0), JSPROP_ENUMERATE);
465 }
466
DefineGetter(JSContext * aCx,JS::Handle<JSObject * > aTarget,const nsAString & aId,const nsAString & aResourceURI)467 static bool DefineGetter(JSContext* aCx, JS::Handle<JSObject*> aTarget,
468 const nsAString& aId, const nsAString& aResourceURI) {
469 JS::RootedValue uri(aCx);
470 JS::RootedValue idValue(aCx);
471 JS::Rooted<jsid> id(aCx);
472 if (!xpc::NonVoidStringToJsval(aCx, aResourceURI, &uri) ||
473 !xpc::NonVoidStringToJsval(aCx, aId, &idValue) ||
474 !JS_ValueToId(aCx, idValue, &id)) {
475 return false;
476 }
477 idValue = js::IdToValue(id);
478
479 JS::Rooted<JSObject*> getter(
480 aCx, JS_GetFunctionObject(
481 js::NewFunctionByIdWithReserved(aCx, ModuleGetter, 0, 0, id)));
482
483 JS::Rooted<JSObject*> setter(
484 aCx, JS_GetFunctionObject(
485 js::NewFunctionByIdWithReserved(aCx, ModuleSetter, 0, 0, id)));
486
487 if (!getter || !setter) {
488 JS_ReportOutOfMemory(aCx);
489 return false;
490 }
491
492 js::SetFunctionNativeReserved(getter, SLOT_ID, idValue);
493 js::SetFunctionNativeReserved(setter, SLOT_ID, idValue);
494
495 js::SetFunctionNativeReserved(getter, SLOT_URI, uri);
496
497 return JS_DefinePropertyById(
498 aCx, aTarget, id, JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
499 JS_DATA_TO_FUNC_PTR(JSNative, setter.get()),
500 JSPROP_GETTER | JSPROP_SETTER | JSPROP_ENUMERATE);
501 }
502 } // namespace module_getter
503
DefineModuleGetter(const GlobalObject & global,JS::Handle<JSObject * > target,const nsAString & id,const nsAString & resourceURI,ErrorResult & aRv)504 /* static */ void ChromeUtils::DefineModuleGetter(const GlobalObject& global,
505 JS::Handle<JSObject*> target,
506 const nsAString& id,
507 const nsAString& resourceURI,
508 ErrorResult& aRv) {
509 if (!module_getter::DefineGetter(global.Context(), target, id, resourceURI)) {
510 aRv.NoteJSContextException(global.Context());
511 }
512 }
513
OriginAttributesToSuffix(dom::GlobalObject & aGlobal,const dom::OriginAttributesDictionary & aAttrs,nsCString & aSuffix)514 /* static */ void ChromeUtils::OriginAttributesToSuffix(
515 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
516 nsCString& aSuffix)
517
518 {
519 OriginAttributes attrs(aAttrs);
520 attrs.CreateSuffix(aSuffix);
521 }
522
OriginAttributesMatchPattern(dom::GlobalObject & aGlobal,const dom::OriginAttributesDictionary & aAttrs,const dom::OriginAttributesPatternDictionary & aPattern)523 /* static */ bool ChromeUtils::OriginAttributesMatchPattern(
524 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
525 const dom::OriginAttributesPatternDictionary& aPattern) {
526 OriginAttributes attrs(aAttrs);
527 OriginAttributesPattern pattern(aPattern);
528 return pattern.Matches(attrs);
529 }
530
CreateOriginAttributesFromOrigin(dom::GlobalObject & aGlobal,const nsAString & aOrigin,dom::OriginAttributesDictionary & aAttrs,ErrorResult & aRv)531 /* static */ void ChromeUtils::CreateOriginAttributesFromOrigin(
532 dom::GlobalObject& aGlobal, const nsAString& aOrigin,
533 dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv) {
534 OriginAttributes attrs;
535 nsAutoCString suffix;
536 if (!attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(aOrigin), suffix)) {
537 aRv.Throw(NS_ERROR_FAILURE);
538 return;
539 }
540 aAttrs = attrs;
541 }
542
FillNonDefaultOriginAttributes(dom::GlobalObject & aGlobal,const dom::OriginAttributesDictionary & aAttrs,dom::OriginAttributesDictionary & aNewAttrs)543 /* static */ void ChromeUtils::FillNonDefaultOriginAttributes(
544 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
545 dom::OriginAttributesDictionary& aNewAttrs) {
546 aNewAttrs = aAttrs;
547 }
548
IsOriginAttributesEqual(dom::GlobalObject & aGlobal,const dom::OriginAttributesDictionary & aA,const dom::OriginAttributesDictionary & aB)549 /* static */ bool ChromeUtils::IsOriginAttributesEqual(
550 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aA,
551 const dom::OriginAttributesDictionary& aB) {
552 return IsOriginAttributesEqual(aA, aB);
553 }
554
IsOriginAttributesEqual(const dom::OriginAttributesDictionary & aA,const dom::OriginAttributesDictionary & aB)555 /* static */ bool ChromeUtils::IsOriginAttributesEqual(
556 const dom::OriginAttributesDictionary& aA,
557 const dom::OriginAttributesDictionary& aB) {
558 return aA.mAppId == aB.mAppId &&
559 aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
560 aA.mUserContextId == aB.mUserContextId &&
561 aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
562 }
563
564 #ifdef NIGHTLY_BUILD
GetRecentJSDevError(GlobalObject & aGlobal,JS::MutableHandleValue aRetval,ErrorResult & aRv)565 /* static */ void ChromeUtils::GetRecentJSDevError(
566 GlobalObject& aGlobal, JS::MutableHandleValue aRetval, ErrorResult& aRv) {
567 aRetval.setUndefined();
568 auto runtime = CycleCollectedJSRuntime::Get();
569 MOZ_ASSERT(runtime);
570
571 auto cx = aGlobal.Context();
572 if (!runtime->GetRecentDevError(cx, aRetval)) {
573 aRv.NoteJSContextException(cx);
574 return;
575 }
576 }
577
ClearRecentJSDevError(GlobalObject &)578 /* static */ void ChromeUtils::ClearRecentJSDevError(GlobalObject&) {
579 auto runtime = CycleCollectedJSRuntime::Get();
580 MOZ_ASSERT(runtime);
581
582 runtime->ClearRecentDevError();
583 }
584 #endif // NIGHTLY_BUILD
585
586 constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
587
GetCallerLocation(const GlobalObject & aGlobal,nsIPrincipal * aPrincipal,JS::MutableHandle<JSObject * > aRetval)588 /* static */ void ChromeUtils::GetCallerLocation(
589 const GlobalObject& aGlobal, nsIPrincipal* aPrincipal,
590 JS::MutableHandle<JSObject*> aRetval) {
591 JSContext* cx = aGlobal.Context();
592
593 auto* principals = nsJSPrincipals::get(aPrincipal);
594
595 JS::StackCapture captureMode(JS::FirstSubsumedFrame(cx, principals));
596
597 JS::RootedObject frame(cx);
598 if (!JS::CaptureCurrentStack(cx, &frame, mozilla::Move(captureMode))) {
599 JS_ClearPendingException(cx);
600 aRetval.set(nullptr);
601 return;
602 }
603
604 // FirstSubsumedFrame gets us a stack which stops at the first principal which
605 // is subsumed by the given principal. That means that we may have a lot of
606 // privileged frames that we don't care about at the top of the stack, though.
607 // We need to filter those out to get the frame we actually want.
608 aRetval.set(
609 js::GetFirstSubsumedSavedFrame(cx, principals, frame, kSkipSelfHosted));
610 }
611
CreateError(const GlobalObject & aGlobal,const nsAString & aMessage,JS::Handle<JSObject * > aStack,JS::MutableHandle<JSObject * > aRetVal,ErrorResult & aRv)612 /* static */ void ChromeUtils::CreateError(const GlobalObject& aGlobal,
613 const nsAString& aMessage,
614 JS::Handle<JSObject*> aStack,
615 JS::MutableHandle<JSObject*> aRetVal,
616 ErrorResult& aRv) {
617 if (aStack && !JS::IsSavedFrame(aStack)) {
618 aRv.Throw(NS_ERROR_INVALID_ARG);
619 return;
620 }
621
622 JSContext* cx = aGlobal.Context();
623
624 auto cleanup = MakeScopeExit([&]() { aRv.NoteJSContextException(cx); });
625
626 JS::RootedObject retVal(cx);
627 {
628 JS::RootedString fileName(cx, JS_GetEmptyString(cx));
629 uint32_t line = 0;
630 uint32_t column = 0;
631
632 Maybe<JSAutoCompartment> ac;
633 JS::RootedObject stack(cx);
634 if (aStack) {
635 stack = UncheckedUnwrap(aStack);
636 ac.emplace(cx, stack);
637
638 if (JS::GetSavedFrameLine(cx, stack, &line) != JS::SavedFrameResult::Ok ||
639 JS::GetSavedFrameColumn(cx, stack, &column) !=
640 JS::SavedFrameResult::Ok ||
641 JS::GetSavedFrameSource(cx, stack, &fileName) !=
642 JS::SavedFrameResult::Ok) {
643 return;
644 }
645 }
646
647 JS::RootedString message(cx);
648 {
649 JS::RootedValue msgVal(cx);
650 if (!xpc::NonVoidStringToJsval(cx, aMessage, &msgVal)) {
651 return;
652 }
653 message = msgVal.toString();
654 }
655
656 JS::Rooted<JS::Value> err(cx);
657 if (!JS::CreateError(cx, JSEXN_ERR, stack, fileName, line, column, nullptr,
658 message, &err)) {
659 return;
660 }
661
662 MOZ_ASSERT(err.isObject());
663 retVal = &err.toObject();
664 }
665
666 if (aStack && !JS_WrapObject(cx, &retVal)) {
667 return;
668 }
669
670 cleanup.release();
671 aRetVal.set(retVal);
672 }
673
674 } // namespace dom
675 } // namespace mozilla
676