Source code

Revision control

Other Tools

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 "js/CharacterEncoding.h"
10
#include "js/SavedFrameAPI.h"
11
#include "jsfriendapi.h"
12
#include "WrapperFactory.h"
13
14
#include "mozilla/Base64.h"
15
#include "mozilla/BasePrincipal.h"
16
#include "mozilla/CycleCollectedJSRuntime.h"
17
#include "mozilla/IntentionalCrash.h"
18
#include "mozilla/PerformanceMetricsCollector.h"
19
#include "mozilla/PerfStats.h"
20
#include "mozilla/Preferences.h"
21
#include "mozilla/ProcInfo.h"
22
#include "mozilla/RDDProcessManager.h"
23
#include "mozilla/ResultExtensions.h"
24
#include "mozilla/TimeStamp.h"
25
#include "mozilla/dom/BrowsingContext.h"
26
#include "mozilla/dom/ContentParent.h"
27
#include "mozilla/dom/IdleDeadline.h"
28
#include "mozilla/dom/JSWindowActorService.h"
29
#include "mozilla/dom/Promise.h"
30
#include "mozilla/dom/ReportingHeader.h"
31
#include "mozilla/dom/UnionTypes.h"
32
#include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options
33
#include "mozilla/gfx/GPUProcessManager.h"
34
#include "mozilla/ipc/GeckoChildProcessHost.h"
35
#include "mozilla/net/UrlClassifierFeatureFactory.h"
36
#include "mozilla/net/SocketProcessHost.h"
37
#include "IOActivityMonitor.h"
38
#include "nsIOService.h"
39
#include "nsThreadUtils.h"
40
#include "mozJSComponentLoader.h"
41
#include "GeckoProfiler.h"
42
#include "nsIException.h"
43
44
namespace mozilla {
45
namespace dom {
46
47
/* static */
48
void ChromeUtils::NondeterministicGetWeakMapKeys(
49
GlobalObject& aGlobal, JS::Handle<JS::Value> aMap,
50
JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
51
if (!aMap.isObject()) {
52
aRetval.setUndefined();
53
} else {
54
JSContext* cx = aGlobal.Context();
55
JS::Rooted<JSObject*> objRet(cx);
56
JS::Rooted<JSObject*> mapObj(cx, &aMap.toObject());
57
if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &objRet)) {
58
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
59
} else {
60
aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
61
}
62
}
63
}
64
65
/* static */
66
void ChromeUtils::NondeterministicGetWeakSetKeys(
67
GlobalObject& aGlobal, JS::Handle<JS::Value> aSet,
68
JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
69
if (!aSet.isObject()) {
70
aRetval.setUndefined();
71
} else {
72
JSContext* cx = aGlobal.Context();
73
JS::Rooted<JSObject*> objRet(cx);
74
JS::Rooted<JSObject*> setObj(cx, &aSet.toObject());
75
if (!JS_NondeterministicGetWeakSetKeys(cx, setObj, &objRet)) {
76
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
77
} else {
78
aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
79
}
80
}
81
}
82
83
/* static */
84
void ChromeUtils::Base64URLEncode(GlobalObject& aGlobal,
85
const ArrayBufferViewOrArrayBuffer& aSource,
86
const Base64URLEncodeOptions& aOptions,
87
nsACString& aResult, ErrorResult& aRv) {
88
size_t length = 0;
89
uint8_t* data = nullptr;
90
if (aSource.IsArrayBuffer()) {
91
const ArrayBuffer& buffer = aSource.GetAsArrayBuffer();
92
buffer.ComputeLengthAndData();
93
length = buffer.Length();
94
data = buffer.Data();
95
} else if (aSource.IsArrayBufferView()) {
96
const ArrayBufferView& view = aSource.GetAsArrayBufferView();
97
view.ComputeLengthAndData();
98
length = view.Length();
99
data = view.Data();
100
} else {
101
MOZ_CRASH("Uninitialized union: expected buffer or view");
102
}
103
104
auto paddingPolicy = aOptions.mPad ? Base64URLEncodePaddingPolicy::Include
105
: Base64URLEncodePaddingPolicy::Omit;
106
nsresult rv = mozilla::Base64URLEncode(length, data, paddingPolicy, aResult);
107
if (NS_WARN_IF(NS_FAILED(rv))) {
108
aResult.Truncate();
109
aRv.Throw(rv);
110
}
111
}
112
113
/* static */
114
void ChromeUtils::Base64URLDecode(GlobalObject& aGlobal,
115
const nsACString& aString,
116
const Base64URLDecodeOptions& aOptions,
117
JS::MutableHandle<JSObject*> aRetval,
118
ErrorResult& aRv) {
119
Base64URLDecodePaddingPolicy paddingPolicy;
120
switch (aOptions.mPadding) {
121
case Base64URLDecodePadding::Require:
122
paddingPolicy = Base64URLDecodePaddingPolicy::Require;
123
break;
124
125
case Base64URLDecodePadding::Ignore:
126
paddingPolicy = Base64URLDecodePaddingPolicy::Ignore;
127
break;
128
129
case Base64URLDecodePadding::Reject:
130
paddingPolicy = Base64URLDecodePaddingPolicy::Reject;
131
break;
132
133
default:
134
aRv.Throw(NS_ERROR_INVALID_ARG);
135
return;
136
}
137
FallibleTArray<uint8_t> data;
138
nsresult rv = mozilla::Base64URLDecode(aString, paddingPolicy, data);
139
if (NS_WARN_IF(NS_FAILED(rv))) {
140
aRv.Throw(rv);
141
return;
142
}
143
144
JS::Rooted<JSObject*> buffer(
145
aGlobal.Context(),
146
ArrayBuffer::Create(aGlobal.Context(), data.Length(), data.Elements()));
147
if (NS_WARN_IF(!buffer)) {
148
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
149
return;
150
}
151
aRetval.set(buffer);
152
}
153
154
/* static */
155
void ChromeUtils::ReleaseAssert(GlobalObject& aGlobal, bool aCondition,
156
const nsAString& aMessage) {
157
// If the condition didn't fail, which is the likely case, immediately return.
158
if (MOZ_LIKELY(aCondition)) {
159
return;
160
}
161
162
// Extract the current stack from the JS runtime to embed in the crash reason.
163
nsAutoString filename;
164
uint32_t lineNo = 0;
165
166
if (nsCOMPtr<nsIStackFrame> location = GetCurrentJSStack(1)) {
167
location->GetFilename(aGlobal.Context(), filename);
168
lineNo = location->GetLineNumber(aGlobal.Context());
169
} else {
170
filename.Assign(NS_LITERAL_STRING("<unknown>"));
171
}
172
173
// Convert to utf-8 for adding as the MozCrashReason.
174
NS_ConvertUTF16toUTF8 filenameUtf8(filename);
175
NS_ConvertUTF16toUTF8 messageUtf8(aMessage);
176
177
// Actually crash.
178
MOZ_CRASH_UNSAFE_PRINTF("Failed ChromeUtils.releaseAssert(\"%s\") @ %s:%u",
179
messageUtf8.get(), filenameUtf8.get(), lineNo);
180
}
181
182
/* static */
183
void ChromeUtils::WaiveXrays(GlobalObject& aGlobal, JS::HandleValue aVal,
184
JS::MutableHandleValue aRetval, ErrorResult& aRv) {
185
JS::RootedValue value(aGlobal.Context(), aVal);
186
if (!xpc::WrapperFactory::WaiveXrayAndWrap(aGlobal.Context(), &value)) {
187
aRv.NoteJSContextException(aGlobal.Context());
188
} else {
189
aRetval.set(value);
190
}
191
}
192
193
/* static */
194
void ChromeUtils::UnwaiveXrays(GlobalObject& aGlobal, JS::HandleValue aVal,
195
JS::MutableHandleValue aRetval,
196
ErrorResult& aRv) {
197
if (!aVal.isObject()) {
198
aRetval.set(aVal);
199
return;
200
}
201
202
JS::RootedObject obj(aGlobal.Context(),
203
js::UncheckedUnwrap(&aVal.toObject()));
204
if (!JS_WrapObject(aGlobal.Context(), &obj)) {
205
aRv.NoteJSContextException(aGlobal.Context());
206
} else {
207
aRetval.setObject(*obj);
208
}
209
}
210
211
/* static */
212
void ChromeUtils::GetClassName(GlobalObject& aGlobal, JS::HandleObject aObj,
213
bool aUnwrap, nsAString& aRetval) {
214
JS::RootedObject obj(aGlobal.Context(), aObj);
215
if (aUnwrap) {
216
obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
217
}
218
219
aRetval =
220
NS_ConvertUTF8toUTF16(nsDependentCString(js::GetObjectClass(obj)->name));
221
}
222
223
/* static */
224
void ChromeUtils::ShallowClone(GlobalObject& aGlobal, JS::HandleObject aObj,
225
JS::HandleObject aTarget,
226
JS::MutableHandleObject aRetval,
227
ErrorResult& aRv) {
228
JSContext* cx = aGlobal.Context();
229
230
auto cleanup = MakeScopeExit([&]() { aRv.NoteJSContextException(cx); });
231
232
JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
233
JS::RootedVector<JS::Value> values(cx);
234
JS::RootedVector<jsid> valuesIds(cx);
235
236
{
237
// cx represents our current Realm, so it makes sense to use it for the
238
// CheckedUnwrapDynamic call. We do want CheckedUnwrapDynamic, in case
239
// someone is shallow-cloning a Window.
240
JS::RootedObject obj(cx, js::CheckedUnwrapDynamic(aObj, cx));
241
if (!obj) {
242
js::ReportAccessDenied(cx);
243
return;
244
}
245
246
if (js::IsScriptedProxy(obj)) {
247
JS_ReportErrorASCII(cx, "Shallow cloning a proxy object is not allowed");
248
return;
249
}
250
251
JSAutoRealm ar(cx, obj);
252
253
if (!JS_Enumerate(cx, obj, &ids) || !values.reserve(ids.length()) ||
254
!valuesIds.reserve(ids.length())) {
255
return;
256
}
257
258
JS::Rooted<JS::PropertyDescriptor> desc(cx);
259
JS::RootedId id(cx);
260
for (jsid idVal : ids) {
261
id = idVal;
262
if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) {
263
continue;
264
}
265
if (desc.setter() || desc.getter()) {
266
continue;
267
}
268
valuesIds.infallibleAppend(id);
269
values.infallibleAppend(desc.value());
270
}
271
}
272
273
JS::RootedObject obj(cx);
274
{
275
Maybe<JSAutoRealm> ar;
276
if (aTarget) {
277
// Our target could be anything, so we want CheckedUnwrapDynamic here.
278
// "cx" represents the current Realm when we were called from bindings, so
279
// we can just use that.
280
JS::RootedObject target(cx, js::CheckedUnwrapDynamic(aTarget, cx));
281
if (!target) {
282
js::ReportAccessDenied(cx);
283
return;
284
}
285
ar.emplace(cx, target);
286
}
287
288
obj = JS_NewPlainObject(cx);
289
if (!obj) {
290
return;
291
}
292
293
JS::RootedValue value(cx);
294
JS::RootedId id(cx);
295
for (uint32_t i = 0; i < valuesIds.length(); i++) {
296
id = valuesIds[i];
297
value = values[i];
298
299
JS_MarkCrossZoneId(cx, id);
300
if (!JS_WrapValue(cx, &value) ||
301
!JS_SetPropertyById(cx, obj, id, value)) {
302
return;
303
}
304
}
305
}
306
307
if (aTarget && !JS_WrapObject(cx, &obj)) {
308
return;
309
}
310
311
cleanup.release();
312
aRetval.set(obj);
313
}
314
315
namespace {
316
class IdleDispatchRunnable final : public IdleRunnable,
317
public nsITimerCallback {
318
public:
319
NS_DECL_ISUPPORTS_INHERITED
320
321
IdleDispatchRunnable(nsIGlobalObject* aParent, IdleRequestCallback& aCallback)
322
: IdleRunnable("ChromeUtils::IdleDispatch"),
323
mCallback(&aCallback),
324
mParent(aParent) {}
325
326
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.
327
// See bug 1535398.
328
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
329
if (mCallback) {
330
CancelTimer();
331
332
auto deadline = mDeadline - TimeStamp::ProcessCreation();
333
334
ErrorResult rv;
335
RefPtr<IdleDeadline> idleDeadline =
336
new IdleDeadline(mParent, mTimedOut, deadline.ToMilliseconds());
337
338
RefPtr<IdleRequestCallback> callback(mCallback.forget());
339
MOZ_ASSERT(!mCallback);
340
callback->Call(*idleDeadline, "ChromeUtils::IdleDispatch handler");
341
mParent = nullptr;
342
}
343
return NS_OK;
344
}
345
346
void SetDeadline(TimeStamp aDeadline) override { mDeadline = aDeadline; }
347
348
NS_IMETHOD Notify(nsITimer* aTimer) override {
349
mTimedOut = true;
350
SetDeadline(TimeStamp::Now());
351
return Run();
352
}
353
354
void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override {
355
MOZ_ASSERT(aTarget);
356
MOZ_ASSERT(!mTimer);
357
NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aDelay,
358
nsITimer::TYPE_ONE_SHOT, aTarget);
359
}
360
361
protected:
362
virtual ~IdleDispatchRunnable() { CancelTimer(); }
363
364
private:
365
void CancelTimer() {
366
if (mTimer) {
367
mTimer->Cancel();
368
mTimer = nullptr;
369
}
370
}
371
372
RefPtr<IdleRequestCallback> mCallback;
373
nsCOMPtr<nsIGlobalObject> mParent;
374
375
nsCOMPtr<nsITimer> mTimer;
376
377
TimeStamp mDeadline{};
378
bool mTimedOut = false;
379
};
380
381
NS_IMPL_ISUPPORTS_INHERITED(IdleDispatchRunnable, IdleRunnable,
382
nsITimerCallback)
383
} // anonymous namespace
384
385
/* static */
386
void ChromeUtils::IdleDispatch(const GlobalObject& aGlobal,
387
IdleRequestCallback& aCallback,
388
const IdleRequestOptions& aOptions,
389
ErrorResult& aRv) {
390
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
391
MOZ_ASSERT(global);
392
393
auto runnable = MakeRefPtr<IdleDispatchRunnable>(global, aCallback);
394
395
if (aOptions.mTimeout.WasPassed()) {
396
aRv = NS_DispatchToCurrentThreadQueue(
397
runnable.forget(), aOptions.mTimeout.Value(), EventQueuePriority::Idle);
398
} else {
399
aRv = NS_DispatchToCurrentThreadQueue(runnable.forget(),
400
EventQueuePriority::Idle);
401
}
402
}
403
404
/* static */
405
void ChromeUtils::Import(const GlobalObject& aGlobal,
406
const nsAString& aResourceURI,
407
const Optional<JS::Handle<JSObject*>>& aTargetObj,
408
JS::MutableHandle<JSObject*> aRetval,
409
ErrorResult& aRv) {
410
RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
411
MOZ_ASSERT(moduleloader);
412
413
NS_ConvertUTF16toUTF8 registryLocation(aResourceURI);
414
415
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("ChromeUtils::Import", OTHER,
416
registryLocation);
417
418
JSContext* cx = aGlobal.Context();
419
420
bool ignoreExports = aTargetObj.WasPassed() && !aTargetObj.Value();
421
422
JS::RootedObject global(cx);
423
JS::RootedObject exports(cx);
424
nsresult rv = moduleloader->Import(cx, registryLocation, &global, &exports,
425
ignoreExports);
426
if (NS_FAILED(rv)) {
427
aRv.Throw(rv);
428
return;
429
}
430
431
// Import() on the component loader can return NS_OK while leaving an
432
// exception on the JSContext. Check for that case.
433
if (JS_IsExceptionPending(cx)) {
434
aRv.NoteJSContextException(cx);
435
return;
436
}
437
438
if (ignoreExports) {
439
// Since we're ignoring exported symbols, return the module global rather
440
// than an exports object.
441
//
442
// Note: This behavior is deprecated, since it is incompatible with ES6
443
// module semantics, which don't include any such global object.
444
if (!JS_WrapObject(cx, &global)) {
445
aRv.Throw(NS_ERROR_FAILURE);
446
return;
447
}
448
aRetval.set(global);
449
return;
450
}
451
452
if (aTargetObj.WasPassed()) {
453
if (!JS_AssignObject(cx, aTargetObj.Value(), exports)) {
454
aRv.Throw(NS_ERROR_FAILURE);
455
return;
456
}
457
}
458
459
if (!JS_WrapObject(cx, &exports)) {
460
aRv.Throw(NS_ERROR_FAILURE);
461
return;
462
}
463
aRetval.set(exports);
464
}
465
466
namespace module_getter {
467
static const size_t SLOT_ID = 0;
468
static const size_t SLOT_URI = 1;
469
470
static bool ExtractArgs(JSContext* aCx, JS::CallArgs& aArgs,
471
JS::MutableHandle<JSObject*> aCallee,
472
JS::MutableHandle<JSObject*> aThisObj,
473
JS::MutableHandle<jsid> aId) {
474
aCallee.set(&aArgs.callee());
475
476
JS::Handle<JS::Value> thisv = aArgs.thisv();
477
if (!thisv.isObject()) {
478
JS_ReportErrorASCII(aCx, "Invalid target object");
479
return false;
480
}
481
482
aThisObj.set(&thisv.toObject());
483
484
JS::Rooted<JS::Value> id(aCx,
485
js::GetFunctionNativeReserved(aCallee, SLOT_ID));
486
MOZ_ALWAYS_TRUE(JS_ValueToId(aCx, id, aId));
487
return true;
488
}
489
490
static bool ModuleGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
491
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
492
493
JS::Rooted<JSObject*> callee(aCx);
494
JS::Rooted<JSObject*> thisObj(aCx);
495
JS::Rooted<jsid> id(aCx);
496
if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
497
return false;
498
}
499
500
JS::Rooted<JSString*> moduleURI(
501
aCx, js::GetFunctionNativeReserved(callee, SLOT_URI).toString());
502
JS::UniqueChars bytes = JS_EncodeStringToUTF8(aCx, moduleURI);
503
if (!bytes) {
504
return false;
505
}
506
nsDependentCString uri(bytes.get());
507
508
RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
509
MOZ_ASSERT(moduleloader);
510
511
JS::Rooted<JSObject*> moduleGlobal(aCx);
512
JS::Rooted<JSObject*> moduleExports(aCx);
513
nsresult rv = moduleloader->Import(aCx, uri, &moduleGlobal, &moduleExports);
514
if (NS_FAILED(rv)) {
515
Throw(aCx, rv);
516
return false;
517
}
518
519
JS::RootedValue value(aCx);
520
if (!JS_GetPropertyById(aCx, moduleExports, id, &value)) {
521
return false;
522
}
523
524
if (!JS_DefinePropertyById(aCx, thisObj, id, value, JSPROP_ENUMERATE)) {
525
return false;
526
}
527
528
args.rval().set(value);
529
return true;
530
}
531
532
static bool ModuleSetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
533
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
534
535
JS::Rooted<JSObject*> callee(aCx);
536
JS::Rooted<JSObject*> thisObj(aCx);
537
JS::Rooted<jsid> id(aCx);
538
if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
539
return false;
540
}
541
542
return JS_DefinePropertyById(aCx, thisObj, id, args.get(0), JSPROP_ENUMERATE);
543
}
544
545
static bool DefineGetter(JSContext* aCx, JS::Handle<JSObject*> aTarget,
546
const nsAString& aId, const nsAString& aResourceURI) {
547
JS::RootedValue uri(aCx);
548
JS::RootedValue idValue(aCx);
549
JS::Rooted<jsid> id(aCx);
550
if (!xpc::NonVoidStringToJsval(aCx, aResourceURI, &uri) ||
551
!xpc::NonVoidStringToJsval(aCx, aId, &idValue) ||
552
!JS_ValueToId(aCx, idValue, &id)) {
553
return false;
554
}
555
idValue = js::IdToValue(id);
556
557
JS::Rooted<JSObject*> getter(
558
aCx, JS_GetFunctionObject(
559
js::NewFunctionByIdWithReserved(aCx, ModuleGetter, 0, 0, id)));
560
561
JS::Rooted<JSObject*> setter(
562
aCx, JS_GetFunctionObject(
563
js::NewFunctionByIdWithReserved(aCx, ModuleSetter, 0, 0, id)));
564
565
if (!getter || !setter) {
566
JS_ReportOutOfMemory(aCx);
567
return false;
568
}
569
570
js::SetFunctionNativeReserved(getter, SLOT_ID, idValue);
571
js::SetFunctionNativeReserved(setter, SLOT_ID, idValue);
572
573
js::SetFunctionNativeReserved(getter, SLOT_URI, uri);
574
575
return JS_DefinePropertyById(
576
aCx, aTarget, id, getter, setter,
577
JSPROP_GETTER | JSPROP_SETTER | JSPROP_ENUMERATE);
578
}
579
} // namespace module_getter
580
581
/* static */
582
void ChromeUtils::DefineModuleGetter(const GlobalObject& global,
583
JS::Handle<JSObject*> target,
584
const nsAString& id,
585
const nsAString& resourceURI,
586
ErrorResult& aRv) {
587
if (!module_getter::DefineGetter(global.Context(), target, id, resourceURI)) {
588
aRv.NoteJSContextException(global.Context());
589
}
590
}
591
592
/* static */
593
void ChromeUtils::OriginAttributesToSuffix(
594
dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
595
nsCString& aSuffix)
596
597
{
598
OriginAttributes attrs(aAttrs);
599
attrs.CreateSuffix(aSuffix);
600
}
601
602
/* static */
603
bool ChromeUtils::OriginAttributesMatchPattern(
604
dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
605
const dom::OriginAttributesPatternDictionary& aPattern) {
606
OriginAttributes attrs(aAttrs);
607
OriginAttributesPattern pattern(aPattern);
608
return pattern.Matches(attrs);
609
}
610
611
/* static */
612
void ChromeUtils::CreateOriginAttributesFromOrigin(
613
dom::GlobalObject& aGlobal, const nsAString& aOrigin,
614
dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv) {
615
OriginAttributes attrs;
616
nsAutoCString suffix;
617
if (!attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(aOrigin), suffix)) {
618
aRv.Throw(NS_ERROR_FAILURE);
619
return;
620
}
621
aAttrs = attrs;
622
}
623
624
/* static */
625
void ChromeUtils::FillNonDefaultOriginAttributes(
626
dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
627
dom::OriginAttributesDictionary& aNewAttrs) {
628
aNewAttrs = aAttrs;
629
}
630
631
/* static */
632
bool ChromeUtils::IsOriginAttributesEqual(
633
dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aA,
634
const dom::OriginAttributesDictionary& aB) {
635
return IsOriginAttributesEqual(aA, aB);
636
}
637
638
/* static */
639
bool ChromeUtils::IsOriginAttributesEqual(
640
const dom::OriginAttributesDictionary& aA,
641
const dom::OriginAttributesDictionary& aB) {
642
return aA == aB;
643
}
644
645
#ifdef NIGHTLY_BUILD
646
/* static */
647
void ChromeUtils::GetRecentJSDevError(GlobalObject& aGlobal,
648
JS::MutableHandleValue aRetval,
649
ErrorResult& aRv) {
650
aRetval.setUndefined();
651
auto runtime = CycleCollectedJSRuntime::Get();
652
MOZ_ASSERT(runtime);
653
654
auto cx = aGlobal.Context();
655
if (!runtime->GetRecentDevError(cx, aRetval)) {
656
aRv.NoteJSContextException(cx);
657
return;
658
}
659
}
660
661
/* static */
662
void ChromeUtils::ClearRecentJSDevError(GlobalObject&) {
663
auto runtime = CycleCollectedJSRuntime::Get();
664
MOZ_ASSERT(runtime);
665
666
runtime->ClearRecentDevError();
667
}
668
#endif // NIGHTLY_BUILD
669
670
#define PROCTYPE_TO_WEBIDL_CASE(_procType, _webidl) \
671
case mozilla::ProcType::_procType: \
672
return WebIDLProcType::_webidl
673
674
static WebIDLProcType ProcTypeToWebIDL(mozilla::ProcType aType) {
675
// |strings| contains an extra non-enum value, so subtract one.
676
// Max is the value of the last enum, not the length, so add one.
677
static_assert(ArrayLength(WebIDLProcTypeValues::strings) - 1 ==
678
static_cast<size_t>(ProcType::Max) + 1,
679
"In order for this static cast to be okay, "
680
"WebIDLProcType must match ProcType exactly");
681
682
switch (aType) {
683
PROCTYPE_TO_WEBIDL_CASE(Web, Web);
684
PROCTYPE_TO_WEBIDL_CASE(File, File);
685
PROCTYPE_TO_WEBIDL_CASE(Extension, Extension);
686
PROCTYPE_TO_WEBIDL_CASE(PrivilegedAbout, Privilegedabout);
687
PROCTYPE_TO_WEBIDL_CASE(WebLargeAllocation, WebLargeAllocation);
688
PROCTYPE_TO_WEBIDL_CASE(Browser, Browser);
689
PROCTYPE_TO_WEBIDL_CASE(Plugin, Plugin);
690
PROCTYPE_TO_WEBIDL_CASE(IPDLUnitTest, IpdlUnitTest);
691
PROCTYPE_TO_WEBIDL_CASE(GMPlugin, GmpPlugin);
692
PROCTYPE_TO_WEBIDL_CASE(GPU, Gpu);
693
PROCTYPE_TO_WEBIDL_CASE(VR, Vr);
694
PROCTYPE_TO_WEBIDL_CASE(RDD, Rdd);
695
PROCTYPE_TO_WEBIDL_CASE(Socket, Socket);
696
PROCTYPE_TO_WEBIDL_CASE(RemoteSandboxBroker, RemoteSandboxBroker);
697
PROCTYPE_TO_WEBIDL_CASE(Unknown, Unknown);
698
}
699
700
MOZ_ASSERT(false, "Unhandled case in ProcTypeToWebIDL");
701
return WebIDLProcType::Unknown;
702
}
703
704
#undef PROCTYPE_TO_WEBIDL_CASE
705
706
/* static */
707
already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
708
ErrorResult& aRv) {
709
// This function will use IPDL to enable threads info on macOS
711
if (!XRE_IsParentProcess()) {
712
aRv.Throw(NS_ERROR_FAILURE);
713
return nullptr;
714
}
715
// Creating a JS promise
716
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
717
MOZ_ASSERT(global);
718
RefPtr<Promise> domPromise = Promise::Create(global, aRv);
719
if (NS_WARN_IF(aRv.Failed())) {
720
return nullptr;
721
}
722
MOZ_ASSERT(domPromise);
723
724
base::ProcessId parentPid = base::GetCurrentProcId();
725
RefPtr<nsISerialEventTarget> target =
726
global->EventTargetFor(TaskCategory::Performance);
727
728
// Getting the parent proc info
729
mozilla::GetProcInfo(parentPid, 0, mozilla::ProcType::Browser)
730
->Then(
731
target, __func__,
732
[target, domPromise, parentPid](ProcInfo aParentInfo) {
733
// Get a list of ContentParent
734
nsTArray<ContentParent*> contentParents;
735
ContentParent::GetAll(contentParents);
736
nsTArray<RefPtr<ProcInfoPromise>> promises;
737
mozilla::ipc::GeckoChildProcessHost::GetAll(
738
[&promises, contentParents](
739
mozilla::ipc::GeckoChildProcessHost* aGeckoProcess) {
740
if (!aGeckoProcess->GetChildProcessHandle()) {
741
return;
742
}
743
744
base::ProcessId childPid =
745
base::GetProcId(aGeckoProcess->GetChildProcessHandle());
746
int32_t childId = 0;
747
mozilla::ProcType type = mozilla::ProcType::Unknown;
748
switch (aGeckoProcess->GetProcessType()) {
749
case GeckoProcessType::GeckoProcessType_Content: {
750
ContentParent* contentParent = nullptr;
751
// This loop can become slow as we get more processes in
752
// Fission, so might need some refactoring in the future.
753
for (ContentParent* parent : contentParents) {
754
// find the match
755
if (parent->Process() == aGeckoProcess) {
756
contentParent = parent;
757
break;
758
}
759
}
760
if (!contentParent) {
761
return;
762
}
763
// Converting the Content Type into a ProcType
764
nsAutoString processType;
765
processType.Assign(contentParent->GetRemoteType());
766
if (IsWebRemoteType(processType)) {
767
type = mozilla::ProcType::Web;
768
} else if (processType.EqualsLiteral(FILE_REMOTE_TYPE)) {
769
type = mozilla::ProcType::File;
770
} else if (processType.EqualsLiteral(
771
EXTENSION_REMOTE_TYPE)) {
772
type = mozilla::ProcType::Extension;
773
} else if (processType.EqualsLiteral(
774
PRIVILEGEDABOUT_REMOTE_TYPE)) {
775
type = mozilla::ProcType::PrivilegedAbout;
776
} else if (processType.EqualsLiteral(
777
LARGE_ALLOCATION_REMOTE_TYPE)) {
778
type = mozilla::ProcType::WebLargeAllocation;
779
}
780
childId = contentParent->ChildID();
781
break;
782
}
783
case GeckoProcessType::GeckoProcessType_Default:
784
type = mozilla::ProcType::Browser;
785
break;
786
case GeckoProcessType::GeckoProcessType_Plugin:
787
type = mozilla::ProcType::Plugin;
788
break;
789
case GeckoProcessType::GeckoProcessType_GMPlugin:
790
type = mozilla::ProcType::GMPlugin;
791
break;
792
case GeckoProcessType::GeckoProcessType_GPU:
793
type = mozilla::ProcType::GPU;
794
break;
795
case GeckoProcessType::GeckoProcessType_VR:
796
type = mozilla::ProcType::VR;
797
break;
798
case GeckoProcessType::GeckoProcessType_RDD:
799
type = mozilla::ProcType::RDD;
800
break;
801
case GeckoProcessType::GeckoProcessType_Socket:
802
type = mozilla::ProcType::Socket;
803
break;
804
case GeckoProcessType::GeckoProcessType_RemoteSandboxBroker:
805
type = mozilla::ProcType::RemoteSandboxBroker;
806
break;
807
default:
808
// Leave the default Unknown value in |type|.
809
break;
810
}
811
812
promises.AppendElement(
813
#ifdef XP_MACOSX
814
mozilla::GetProcInfo(childPid, childId, type,
815
aGeckoProcess->GetChildTask())
816
#else
817
mozilla::GetProcInfo(childPid, childId, type)
818
#endif
819
);
820
});
821
822
auto ProcInfoResolver =
823
[domPromise, parentPid, parentInfo = aParentInfo](
824
const nsTArray<ProcInfo>& aChildrenInfo) {
825
mozilla::dom::ParentProcInfoDictionary procInfo;
826
// parent, basic info.
827
procInfo.mPid = parentPid;
828
procInfo.mFilename.Assign(parentInfo.filename);
829
procInfo.mType = mozilla::dom::WebIDLProcType::Browser;
830
procInfo.mVirtualMemorySize = parentInfo.virtualMemorySize;
831
procInfo.mResidentSetSize = parentInfo.residentSetSize;
832
procInfo.mCpuUser = parentInfo.cpuUser;
833
procInfo.mCpuKernel = parentInfo.cpuKernel;
834
835
// parent, threads info.
836
mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary>
837
threads;
838
for (const ThreadInfo& entry : parentInfo.threads) {
839
ThreadInfoDictionary* thread =
840
threads.AppendElement(fallible);
841
if (NS_WARN_IF(!thread)) {
842
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
843
return;
844
}
845
thread->mCpuUser = entry.cpuUser;
846
thread->mCpuKernel = entry.cpuKernel;
847
thread->mTid = entry.tid;
848
}
849
procInfo.mThreads = threads;
850
851
mozilla::dom::Sequence<mozilla::dom::ChildProcInfoDictionary>
852
children;
853
for (const ProcInfo& info : aChildrenInfo) {
854
ChildProcInfoDictionary* childProcInfo =
855
children.AppendElement(fallible);
856
if (NS_WARN_IF(!childProcInfo)) {
857
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
858
return;
859
}
860
// Basic info.
861
childProcInfo->mChildID = info.childId;
862
childProcInfo->mType = ProcTypeToWebIDL(info.type);
863
childProcInfo->mPid = info.pid;
864
childProcInfo->mFilename.Assign(info.filename);
865
childProcInfo->mVirtualMemorySize = info.virtualMemorySize;
866
childProcInfo->mResidentSetSize = info.residentSetSize;
867
childProcInfo->mCpuUser = info.cpuUser;
868
childProcInfo->mCpuKernel = info.cpuKernel;
869
870
// Threads info.
871
mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary>
872
threads;
873
for (const ThreadInfo& entry : info.threads) {
874
ThreadInfoDictionary* thread =
875
threads.AppendElement(fallible);
876
if (NS_WARN_IF(!thread)) {
877
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
878
return;
879
}
880
thread->mCpuUser = entry.cpuUser;
881
thread->mCpuKernel = entry.cpuKernel;
882
thread->mTid = entry.tid;
883
thread->mName.Assign(entry.name);
884
}
885
childProcInfo->mThreads = threads;
886
}
887
procInfo.mChildren = children;
888
domPromise->MaybeResolve(procInfo);
889
}; // end of ProcInfoResolver
890
891
ProcInfoPromise::All(target, promises)
892
->Then(target, __func__, std::move(ProcInfoResolver),
893
[domPromise](const nsresult aResult) {
894
domPromise->MaybeReject(aResult);
895
}); // end of ProcInfoPromise::All
896
},
897
[domPromise](nsresult aRv) {
898
domPromise->MaybeReject(aRv);
899
}); // end of mozilla::GetProcInfo
900
901
// sending back the promise instance
902
return domPromise.forget();
903
}
904
905
/* static */
906
already_AddRefed<Promise> ChromeUtils::RequestPerformanceMetrics(
907
GlobalObject& aGlobal, ErrorResult& aRv) {
908
MOZ_ASSERT(XRE_IsParentProcess());
909
910
// Creating a JS promise
911
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
912
MOZ_ASSERT(global);
913
RefPtr<Promise> domPromise = Promise::Create(global, aRv);
914
if (NS_WARN_IF(aRv.Failed())) {
915
return nullptr;
916
}
917
MOZ_ASSERT(domPromise);
918
RefPtr<nsISerialEventTarget> target =
919
global->EventTargetFor(TaskCategory::Performance);
920
921
// requesting metrics, that will be returned into the promise
922
PerformanceMetricsCollector::RequestMetrics()->Then(
923
target, __func__,
924
[domPromise,
925
target](nsTArray<dom::PerformanceInfoDictionary>&& aResults) {
926
domPromise->MaybeResolve(std::move(aResults));
927
},
928
[domPromise](const nsresult& aRv) { domPromise->MaybeReject(aRv); });
929
930
// sending back the promise instance
931
return domPromise.forget();
932
}
933
934
void ChromeUtils::SetPerfStatsCollectionMask(GlobalObject& aGlobal,
935
uint64_t aMask) {
936
PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask));
937
}
938
939
already_AddRefed<Promise> ChromeUtils::CollectPerfStats(GlobalObject& aGlobal,
940
ErrorResult& aRv) {
941
// Creating a JS promise
942
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
943
MOZ_ASSERT(global);
944
945
RefPtr<Promise> promise = Promise::Create(global, aRv);
946
if (aRv.Failed()) {
947
return nullptr;
948
}
949
950
RefPtr<PerfStats::PerfStatsPromise> extPromise =
951
PerfStats::CollectPerfStatsJSON();
952
953
extPromise->Then(
954
GetCurrentThreadSerialEventTarget(), __func__,
955
[promise](const nsCString& aResult) {
956
promise->MaybeResolve(NS_ConvertUTF8toUTF16(aResult));
957
},
958
[promise](bool aValue) { promise->MaybeReject(NS_ERROR_FAILURE); });
959
960
return promise.forget();
961
}
962
963
constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
964
965
/* static */
966
void ChromeUtils::GetCallerLocation(const GlobalObject& aGlobal,
967
nsIPrincipal* aPrincipal,
968
JS::MutableHandle<JSObject*> aRetval) {
969
JSContext* cx = aGlobal.Context();
970
971
auto* principals = nsJSPrincipals::get(aPrincipal);
972
973
JS::StackCapture captureMode(JS::FirstSubsumedFrame(cx, principals));
974
975
JS::RootedObject frame(cx);
976
if (!JS::CaptureCurrentStack(cx, &frame, std::move(captureMode))) {
977
JS_ClearPendingException(cx);
978
aRetval.set(nullptr);
979
return;
980
}
981
982
// FirstSubsumedFrame gets us a stack which stops at the first principal which
983
// is subsumed by the given principal. That means that we may have a lot of
984
// privileged frames that we don't care about at the top of the stack, though.
985
// We need to filter those out to get the frame we actually want.
986
aRetval.set(
987
js::GetFirstSubsumedSavedFrame(cx, principals, frame, kSkipSelfHosted));
988
}
989
990
/* static */
991
void ChromeUtils::CreateError(const GlobalObject& aGlobal,
992
const nsAString& aMessage,
993
JS::Handle<JSObject*> aStack,
994
JS::MutableHandle<JSObject*> aRetVal,
995
ErrorResult& aRv) {
996
if (aStack && !JS::IsMaybeWrappedSavedFrame(aStack)) {
997
aRv.Throw(NS_ERROR_INVALID_ARG);
998
return;
999
}
1000
1001
JSContext* cx = aGlobal.Context();
1002
1003
auto cleanup = MakeScopeExit([&]() { aRv.NoteJSContextException(cx); });
1004
1005
JS::RootedObject retVal(cx);
1006
{
1007
JS::RootedString fileName(cx, JS_GetEmptyString(cx));
1008
uint32_t line = 0;
1009
uint32_t column = 0;
1010
1011
Maybe<JSAutoRealm> ar;
1012
JS::RootedObject stack(cx);
1013
if (aStack) {
1014
stack = UncheckedUnwrap(aStack);
1015
ar.emplace(cx, stack);
1016
1017
JSPrincipals* principals =
1018
JS::GetRealmPrincipals(js::GetContextRealm(cx));
1019
if (JS::GetSavedFrameLine(cx, principals, stack, &line) !=
1020
JS::SavedFrameResult::Ok ||
1021
JS::GetSavedFrameColumn(cx, principals, stack, &column) !=
1022
JS::SavedFrameResult::Ok ||
1023
JS::GetSavedFrameSource(cx, principals, stack, &fileName) !=
1024
JS::SavedFrameResult::Ok) {
1025
return;
1026
}
1027
}
1028
1029
JS::RootedString message(cx);
1030
{
1031
JS::RootedValue msgVal(cx);
1032
if (!xpc::NonVoidStringToJsval(cx, aMessage, &msgVal)) {
1033
return;
1034
}
1035
message = msgVal.toString();
1036
}
1037
1038
JS::Rooted<JS::Value> err(cx);
1039
if (!JS::CreateError(cx, JSEXN_ERR, stack, fileName, line, column, nullptr,
1040
message, &err)) {
1041
return;
1042
}
1043
1044
MOZ_ASSERT(err.isObject());
1045
retVal = &err.toObject();
1046
}
1047
1048
if (aStack && !JS_WrapObject(cx, &retVal)) {
1049
return;
1050
}
1051
1052
cleanup.release();
1053
aRetVal.set(retVal);
1054
}
1055
1056
/* static */
1057
already_AddRefed<Promise> ChromeUtils::RequestIOActivity(GlobalObject& aGlobal,
1058
ErrorResult& aRv) {
1059
MOZ_ASSERT(XRE_IsParentProcess());
1060
MOZ_ASSERT(Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false));
1061
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
1062
MOZ_ASSERT(global);
1063
RefPtr<Promise> domPromise = Promise::Create(global, aRv);
1064
if (NS_WARN_IF(aRv.Failed())) {
1065
return nullptr;
1066
}
1067
MOZ_ASSERT(domPromise);
1068
mozilla::net::IOActivityMonitor::RequestActivities(domPromise);
1069
return domPromise.forget();
1070
}
1071
1072
/* static */
1073
bool ChromeUtils::HasReportingHeaderForOrigin(GlobalObject& global,
1074
const nsAString& aOrigin,
1075
ErrorResult& aRv) {
1076
if (!XRE_IsParentProcess()) {
1077
aRv.Throw(NS_ERROR_FAILURE);
1078
return false;
1079
}
1080
1081
return ReportingHeader::HasReportingHeaderForOrigin(
1082
NS_ConvertUTF16toUTF8(aOrigin));
1083
}
1084
1085
/* static */
1086
PopupBlockerState ChromeUtils::GetPopupControlState(GlobalObject& aGlobal) {
1087
switch (PopupBlocker::GetPopupControlState()) {
1088
case PopupBlocker::PopupControlState::openAllowed:
1089
return PopupBlockerState::OpenAllowed;
1090
1091
case PopupBlocker::PopupControlState::openControlled:
1092
return PopupBlockerState::OpenControlled;
1093
1094
case PopupBlocker::PopupControlState::openBlocked:
1095
return PopupBlockerState::OpenBlocked;
1096
1097
case PopupBlocker::PopupControlState::openAbused:
1098
return PopupBlockerState::OpenAbused;
1099
1100
case PopupBlocker::PopupControlState::openOverridden:
1101
return PopupBlockerState::OpenOverridden;
1102
1103
default:
1104
MOZ_CRASH(
1105
"PopupBlocker::PopupControlState and PopupBlockerState are out of "
1106
"sync");
1107
}
1108
}
1109
1110
/* static */
1111
bool ChromeUtils::IsPopupTokenUnused(GlobalObject& aGlobal) {
1112
return PopupBlocker::IsPopupOpeningTokenUnused();
1113
}
1114
1115
/* static */
1116
double ChromeUtils::LastExternalProtocolIframeAllowed(GlobalObject& aGlobal) {
1117
TimeStamp when = PopupBlocker::WhenLastExternalProtocolIframeAllowed();
1118
if (when.IsNull()) {
1119
return 0;
1120
}
1121
1122
TimeDuration duration = TimeStamp::Now() - when;
1123
return duration.ToMilliseconds();
1124
}
1125
1126
/* static */
1127
void ChromeUtils::ResetLastExternalProtocolIframeAllowed(
1128
GlobalObject& aGlobal) {
1129
PopupBlocker::ResetLastExternalProtocolIframeAllowed();
1130
}
1131
1132
/* static */
1133
void ChromeUtils::RegisterWindowActor(const GlobalObject& aGlobal,
1134
const nsAString& aName,
1135
const WindowActorOptions& aOptions,
1136
ErrorResult& aRv) {
1137
MOZ_ASSERT(XRE_IsParentProcess());
1138
1139
RefPtr<JSWindowActorService> service = JSWindowActorService::GetSingleton();
1140
service->RegisterWindowActor(aName, aOptions, aRv);
1141
}
1142
1143
/* static */
1144
void ChromeUtils::UnregisterWindowActor(const GlobalObject& aGlobal,
1145
const nsAString& aName) {
1146
MOZ_ASSERT(XRE_IsParentProcess());
1147
1148
RefPtr<JSWindowActorService> service = JSWindowActorService::GetSingleton();
1149
service->UnregisterWindowActor(aName);
1150
}
1151
1152
/* static */
1153
bool ChromeUtils::IsClassifierBlockingErrorCode(GlobalObject& aGlobal,
1154
uint32_t aError) {
1155
return net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
1156
static_cast<nsresult>(aError));
1157
}
1158
1159
/* static */
1160
void ChromeUtils::PrivateNoteIntentionalCrash(const GlobalObject& aGlobal,
1161
ErrorResult& aError) {
1162
if (XRE_IsContentProcess()) {
1163
NoteIntentionalCrash("tab");
1164
return;
1165
}
1166
aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
1167
}
1168
1169
} // namespace dom
1170
} // namespace mozilla