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