ChromeUtils.cpp (92852B)
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 "JSOracleParent.h" 10 #include "ThirdPartyUtil.h" 11 #include "VsyncSource.h" 12 #include "WrapperFactory.h" 13 #include "imgLoader.h" 14 #include "js/CallAndConstruct.h" // JS::Call 15 #include "js/CharacterEncoding.h" 16 #include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin 17 #include "js/Date.h" // JS::IsISOStyleDate 18 #include "js/Object.h" // JS::GetClass 19 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_SetProperty, JS_SetPropertyById, JS::IdVector 20 #include "js/PropertyDescriptor.h" // JS::PropertyDescriptor, JS_GetOwnPropertyDescriptorById 21 #include "js/SavedFrameAPI.h" 22 #include "js/Value.h" // JS::Value, JS::StringValue 23 #include "jsfriendapi.h" 24 #include "mozJSModuleLoader.h" 25 #include "mozilla/Base64.h" 26 #include "mozilla/ControllerCommand.h" 27 #include "mozilla/CycleCollectedJSRuntime.h" 28 #include "mozilla/ErrorNames.h" 29 #include "mozilla/EventStateManager.h" 30 #include "mozilla/FormAutofillNative.h" 31 #include "mozilla/IntentionalCrash.h" 32 #include "mozilla/KeySystemConfig.h" 33 #include "mozilla/PerfStats.h" 34 #include "mozilla/Preferences.h" 35 #include "mozilla/ProcInfo.h" 36 #include "mozilla/ProfilerLabels.h" 37 #include "mozilla/ProfilerMarkers.h" 38 #include "mozilla/RemoteMediaManagerChild.h" 39 #include "mozilla/ScopeExit.h" 40 #include "mozilla/ScrollingMetrics.h" 41 #include "mozilla/SharedStyleSheetCache.h" 42 #include "mozilla/SpinEventLoopUntil.h" 43 #include "mozilla/TimeStamp.h" 44 #include "mozilla/WheelHandlingHelper.h" 45 #include "mozilla/dom/ContentParent.h" 46 #include "mozilla/dom/IdleDeadline.h" 47 #include "mozilla/dom/InProcessParent.h" 48 #include "mozilla/dom/JSActorService.h" 49 #include "mozilla/dom/MediaSessionBinding.h" 50 #include "mozilla/dom/PBrowserParent.h" 51 #include "mozilla/dom/PopupBlocker.h" 52 #include "mozilla/dom/Promise.h" 53 #include "mozilla/dom/Record.h" 54 #include "mozilla/dom/ReportingHeader.h" 55 #include "mozilla/dom/SharedScriptCache.h" 56 #include "mozilla/dom/UnionTypes.h" 57 #include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options 58 #include "mozilla/dom/WindowGlobalParent.h" 59 #include "mozilla/dom/WorkerScope.h" 60 #include "mozilla/dom/quota/QuotaManager.h" 61 #include "mozilla/image/FetchDecodedImage.h" 62 #include "mozilla/ipc/GeckoChildProcessHost.h" 63 #include "mozilla/ipc/UtilityProcessHost.h" 64 #include "mozilla/ipc/UtilityProcessManager.h" 65 #include "mozilla/ipc/UtilityProcessSandboxing.h" 66 #include "mozilla/layers/WebRenderBridgeChild.h" 67 #include "mozilla/layers/WebRenderLayerManager.h" 68 #include "mozilla/net/UrlClassifierFeatureFactory.h" 69 #include "nsContentUtils.h" 70 #include "nsControllerCommandTable.h" 71 #include "nsDocShell.h" 72 #include "nsIException.h" 73 #include "nsIRFPTargetSetIDL.h" 74 #include "nsIWidget.h" 75 #include "nsNativeTheme.h" 76 #include "nsRFPTargetSetIDL.h" 77 #include "nsString.h" 78 #include "nsThreadUtils.h" 79 80 #ifdef XP_UNIX 81 # include <errno.h> 82 # include <fcntl.h> 83 # include <poll.h> 84 # include <sys/wait.h> 85 # include <unistd.h> 86 87 # ifdef XP_LINUX 88 # include <sys/prctl.h> 89 # endif 90 #endif 91 92 #ifdef MOZ_WMF_CDM 93 # include "mozilla/MFCDMParent.h" 94 #endif 95 96 #ifdef MOZ_WIDGET_ANDROID 97 # include "mozilla/java/GeckoAppShellWrappers.h" 98 #endif 99 100 namespace mozilla::dom { 101 102 // Setup logging 103 extern mozilla::LazyLogModule gMlsLog; 104 105 /* static */ 106 void ChromeUtils::NondeterministicGetWeakMapKeys( 107 GlobalObject& aGlobal, JS::Handle<JS::Value> aMap, 108 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) { 109 if (!aMap.isObject()) { 110 aRetval.setUndefined(); 111 } else { 112 JSContext* cx = aGlobal.Context(); 113 JS::Rooted<JSObject*> objRet(cx); 114 JS::Rooted<JSObject*> mapObj(cx, &aMap.toObject()); 115 if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &objRet)) { 116 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 117 } else { 118 aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue()); 119 } 120 } 121 } 122 123 /* static */ 124 void ChromeUtils::NondeterministicGetWeakSetKeys( 125 GlobalObject& aGlobal, JS::Handle<JS::Value> aSet, 126 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) { 127 if (!aSet.isObject()) { 128 aRetval.setUndefined(); 129 } else { 130 JSContext* cx = aGlobal.Context(); 131 JS::Rooted<JSObject*> objRet(cx); 132 JS::Rooted<JSObject*> setObj(cx, &aSet.toObject()); 133 if (!JS_NondeterministicGetWeakSetKeys(cx, setObj, &objRet)) { 134 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 135 } else { 136 aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue()); 137 } 138 } 139 } 140 141 /* static */ 142 void ChromeUtils::Base64URLEncode(GlobalObject& aGlobal, 143 const ArrayBufferViewOrArrayBuffer& aSource, 144 const Base64URLEncodeOptions& aOptions, 145 nsACString& aResult, ErrorResult& aRv) { 146 auto paddingPolicy = aOptions.mPad ? Base64URLEncodePaddingPolicy::Include 147 : Base64URLEncodePaddingPolicy::Omit; 148 ProcessTypedArrays( 149 aSource, [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) { 150 nsresult rv = mozilla::Base64URLEncode(aData.Length(), aData.Elements(), 151 paddingPolicy, aResult); 152 if (NS_WARN_IF(NS_FAILED(rv))) { 153 aResult.Truncate(); 154 aRv.Throw(rv); 155 } 156 }); 157 } 158 159 /* static */ 160 void ChromeUtils::Base64URLDecode(GlobalObject& aGlobal, 161 const nsACString& aString, 162 const Base64URLDecodeOptions& aOptions, 163 JS::MutableHandle<JSObject*> aRetval, 164 ErrorResult& aRv) { 165 Base64URLDecodePaddingPolicy paddingPolicy; 166 switch (aOptions.mPadding) { 167 case Base64URLDecodePadding::Require: 168 paddingPolicy = Base64URLDecodePaddingPolicy::Require; 169 break; 170 171 case Base64URLDecodePadding::Ignore: 172 paddingPolicy = Base64URLDecodePaddingPolicy::Ignore; 173 break; 174 175 case Base64URLDecodePadding::Reject: 176 paddingPolicy = Base64URLDecodePaddingPolicy::Reject; 177 break; 178 179 default: 180 aRv.Throw(NS_ERROR_INVALID_ARG); 181 return; 182 } 183 FallibleTArray<uint8_t> data; 184 nsresult rv = mozilla::Base64URLDecode(aString, paddingPolicy, data); 185 if (NS_WARN_IF(NS_FAILED(rv))) { 186 aRv.Throw(rv); 187 return; 188 } 189 190 JS::Rooted<JSObject*> buffer( 191 aGlobal.Context(), ArrayBuffer::Create(aGlobal.Context(), data, aRv)); 192 if (aRv.Failed()) { 193 return; 194 } 195 aRetval.set(buffer); 196 } 197 198 /* static */ 199 void ChromeUtils::ReleaseAssert(GlobalObject& aGlobal, bool aCondition, 200 const nsAString& aMessage) { 201 // If the condition didn't fail, which is the likely case, immediately return. 202 if (MOZ_LIKELY(aCondition)) { 203 return; 204 } 205 206 // Extract the current stack from the JS runtime to embed in the crash reason. 207 nsAutoCString filename; 208 uint32_t lineNo = 0; 209 210 if (nsCOMPtr<nsIStackFrame> location = GetCurrentJSStack(1)) { 211 location->GetFilename(aGlobal.Context(), filename); 212 lineNo = location->GetLineNumber(aGlobal.Context()); 213 } else { 214 filename.Assign("<unknown>"_ns); 215 } 216 217 // Convert to utf-8 for adding as the MozCrashReason. 218 NS_ConvertUTF16toUTF8 messageUtf8(aMessage); 219 220 // Actually crash. 221 MOZ_CRASH_UNSAFE_PRINTF("Failed ChromeUtils.releaseAssert(\"%s\") @ %s:%u", 222 messageUtf8.get(), filename.get(), lineNo); 223 } 224 225 /* static */ 226 void ChromeUtils::AddProfilerMarker( 227 GlobalObject& aGlobal, const nsACString& aName, 228 const ProfilerMarkerOptionsOrDouble& aOptions, 229 const Optional<nsACString>& aText) { 230 if (!profiler_thread_is_being_profiled_for_markers()) { 231 return; 232 } 233 234 MarkerOptions options; 235 236 MarkerCategory category = ::geckoprofiler::category::JS; 237 238 DOMHighResTimeStamp startTime = 0; 239 uint64_t innerWindowId = 0; 240 if (aOptions.IsDouble()) { 241 startTime = aOptions.GetAsDouble(); 242 } else { 243 const ProfilerMarkerOptions& opt = aOptions.GetAsProfilerMarkerOptions(); 244 startTime = opt.mStartTime; 245 innerWindowId = opt.mInnerWindowId; 246 247 if (opt.mCaptureStack) { 248 // If we will be capturing a stack, change the category of the 249 // ChromeUtils.addProfilerMarker label automatically added by the webidl 250 // binding from DOM to PROFILER so that this function doesn't appear in 251 // the marker stack. 252 JSContext* cx = aGlobal.Context(); 253 ProfilingStack* stack = js::GetContextProfilingStackIfEnabled(cx); 254 if (MOZ_LIKELY(stack)) { 255 uint32_t sp = stack->stackPointer; 256 if (MOZ_LIKELY(sp > 0)) { 257 js::ProfilingStackFrame& frame = stack->frames[sp - 1]; 258 if (frame.isLabelFrame() && "ChromeUtils"_ns.Equals(frame.label()) && 259 "addProfilerMarker"_ns.Equals(frame.dynamicString())) { 260 frame.setLabelCategory(JS::ProfilingCategoryPair::PROFILER); 261 } 262 } 263 } 264 265 options.Set(MarkerStack::Capture()); 266 } 267 #define BEGIN_CATEGORY(name, labelAsString, color) \ 268 if (opt.mCategory.Equals(labelAsString)) { \ 269 category = ::geckoprofiler::category::name; \ 270 } else 271 #define SUBCATEGORY(supercategory, name, labelAsString) 272 #define END_CATEGORY 273 MOZ_PROFILING_CATEGORY_LIST(BEGIN_CATEGORY, SUBCATEGORY, END_CATEGORY) 274 #undef BEGIN_CATEGORY 275 #undef SUBCATEGORY 276 #undef END_CATEGORY 277 { 278 category = ::geckoprofiler::category::OTHER; 279 } 280 } 281 if (startTime) { 282 options.Set(MarkerTiming::IntervalUntilNowFrom( 283 TimeStamp::ProcessCreation() + 284 TimeDuration::FromMilliseconds(startTime))); 285 } 286 287 if (innerWindowId) { 288 options.Set(MarkerInnerWindowId(innerWindowId)); 289 } else { 290 options.Set(MarkerInnerWindowIdFromJSContext(aGlobal.Context())); 291 } 292 293 { 294 AUTO_PROFILER_STATS(ChromeUtils_AddProfilerMarker); 295 if (aText.WasPassed()) { 296 profiler_add_marker(aName, category, std::move(options), 297 ::geckoprofiler::markers::TextMarker{}, 298 aText.Value()); 299 } else { 300 profiler_add_marker(aName, category, std::move(options)); 301 } 302 } 303 } 304 305 /* static */ 306 void ChromeUtils::GetXPCOMErrorName(GlobalObject& aGlobal, uint32_t aErrorCode, 307 nsACString& aRetval) { 308 GetErrorName((nsresult)aErrorCode, aRetval); 309 } 310 311 /* static */ 312 void ChromeUtils::WaiveXrays(GlobalObject& aGlobal, JS::Handle<JS::Value> aVal, 313 JS::MutableHandle<JS::Value> aRetval, 314 ErrorResult& aRv) { 315 JS::Rooted<JS::Value> value(aGlobal.Context(), aVal); 316 if (!xpc::WrapperFactory::WaiveXrayAndWrap(aGlobal.Context(), &value)) { 317 aRv.NoteJSContextException(aGlobal.Context()); 318 } else { 319 aRetval.set(value); 320 } 321 } 322 323 /* static */ 324 void ChromeUtils::UnwaiveXrays(GlobalObject& aGlobal, 325 JS::Handle<JS::Value> aVal, 326 JS::MutableHandle<JS::Value> aRetval, 327 ErrorResult& aRv) { 328 if (!aVal.isObject()) { 329 aRetval.set(aVal); 330 return; 331 } 332 333 JS::Rooted<JSObject*> obj(aGlobal.Context(), 334 js::UncheckedUnwrap(&aVal.toObject())); 335 if (!JS_WrapObject(aGlobal.Context(), &obj)) { 336 aRv.NoteJSContextException(aGlobal.Context()); 337 } else { 338 aRetval.setObject(*obj); 339 } 340 } 341 342 /* static */ 343 void ChromeUtils::GetClassName(GlobalObject& aGlobal, 344 JS::Handle<JSObject*> aObj, bool aUnwrap, 345 nsAString& aRetval) { 346 JS::Rooted<JSObject*> obj(aGlobal.Context(), aObj); 347 if (aUnwrap) { 348 obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); 349 } 350 351 aRetval = NS_ConvertUTF8toUTF16(nsDependentCString(JS::GetClass(obj)->name)); 352 } 353 354 /* static */ 355 bool ChromeUtils::IsDOMObject(GlobalObject& aGlobal, JS::Handle<JSObject*> aObj, 356 bool aUnwrap) { 357 JS::Rooted<JSObject*> obj(aGlobal.Context(), aObj); 358 if (aUnwrap) { 359 obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); 360 } 361 362 return mozilla::dom::IsDOMObject(obj); 363 } 364 365 /* static */ 366 bool ChromeUtils::IsISOStyleDate(GlobalObject& aGlobal, 367 const nsACString& aStr) { 368 // aStr is a UTF-8 string, however we can cast to JS::Latin1Chars 369 // because JS::IsISOStyleDate handles ASCII only 370 return JS::IsISOStyleDate(aGlobal.Context(), 371 JS::Latin1Chars(aStr.Data(), aStr.Length())); 372 } 373 374 /* static */ 375 void ChromeUtils::ShallowClone(GlobalObject& aGlobal, 376 JS::Handle<JSObject*> aObj, 377 JS::Handle<JSObject*> aTarget, 378 JS::MutableHandle<JSObject*> aRetval, 379 ErrorResult& aRv) { 380 JSContext* cx = aGlobal.Context(); 381 382 auto cleanup = MakeScopeExit([&]() { aRv.NoteJSContextException(cx); }); 383 384 JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx)); 385 JS::RootedVector<JS::Value> values(cx); 386 JS::RootedVector<jsid> valuesIds(cx); 387 388 { 389 // cx represents our current Realm, so it makes sense to use it for the 390 // CheckedUnwrapDynamic call. We do want CheckedUnwrapDynamic, in case 391 // someone is shallow-cloning a Window. 392 JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrapDynamic(aObj, cx)); 393 if (!obj) { 394 js::ReportAccessDenied(cx); 395 return; 396 } 397 398 if (js::IsScriptedProxy(obj)) { 399 JS_ReportErrorASCII(cx, "Shallow cloning a proxy object is not allowed"); 400 return; 401 } 402 403 JSAutoRealm ar(cx, obj); 404 405 if (!JS_Enumerate(cx, obj, &ids) || !values.reserve(ids.length()) || 406 !valuesIds.reserve(ids.length())) { 407 return; 408 } 409 410 JS::Rooted<Maybe<JS::PropertyDescriptor>> desc(cx); 411 JS::Rooted<JS::PropertyKey> id(cx); 412 for (jsid idVal : ids) { 413 id = idVal; 414 if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) { 415 continue; 416 } 417 if (desc.isNothing() || desc->isAccessorDescriptor()) { 418 continue; 419 } 420 valuesIds.infallibleAppend(id); 421 values.infallibleAppend(desc->value()); 422 } 423 } 424 425 JS::Rooted<JSObject*> obj(cx); 426 { 427 Maybe<JSAutoRealm> ar; 428 if (aTarget) { 429 // Our target could be anything, so we want CheckedUnwrapDynamic here. 430 // "cx" represents the current Realm when we were called from bindings, so 431 // we can just use that. 432 JS::Rooted<JSObject*> target(cx, js::CheckedUnwrapDynamic(aTarget, cx)); 433 if (!target) { 434 js::ReportAccessDenied(cx); 435 return; 436 } 437 ar.emplace(cx, target); 438 } 439 440 obj = JS_NewPlainObject(cx); 441 if (!obj) { 442 return; 443 } 444 445 JS::Rooted<JS::Value> value(cx); 446 JS::Rooted<JS::PropertyKey> id(cx); 447 for (uint32_t i = 0; i < valuesIds.length(); i++) { 448 id = valuesIds[i]; 449 value = values[i]; 450 451 JS_MarkCrossZoneId(cx, id); 452 if (!JS_WrapValue(cx, &value) || 453 !JS_SetPropertyById(cx, obj, id, value)) { 454 return; 455 } 456 } 457 } 458 459 if (aTarget && !JS_WrapObject(cx, &obj)) { 460 return; 461 } 462 463 cleanup.release(); 464 aRetval.set(obj); 465 } 466 467 namespace { 468 class IdleDispatchRunnable final : public IdleRunnable, 469 public nsITimerCallback { 470 public: 471 NS_DECL_ISUPPORTS_INHERITED 472 473 IdleDispatchRunnable(nsIGlobalObject* aParent, IdleRequestCallback& aCallback) 474 : IdleRunnable("ChromeUtils::IdleDispatch"), 475 mCallback(&aCallback), 476 mParent(aParent) {} 477 478 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. 479 // See bug 1535398. 480 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { 481 if (mCallback) { 482 CancelTimer(); 483 484 auto deadline = mDeadline - TimeStamp::ProcessCreation(); 485 486 ErrorResult rv; 487 RefPtr<IdleDeadline> idleDeadline = 488 new IdleDeadline(mParent, mTimedOut, deadline.ToMilliseconds()); 489 490 RefPtr<IdleRequestCallback> callback(std::move(mCallback)); 491 MOZ_ASSERT(!mCallback); 492 callback->Call(*idleDeadline, "ChromeUtils::IdleDispatch handler"); 493 mParent = nullptr; 494 } 495 return NS_OK; 496 } 497 498 void SetDeadline(TimeStamp aDeadline) override { mDeadline = aDeadline; } 499 500 NS_IMETHOD Notify(nsITimer* aTimer) override { 501 mTimedOut = true; 502 SetDeadline(TimeStamp::Now()); 503 return Run(); 504 } 505 506 void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override { 507 MOZ_ASSERT(aTarget); 508 MOZ_ASSERT(!mTimer); 509 NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aDelay, 510 nsITimer::TYPE_ONE_SHOT, aTarget); 511 } 512 513 protected: 514 virtual ~IdleDispatchRunnable() { CancelTimer(); } 515 516 private: 517 void CancelTimer() { 518 if (mTimer) { 519 mTimer->Cancel(); 520 mTimer = nullptr; 521 } 522 } 523 524 RefPtr<IdleRequestCallback> mCallback; 525 nsCOMPtr<nsIGlobalObject> mParent; 526 527 nsCOMPtr<nsITimer> mTimer; 528 529 TimeStamp mDeadline{}; 530 bool mTimedOut = false; 531 }; 532 533 NS_IMPL_ISUPPORTS_INHERITED(IdleDispatchRunnable, IdleRunnable, 534 nsITimerCallback) 535 } // anonymous namespace 536 537 /* static */ 538 void ChromeUtils::IdleDispatch(const GlobalObject& aGlobal, 539 IdleRequestCallback& aCallback, 540 const IdleRequestOptions& aOptions, 541 ErrorResult& aRv) { 542 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 543 MOZ_ASSERT(global); 544 545 auto runnable = MakeRefPtr<IdleDispatchRunnable>(global, aCallback); 546 547 if (aOptions.mTimeout.WasPassed()) { 548 aRv = NS_DispatchToCurrentThreadQueue( 549 runnable.forget(), aOptions.mTimeout.Value(), EventQueuePriority::Idle); 550 } else { 551 aRv = NS_DispatchToCurrentThreadQueue(runnable.forget(), 552 EventQueuePriority::Idle); 553 } 554 } 555 556 static mozJSModuleLoader* GetModuleLoaderForCurrentGlobal( 557 JSContext* aCx, const GlobalObject& aGlobal, 558 Maybe<loader::NonSharedGlobalSyncModuleLoaderScope>& 559 aMaybeSyncLoaderScope) { 560 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 561 562 if (mozJSModuleLoader::IsSharedSystemGlobal(global)) { 563 return mozJSModuleLoader::Get(); 564 } 565 if (mozJSModuleLoader::IsDevToolsLoaderGlobal(global)) { 566 return mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx); 567 } 568 569 if (loader::NonSharedGlobalSyncModuleLoaderScope::IsActive()) { 570 mozJSModuleLoader* moduleloader = 571 loader::NonSharedGlobalSyncModuleLoaderScope::ActiveLoader(); 572 573 if (!moduleloader->IsLoaderGlobal(global->GetGlobalJSObject())) { 574 JS_ReportErrorASCII(aCx, 575 "global: \"current\" option cannot be used for " 576 "different global while other importESModule " 577 "with global: \"current\" is on the stack"); 578 return nullptr; 579 } 580 581 return moduleloader; 582 } 583 584 RefPtr targetModuleLoader = global->GetModuleLoader(aCx); 585 if (!targetModuleLoader) { 586 // Sandbox without associated window returns nullptr for GetModuleLoader. 587 JS_ReportErrorASCII(aCx, "No ModuleLoader found for the current context"); 588 return nullptr; 589 } 590 591 if (targetModuleLoader->HasFetchingModules()) { 592 if (!NS_IsMainThread()) { 593 JS_ReportErrorASCII(aCx, 594 "ChromeUtils.importESModule cannot be used in worker " 595 "when there is ongoing dynamic import"); 596 return nullptr; 597 } 598 599 if (!mozilla::SpinEventLoopUntil( 600 "importESModule for current global"_ns, [&]() -> bool { 601 return !targetModuleLoader->HasFetchingModules(); 602 })) { 603 JS_ReportErrorASCII(aCx, "Failed to wait for ongoing module requests"); 604 return nullptr; 605 } 606 } 607 608 aMaybeSyncLoaderScope.emplace(aCx, global); 609 return aMaybeSyncLoaderScope->ActiveLoader(); 610 } 611 612 static mozJSModuleLoader* GetModuleLoaderForOptions( 613 JSContext* aCx, const GlobalObject& aGlobal, 614 const ImportESModuleOptionsDictionary& aOptions, 615 Maybe<loader::NonSharedGlobalSyncModuleLoaderScope>& 616 aMaybeSyncLoaderScope) { 617 if (!aOptions.mGlobal.WasPassed()) { 618 return mozJSModuleLoader::Get(); 619 } 620 621 switch (aOptions.mGlobal.Value()) { 622 case ImportESModuleTargetGlobal::Shared: 623 return mozJSModuleLoader::Get(); 624 625 case ImportESModuleTargetGlobal::Devtools: 626 return mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx); 627 628 case ImportESModuleTargetGlobal::Contextual: { 629 if (!NS_IsMainThread()) { 630 return GetModuleLoaderForCurrentGlobal(aCx, aGlobal, 631 aMaybeSyncLoaderScope); 632 } 633 634 RefPtr devToolsModuleloader = mozJSModuleLoader::GetDevToolsLoader(); 635 if (devToolsModuleloader && 636 devToolsModuleloader->IsLoaderGlobal(aGlobal.Get())) { 637 return mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx); 638 } 639 return mozJSModuleLoader::Get(); 640 } 641 642 case ImportESModuleTargetGlobal::Current: 643 return GetModuleLoaderForCurrentGlobal(aCx, aGlobal, 644 aMaybeSyncLoaderScope); 645 646 default: 647 MOZ_CRASH("Unknown ImportESModuleTargetGlobal"); 648 } 649 } 650 651 static bool ValidateImportOptions( 652 JSContext* aCx, const GlobalObject& aGlobal, 653 const ImportESModuleOptionsDictionary& aOptions) { 654 if (!NS_IsMainThread() && 655 (!aOptions.mGlobal.WasPassed() || 656 (aOptions.mGlobal.Value() != ImportESModuleTargetGlobal::Current && 657 aOptions.mGlobal.Value() != ImportESModuleTargetGlobal::Contextual))) { 658 JS_ReportErrorASCII(aCx, 659 "ChromeUtils.importESModule: Only { global: " 660 "\"current\" } and { global: \"contextual\" } options " 661 "are supported on worker"); 662 return false; 663 } 664 665 if (NS_IsMainThread()) { 666 nsCOMPtr<nsIGlobalObject> global = 667 do_QueryInterface(aGlobal.GetAsSupports()); 668 669 if (mozJSModuleLoader::IsDevToolsLoaderGlobal(global) && 670 !aOptions.mGlobal.WasPassed()) { 671 JS_ReportErrorASCII(aCx, 672 "ChromeUtils.importESModule: global option is " 673 "required in DevTools distinct global"); 674 return false; 675 } 676 } 677 678 return true; 679 } 680 681 /* static */ 682 void ChromeUtils::ImportESModule( 683 const GlobalObject& aGlobal, const nsAString& aResourceURI, 684 const ImportESModuleOptionsDictionary& aOptions, 685 JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) { 686 JSContext* cx = aGlobal.Context(); 687 688 if (!ValidateImportOptions(cx, aGlobal, aOptions)) { 689 aRv.Throw(NS_ERROR_FAILURE); 690 return; 691 } 692 693 Maybe<loader::NonSharedGlobalSyncModuleLoaderScope> maybeSyncLoaderScope; 694 RefPtr<mozJSModuleLoader> moduleloader = 695 GetModuleLoaderForOptions(cx, aGlobal, aOptions, maybeSyncLoaderScope); 696 if (!moduleloader) { 697 aRv.Throw(NS_ERROR_FAILURE); 698 return; 699 } 700 701 NS_ConvertUTF16toUTF8 registryLocation(aResourceURI); 702 703 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( 704 "ChromeUtils::ImportESModule", OTHER, registryLocation); 705 706 JS::Rooted<JSObject*> moduleNamespace(cx); 707 nsresult rv = 708 moduleloader->ImportESModule(cx, registryLocation, &moduleNamespace); 709 if (NS_FAILED(rv)) { 710 aRv.Throw(rv); 711 return; 712 } 713 714 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 715 716 if (!JS_WrapObject(cx, &moduleNamespace)) { 717 aRv.Throw(NS_ERROR_FAILURE); 718 return; 719 } 720 aRetval.set(moduleNamespace); 721 722 if (maybeSyncLoaderScope) { 723 maybeSyncLoaderScope->Finish(); 724 } 725 } 726 727 // An integer encoding for ImportESModuleOptionsDictionary, to pass the value 728 // to the lazy getters. 729 class EncodedOptions { 730 public: 731 explicit EncodedOptions(const ImportESModuleOptionsDictionary& aOptions) { 732 if (aOptions.mGlobal.WasPassed()) { 733 mValue = uint32_t(aOptions.mGlobal.Value()) + 1; 734 } else { 735 mValue = 0; 736 } 737 } 738 739 explicit EncodedOptions(uint32_t aValue) : mValue(aValue) {} 740 741 int32_t toInt32() const { return int32_t(mValue); } 742 743 void DecodeInto(ImportESModuleOptionsDictionary& aOptions) { 744 if (mValue == 0) { 745 aOptions.mGlobal.Reset(); 746 } else { 747 aOptions.mGlobal.Construct(ImportESModuleTargetGlobal(mValue - 1)); 748 } 749 } 750 751 private: 752 uint32_t mValue = 0; 753 }; 754 755 namespace lazy_getter { 756 757 // The property id of the getter. 758 // Used by all lazy getters. 759 static const size_t SLOT_ID = 0; 760 761 // The URI of the module to import. 762 // Used by ChromeUtils.defineESModuleGetters. 763 static const size_t SLOT_URI = 1; 764 765 // An array object that contians values for PARAM_INDEX_TARGET and 766 // PARAM_INDEX_LAMBDA. 767 // Used by ChromeUtils.defineLazyGetter. 768 static const size_t SLOT_PARAMS = 1; 769 770 // The EncodedOptions value. 771 // Used by ChromeUtils.defineESModuleGetters. 772 static const size_t SLOT_OPTIONS = 2; 773 774 static const size_t PARAM_INDEX_TARGET = 0; 775 static const size_t PARAM_INDEX_LAMBDA = 1; 776 static const size_t PARAMS_COUNT = 2; 777 778 static bool ExtractArgs(JSContext* aCx, JS::CallArgs& aArgs, 779 JS::MutableHandle<JSObject*> aCallee, 780 JS::MutableHandle<JSObject*> aThisObj, 781 JS::MutableHandle<jsid> aId) { 782 aCallee.set(&aArgs.callee()); 783 784 JS::Handle<JS::Value> thisv = aArgs.thisv(); 785 if (!thisv.isObject()) { 786 JS_ReportErrorASCII(aCx, "Invalid target object"); 787 return false; 788 } 789 790 aThisObj.set(&thisv.toObject()); 791 792 JS::Rooted<JS::Value> id(aCx, 793 js::GetFunctionNativeReserved(aCallee, SLOT_ID)); 794 MOZ_ALWAYS_TRUE(JS_ValueToId(aCx, id, aId)); 795 return true; 796 } 797 798 static bool JSLazyGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { 799 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); 800 801 JS::Rooted<JSObject*> callee(aCx); 802 JS::Rooted<JSObject*> unused(aCx); 803 JS::Rooted<jsid> id(aCx); 804 if (!ExtractArgs(aCx, args, &callee, &unused, &id)) { 805 return false; 806 } 807 808 JS::Rooted<JS::Value> paramsVal( 809 aCx, js::GetFunctionNativeReserved(callee, SLOT_PARAMS)); 810 if (paramsVal.isUndefined()) { 811 args.rval().setUndefined(); 812 return true; 813 } 814 // Avoid calling the lambda multiple times, in case of: 815 // * the getter function is retrieved from property descriptor and called 816 // * the lambda gets the property again 817 // * the getter function throws and accessed again 818 js::SetFunctionNativeReserved(callee, SLOT_PARAMS, JS::UndefinedHandleValue); 819 820 JS::Rooted<JSObject*> paramsObj(aCx, ¶msVal.toObject()); 821 822 JS::Rooted<JS::Value> targetVal(aCx); 823 JS::Rooted<JS::Value> lambdaVal(aCx); 824 if (!JS_GetElement(aCx, paramsObj, PARAM_INDEX_TARGET, &targetVal)) { 825 return false; 826 } 827 if (!JS_GetElement(aCx, paramsObj, PARAM_INDEX_LAMBDA, &lambdaVal)) { 828 return false; 829 } 830 831 JS::Rooted<JSObject*> targetObj(aCx, &targetVal.toObject()); 832 833 JS::Rooted<JS::Value> value(aCx); 834 if (!JS::Call(aCx, targetObj, lambdaVal, JS::HandleValueArray::empty(), 835 &value)) { 836 return false; 837 } 838 839 if (!JS_DefinePropertyById(aCx, targetObj, id, value, JSPROP_ENUMERATE)) { 840 return false; 841 } 842 843 args.rval().set(value); 844 return true; 845 } 846 847 static bool DefineLazyGetter(JSContext* aCx, JS::Handle<JSObject*> aTarget, 848 JS::Handle<JS::Value> aName, 849 JS::Handle<JSObject*> aLambda) { 850 JS::Rooted<JS::PropertyKey> id(aCx); 851 if (!JS_ValueToId(aCx, aName, &id)) { 852 return false; 853 } 854 855 JS::Rooted<JS::PropertyKey> funId(aCx); 856 if (id.isAtom()) { 857 funId = id; 858 } else { 859 // Don't care int and symbol cases. 860 funId = JS::PropertyKey::NonIntAtom(JS_GetEmptyString(aCx)); 861 } 862 863 JS::Rooted<JSObject*> getter( 864 aCx, JS_GetFunctionObject(js::NewFunctionByIdWithReserved( 865 aCx, JSLazyGetter, 0, 0, funId))); 866 if (!getter) { 867 JS_ReportOutOfMemory(aCx); 868 return false; 869 } 870 871 JS::RootedVector<JS::Value> params(aCx); 872 if (!params.resize(PARAMS_COUNT)) { 873 return false; 874 } 875 params[PARAM_INDEX_TARGET].setObject(*aTarget); 876 params[PARAM_INDEX_LAMBDA].setObject(*aLambda); 877 JS::Rooted<JSObject*> paramsObj(aCx, JS::NewArrayObject(aCx, params)); 878 if (!paramsObj) { 879 return false; 880 } 881 882 js::SetFunctionNativeReserved(getter, SLOT_ID, aName); 883 js::SetFunctionNativeReserved(getter, SLOT_PARAMS, 884 JS::ObjectValue(*paramsObj)); 885 886 return JS_DefinePropertyById(aCx, aTarget, id, getter, nullptr, 887 JSPROP_ENUMERATE); 888 } 889 890 static bool ESModuleGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { 891 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); 892 893 JS::Rooted<JSObject*> callee(aCx); 894 JS::Rooted<JSObject*> thisObj(aCx); 895 JS::Rooted<jsid> id(aCx); 896 if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) { 897 return false; 898 } 899 900 JS::Rooted<JSString*> moduleURI( 901 aCx, js::GetFunctionNativeReserved(callee, SLOT_URI).toString()); 902 JS::UniqueChars bytes = JS_EncodeStringToUTF8(aCx, moduleURI); 903 if (!bytes) { 904 return false; 905 } 906 nsDependentCString uri(bytes.get()); 907 908 JS::Rooted<JS::Value> value(aCx); 909 EncodedOptions encodedOptions( 910 js::GetFunctionNativeReserved(callee, SLOT_OPTIONS).toInt32()); 911 912 ImportESModuleOptionsDictionary options; 913 encodedOptions.DecodeInto(options); 914 915 GlobalObject global(aCx, callee); 916 917 Maybe<loader::NonSharedGlobalSyncModuleLoaderScope> maybeSyncLoaderScope; 918 RefPtr<mozJSModuleLoader> moduleloader = 919 GetModuleLoaderForOptions(aCx, global, options, maybeSyncLoaderScope); 920 if (!moduleloader) { 921 return false; 922 } 923 924 JS::Rooted<JSObject*> moduleNamespace(aCx); 925 nsresult rv = moduleloader->ImportESModule(aCx, uri, &moduleNamespace); 926 if (NS_FAILED(rv)) { 927 Throw(aCx, rv); 928 return false; 929 } 930 931 // ESM's namespace is from the module's realm. 932 { 933 JSAutoRealm ar(aCx, moduleNamespace); 934 if (!JS_GetPropertyById(aCx, moduleNamespace, id, &value)) { 935 return false; 936 } 937 } 938 if (!JS_WrapValue(aCx, &value)) { 939 return false; 940 } 941 942 if (maybeSyncLoaderScope) { 943 maybeSyncLoaderScope->Finish(); 944 } 945 946 if (!JS_DefinePropertyById(aCx, thisObj, id, value, JSPROP_ENUMERATE)) { 947 return false; 948 } 949 950 args.rval().set(value); 951 return true; 952 } 953 954 static bool ESModuleSetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { 955 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); 956 957 JS::Rooted<JSObject*> callee(aCx); 958 JS::Rooted<JSObject*> thisObj(aCx); 959 JS::Rooted<jsid> id(aCx); 960 if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) { 961 return false; 962 } 963 964 return JS_DefinePropertyById(aCx, thisObj, id, args.get(0), JSPROP_ENUMERATE); 965 } 966 967 static bool DefineESModuleGetter(JSContext* aCx, JS::Handle<JSObject*> aTarget, 968 JS::Handle<JS::PropertyKey> aId, 969 JS::Handle<JS::Value> aResourceURI, 970 const EncodedOptions& encodedOptions) { 971 JS::Rooted<JS::Value> idVal(aCx, JS::StringValue(aId.toString())); 972 973 JS::Rooted<JS::Value> optionsVal(aCx, 974 JS::Int32Value(encodedOptions.toInt32())); 975 976 JS::Rooted<JSObject*> getter( 977 aCx, JS_GetFunctionObject(js::NewFunctionByIdWithReserved( 978 aCx, ESModuleGetter, 0, 0, aId))); 979 980 JS::Rooted<JSObject*> setter( 981 aCx, JS_GetFunctionObject(js::NewFunctionByIdWithReserved( 982 aCx, ESModuleSetter, 0, 0, aId))); 983 984 if (!getter || !setter) { 985 JS_ReportOutOfMemory(aCx); 986 return false; 987 } 988 989 js::SetFunctionNativeReserved(getter, SLOT_ID, idVal); 990 js::SetFunctionNativeReserved(setter, SLOT_ID, idVal); 991 992 js::SetFunctionNativeReserved(getter, SLOT_URI, aResourceURI); 993 994 js::SetFunctionNativeReserved(getter, SLOT_OPTIONS, optionsVal); 995 996 return JS_DefinePropertyById(aCx, aTarget, aId, getter, setter, 997 JSPROP_ENUMERATE); 998 } 999 1000 } // namespace lazy_getter 1001 1002 /* static */ 1003 void ChromeUtils::DefineLazyGetter(const GlobalObject& aGlobal, 1004 JS::Handle<JSObject*> aTarget, 1005 JS::Handle<JS::Value> aName, 1006 JS::Handle<JSObject*> aLambda, 1007 ErrorResult& aRv) { 1008 JSContext* cx = aGlobal.Context(); 1009 if (!lazy_getter::DefineLazyGetter(cx, aTarget, aName, aLambda)) { 1010 aRv.NoteJSContextException(cx); 1011 return; 1012 } 1013 } 1014 1015 /* static */ 1016 void ChromeUtils::DefineESModuleGetters( 1017 const GlobalObject& global, JS::Handle<JSObject*> target, 1018 JS::Handle<JSObject*> modules, 1019 const ImportESModuleOptionsDictionary& aOptions, ErrorResult& aRv) { 1020 JSContext* cx = global.Context(); 1021 1022 JS::Rooted<JS::IdVector> props(cx, JS::IdVector(cx)); 1023 if (!JS_Enumerate(cx, modules, &props)) { 1024 aRv.NoteJSContextException(cx); 1025 return; 1026 } 1027 1028 if (!ValidateImportOptions(cx, global, aOptions)) { 1029 aRv.Throw(NS_ERROR_FAILURE); 1030 return; 1031 } 1032 1033 EncodedOptions encodedOptions(aOptions); 1034 1035 JS::Rooted<JS::PropertyKey> prop(cx); 1036 JS::Rooted<JS::Value> resourceURIVal(cx); 1037 for (JS::PropertyKey tmp : props) { 1038 prop = tmp; 1039 1040 if (!prop.isString()) { 1041 aRv.Throw(NS_ERROR_FAILURE); 1042 return; 1043 } 1044 1045 if (!JS_GetPropertyById(cx, modules, prop, &resourceURIVal)) { 1046 aRv.NoteJSContextException(cx); 1047 return; 1048 } 1049 1050 if (!lazy_getter::DefineESModuleGetter(cx, target, prop, resourceURIVal, 1051 encodedOptions)) { 1052 aRv.NoteJSContextException(cx); 1053 return; 1054 } 1055 } 1056 } 1057 1058 #ifdef XP_UNIX 1059 /* static */ 1060 void ChromeUtils::GetLibcConstants(const GlobalObject&, 1061 LibcConstants& aConsts) { 1062 aConsts.mEPERM.Construct(EPERM); 1063 aConsts.mEINTR.Construct(EINTR); 1064 aConsts.mEACCES.Construct(EACCES); 1065 aConsts.mEAGAIN.Construct(EAGAIN); 1066 aConsts.mEINVAL.Construct(EINVAL); 1067 aConsts.mENOSYS.Construct(ENOSYS); 1068 1069 aConsts.mF_SETFD.Construct(F_SETFD); 1070 aConsts.mF_SETFL.Construct(F_SETFL); 1071 1072 aConsts.mFD_CLOEXEC.Construct(FD_CLOEXEC); 1073 1074 aConsts.mAT_EACCESS.Construct(AT_EACCESS); 1075 1076 aConsts.mO_CREAT.Construct(O_CREAT); 1077 aConsts.mO_NONBLOCK.Construct(O_NONBLOCK); 1078 aConsts.mO_WRONLY.Construct(O_WRONLY); 1079 1080 aConsts.mPOLLERR.Construct(POLLERR); 1081 aConsts.mPOLLHUP.Construct(POLLHUP); 1082 aConsts.mPOLLIN.Construct(POLLIN); 1083 aConsts.mPOLLNVAL.Construct(POLLNVAL); 1084 aConsts.mPOLLOUT.Construct(POLLOUT); 1085 1086 aConsts.mWNOHANG.Construct(WNOHANG); 1087 1088 # ifdef XP_LINUX 1089 aConsts.mPR_CAPBSET_READ.Construct(PR_CAPBSET_READ); 1090 # endif 1091 } 1092 #endif 1093 1094 /* static */ 1095 void ChromeUtils::OriginAttributesToSuffix( 1096 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs, 1097 nsCString& aSuffix) 1098 1099 { 1100 OriginAttributes attrs(aAttrs); 1101 attrs.CreateSuffix(aSuffix); 1102 } 1103 1104 /* static */ 1105 bool ChromeUtils::OriginAttributesMatchPattern( 1106 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs, 1107 const dom::OriginAttributesPatternDictionary& aPattern) { 1108 OriginAttributes attrs(aAttrs); 1109 OriginAttributesPattern pattern(aPattern); 1110 return pattern.Matches(attrs); 1111 } 1112 1113 /* static */ 1114 void ChromeUtils::CreateOriginAttributesFromOrigin( 1115 dom::GlobalObject& aGlobal, const nsAString& aOrigin, 1116 dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv) { 1117 OriginAttributes attrs; 1118 nsAutoCString suffix; 1119 if (!attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(aOrigin), suffix)) { 1120 aRv.Throw(NS_ERROR_FAILURE); 1121 return; 1122 } 1123 aAttrs = attrs; 1124 } 1125 1126 /* static */ 1127 void ChromeUtils::CreateOriginAttributesFromOriginSuffix( 1128 dom::GlobalObject& aGlobal, const nsAString& aSuffix, 1129 dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv) { 1130 OriginAttributes attrs; 1131 nsAutoCString suffix; 1132 if (!attrs.PopulateFromSuffix(NS_ConvertUTF16toUTF8(aSuffix))) { 1133 aRv.Throw(NS_ERROR_FAILURE); 1134 return; 1135 } 1136 aAttrs = attrs; 1137 } 1138 1139 /* static */ 1140 void ChromeUtils::FillNonDefaultOriginAttributes( 1141 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs, 1142 dom::OriginAttributesDictionary& aNewAttrs) { 1143 aNewAttrs = aAttrs; 1144 } 1145 1146 /* static */ 1147 bool ChromeUtils::IsOriginAttributesEqual( 1148 dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aA, 1149 const dom::OriginAttributesDictionary& aB) { 1150 return IsOriginAttributesEqual(aA, aB); 1151 } 1152 1153 /* static */ 1154 bool ChromeUtils::IsOriginAttributesEqual( 1155 const dom::OriginAttributesDictionary& aA, 1156 const dom::OriginAttributesDictionary& aB) { 1157 return aA == aB; 1158 } 1159 1160 /* static */ 1161 void ChromeUtils::GetBaseDomainFromPartitionKey(dom::GlobalObject& aGlobal, 1162 const nsAString& aPartitionKey, 1163 nsAString& aBaseDomain, 1164 ErrorResult& aRv) { 1165 nsString scheme; 1166 nsString pkBaseDomain; 1167 int32_t port; 1168 bool ancestor; 1169 1170 if (!mozilla::OriginAttributes::ParsePartitionKey( 1171 aPartitionKey, scheme, pkBaseDomain, port, ancestor)) { 1172 aRv.Throw(NS_ERROR_FAILURE); 1173 return; 1174 } 1175 1176 aBaseDomain = pkBaseDomain; 1177 } 1178 1179 /* static */ 1180 void ChromeUtils::GetPartitionKeyFromURL(dom::GlobalObject& aGlobal, 1181 const nsAString& aTopLevelUrl, 1182 const nsAString& aSubresourceUrl, 1183 const Optional<bool>& aForeignContext, 1184 nsAString& aPartitionKey, 1185 ErrorResult& aRv) { 1186 nsCOMPtr<nsIURI> topLevelURI; 1187 nsresult rv = NS_NewURI(getter_AddRefs(topLevelURI), aTopLevelUrl); 1188 if (NS_SUCCEEDED(rv) && topLevelURI->SchemeIs("chrome")) { 1189 rv = NS_ERROR_FAILURE; 1190 } 1191 if (NS_WARN_IF(NS_FAILED(rv))) { 1192 aPartitionKey.Truncate(); 1193 aRv.Throw(rv); 1194 return; 1195 } 1196 1197 bool foreignResource; 1198 bool fallback = false; 1199 if (!aSubresourceUrl.IsEmpty()) { 1200 nsCOMPtr<nsIURI> resourceURI; 1201 rv = NS_NewURI(getter_AddRefs(resourceURI), aSubresourceUrl); 1202 if (NS_WARN_IF(NS_FAILED(rv))) { 1203 aPartitionKey.Truncate(); 1204 aRv.Throw(rv); 1205 return; 1206 } 1207 1208 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance(); 1209 if (!thirdPartyUtil) { 1210 aPartitionKey.Truncate(); 1211 aRv.Throw(NS_ERROR_SERVICE_NOT_AVAILABLE); 1212 return; 1213 } 1214 1215 rv = thirdPartyUtil->IsThirdPartyURI(topLevelURI, resourceURI, 1216 &foreignResource); 1217 if (NS_FAILED(rv)) { 1218 // we fallback to assuming the resource is foreign if there is an error 1219 foreignResource = true; 1220 fallback = true; 1221 } 1222 } else { 1223 // Assume we have a foreign resource if the resource was not provided 1224 foreignResource = true; 1225 fallback = true; 1226 } 1227 1228 // aForeignContext is whether or not this is a foreign context. 1229 // foreignResource is whether or not the resource is cross-site to the top 1230 // level. So we need to validate that a false foreign context doesn't have a 1231 // same-site resource. That is impossible! 1232 if (aForeignContext.WasPassed() && !aForeignContext.Value() && 1233 foreignResource && !fallback) { 1234 aPartitionKey.Truncate(); 1235 aRv.Throw(nsresult::NS_ERROR_INVALID_ARG); 1236 return; 1237 } 1238 1239 bool foreignByAncestorContext = aForeignContext.WasPassed() && 1240 aForeignContext.Value() && !foreignResource; 1241 mozilla::OriginAttributes attrs; 1242 attrs.SetPartitionKey(topLevelURI, foreignByAncestorContext); 1243 aPartitionKey = attrs.mPartitionKey; 1244 } 1245 1246 #ifdef NIGHTLY_BUILD 1247 /* static */ 1248 void ChromeUtils::GetRecentJSDevError(GlobalObject& aGlobal, 1249 JS::MutableHandle<JS::Value> aRetval, 1250 ErrorResult& aRv) { 1251 aRetval.setUndefined(); 1252 auto runtime = CycleCollectedJSRuntime::Get(); 1253 MOZ_ASSERT(runtime); 1254 1255 auto cx = aGlobal.Context(); 1256 if (!runtime->GetRecentDevError(cx, aRetval)) { 1257 aRv.NoteJSContextException(cx); 1258 return; 1259 } 1260 } 1261 1262 /* static */ 1263 void ChromeUtils::ClearRecentJSDevError(GlobalObject&) { 1264 auto runtime = CycleCollectedJSRuntime::Get(); 1265 MOZ_ASSERT(runtime); 1266 1267 runtime->ClearRecentDevError(); 1268 } 1269 #endif // NIGHTLY_BUILD 1270 1271 void ChromeUtils::ClearMessagingLayerSecurityStateByPrincipal( 1272 GlobalObject&, nsIPrincipal* aPrincipal, ErrorResult& aRv) { 1273 MOZ_LOG(gMlsLog, LogLevel::Debug, 1274 ("ClearMessagingLayerSecurityStateByPrincipal")); 1275 1276 if (NS_WARN_IF(!aPrincipal)) { 1277 MOZ_LOG(gMlsLog, LogLevel::Error, ("Principal is null")); 1278 aRv.Throw(NS_ERROR_FAILURE); 1279 return; 1280 } 1281 1282 // Get the profile directory 1283 nsCOMPtr<nsIFile> file; 1284 aRv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(file)); 1285 if (NS_WARN_IF(aRv.Failed())) { 1286 MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get profile directory")); 1287 aRv.Throw(NS_ERROR_FAILURE); 1288 return; 1289 } 1290 1291 // Append the 'mls' directory 1292 aRv = file->AppendNative("mls"_ns); 1293 if (NS_WARN_IF(aRv.Failed())) { 1294 MOZ_LOG(gMlsLog, LogLevel::Error, 1295 ("Failed to append 'mls' to directory path")); 1296 aRv.Throw(NS_ERROR_FAILURE); 1297 return; 1298 } 1299 1300 bool exists; 1301 aRv = file->Exists(&exists); 1302 if (NS_WARN_IF(aRv.Failed())) { 1303 MOZ_LOG(gMlsLog, LogLevel::Error, 1304 ("Failed to check if 'mls' directory exists")); 1305 aRv.Throw(NS_ERROR_FAILURE); 1306 return; 1307 } 1308 1309 // If the 'mls' directory does not exist, we exit early 1310 if (!exists) { 1311 MOZ_LOG(gMlsLog, LogLevel::Error, ("'mls' directory does not exist")); 1312 return; 1313 } 1314 1315 // Get the storage origin key 1316 nsAutoCString originKey; 1317 aRv = aPrincipal->GetStorageOriginKey(originKey); 1318 if (NS_WARN_IF(aRv.Failed())) { 1319 MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get storage origin key")); 1320 aRv.Throw(NS_ERROR_FAILURE); 1321 return; 1322 } 1323 1324 // Get the origin attributes suffix 1325 nsAutoCString originAttrSuffix; 1326 aRv = aPrincipal->GetOriginSuffix(originAttrSuffix); 1327 if (NS_WARN_IF(aRv.Failed())) { 1328 MOZ_LOG(gMlsLog, LogLevel::Error, 1329 ("Failed to get origin attributes suffix")); 1330 aRv.Throw(NS_ERROR_FAILURE); 1331 return; 1332 } 1333 1334 // Construct the full origin key 1335 nsAutoCString fullOriginKey = originKey + originAttrSuffix; 1336 1337 // We append the full origin key to the file path 1338 aRv = file->AppendNative(fullOriginKey); 1339 if (NS_WARN_IF(aRv.Failed())) { 1340 MOZ_LOG(gMlsLog, LogLevel::Error, 1341 ("Failed to append full origin key to the file path")); 1342 aRv.Throw(NS_ERROR_FAILURE); 1343 return; 1344 } 1345 1346 // Remove the directory recursively 1347 aRv = file->Remove(/* recursive */ true); 1348 if (NS_WARN_IF(aRv.Failed())) { 1349 MOZ_LOG(gMlsLog, LogLevel::Error, 1350 ("Failed to remove : %s", file->HumanReadablePath().get())); 1351 aRv.Throw(NS_ERROR_FAILURE); 1352 return; 1353 } 1354 1355 MOZ_LOG(gMlsLog, LogLevel::Debug, 1356 ("Successfully cleared MLS state for principal")); 1357 } 1358 1359 void ChromeUtils::ClearMessagingLayerSecurityStateBySite( 1360 GlobalObject&, const nsACString& aSchemelessSite, 1361 const dom::OriginAttributesPatternDictionary& aPattern, ErrorResult& aRv) { 1362 MOZ_LOG(gMlsLog, LogLevel::Debug, ("ClearMessagingLayerSecurityStateBySite")); 1363 1364 // Get the profile directory 1365 nsCOMPtr<nsIFile> file; 1366 aRv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(file)); 1367 if (NS_WARN_IF(aRv.Failed())) { 1368 MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get profile directory")); 1369 aRv.Throw(NS_ERROR_FAILURE); 1370 return; 1371 } 1372 1373 // Append the 'mls' directory 1374 aRv = file->AppendNative("mls"_ns); 1375 if (NS_WARN_IF(aRv.Failed())) { 1376 MOZ_LOG(gMlsLog, LogLevel::Error, 1377 ("Failed to append 'mls' to directory path")); 1378 aRv.Throw(NS_ERROR_FAILURE); 1379 return; 1380 } 1381 1382 bool exists; 1383 aRv = file->Exists(&exists); 1384 if (NS_WARN_IF(aRv.Failed())) { 1385 MOZ_LOG(gMlsLog, LogLevel::Error, 1386 ("Failed to check if 'mls' directory exists")); 1387 aRv.Throw(NS_ERROR_FAILURE); 1388 return; 1389 } 1390 1391 // If the 'mls' directory does not exist, we exit early 1392 if (!exists) { 1393 MOZ_LOG(gMlsLog, LogLevel::Error, ("'mls' directory does not exist")); 1394 return; 1395 } 1396 1397 // Check if the schemeless site is empty 1398 if (NS_WARN_IF(aSchemelessSite.IsEmpty())) { 1399 MOZ_LOG(gMlsLog, LogLevel::Error, ("Schemeless site is empty")); 1400 aRv.Throw(NS_ERROR_INVALID_ARG); 1401 return; 1402 } 1403 1404 // Site pattern 1405 OriginAttributesPattern pattern(aPattern); 1406 1407 // Partition pattern 1408 // This pattern is used to (additionally) clear state partitioned under 1409 // aSchemelessSite. 1410 OriginAttributesPattern partitionPattern = pattern; 1411 partitionPattern.mPartitionKeyPattern.Construct(); 1412 partitionPattern.mPartitionKeyPattern.Value().mBaseDomain.Construct( 1413 NS_ConvertUTF8toUTF16(aSchemelessSite)); 1414 1415 // Reverse the base domain using the existing function 1416 nsAutoCString targetReversedBaseDomain(aSchemelessSite); 1417 std::reverse(targetReversedBaseDomain.BeginWriting(), 1418 targetReversedBaseDomain.EndWriting()); 1419 1420 MOZ_LOG(gMlsLog, LogLevel::Debug, 1421 ("Reversed base domain: %s", targetReversedBaseDomain.get())); 1422 1423 // Enumerate files in the 'mls' directory 1424 nsCOMPtr<nsIDirectoryEnumerator> dirEnum; 1425 aRv = file->GetDirectoryEntries(getter_AddRefs(dirEnum)); 1426 if (NS_WARN_IF(aRv.Failed())) { 1427 MOZ_LOG(gMlsLog, LogLevel::Error, 1428 ("Failed to get directory entries in 'mls' directory")); 1429 aRv.Throw(NS_ERROR_FAILURE); 1430 return; 1431 } 1432 1433 // Iterate through all entries in the directory 1434 nsCOMPtr<nsIFile> entry; 1435 while (NS_SUCCEEDED(dirEnum->GetNextFile(getter_AddRefs(entry))) && entry) { 1436 nsAutoCString entryName; 1437 aRv = entry->GetNativeLeafName(entryName); 1438 if (NS_WARN_IF(aRv.Failed())) { 1439 MOZ_LOG(gMlsLog, LogLevel::Error, 1440 ("Failed to get native leaf name for entry")); 1441 continue; 1442 } 1443 1444 // Find the position of .sqlite.enc or .key in the entry name 1445 int32_t sqliteEncPos = entryName.RFind(".sqlite.enc"); 1446 int32_t keyPos = entryName.RFind(".key"); 1447 1448 // Remove the .sqlite.enc or .key suffix from the entryName 1449 if (sqliteEncPos != kNotFound) { 1450 entryName.SetLength(sqliteEncPos); 1451 } else if (keyPos != kNotFound) { 1452 entryName.SetLength(keyPos); 1453 } 1454 1455 // Decode the entry name 1456 nsAutoCString decodedEntryName; 1457 aRv = mozilla::Base64Decode(entryName, decodedEntryName); 1458 if (NS_WARN_IF(aRv.Failed())) { 1459 MOZ_LOG(gMlsLog, LogLevel::Debug, 1460 ("Failed to decode entry name: %s", entryName.get())); 1461 continue; 1462 } 1463 1464 // Find the origin attributes suffix in the entry name by taking the 1465 // value of the entry name after the ^ separator 1466 int32_t separatorPos = decodedEntryName.FindChar('^'); 1467 1468 // We extract the origin attributes suffix from the entry name 1469 nsAutoCString originSuffix; 1470 originSuffix.Assign(Substring(decodedEntryName, separatorPos)); 1471 1472 // Populate the origin attributes from the suffix 1473 OriginAttributes originAttrs; 1474 if (NS_WARN_IF(!originAttrs.PopulateFromSuffix(originSuffix))) { 1475 MOZ_LOG(gMlsLog, LogLevel::Error, 1476 ("Failed to populate origin attributes from suffix")); 1477 continue; 1478 } 1479 1480 // Check if the entry name starts with the reversed base domain 1481 if (StringBeginsWith(decodedEntryName, targetReversedBaseDomain)) { 1482 MOZ_LOG(gMlsLog, LogLevel::Debug, 1483 ("Entry file: %s", entry->HumanReadablePath().get())); 1484 1485 // If there is a valid origin attributes suffix, we remove the entry 1486 // only if it matches. 1487 if (pattern.Matches(originAttrs)) { 1488 aRv = entry->Remove(/* recursive */ false); 1489 if (NS_WARN_IF(aRv.Failed())) { 1490 MOZ_LOG(gMlsLog, LogLevel::Error, 1491 ("Failed to remove file: %s", decodedEntryName.get())); 1492 } 1493 MOZ_LOG(gMlsLog, LogLevel::Debug, 1494 ("Removed file: %s", decodedEntryName.get())); 1495 } 1496 } 1497 1498 // If there is a valid origin attributes suffix, we remove the entry 1499 // only if it matches. We are checking for state partitioned under 1500 // aSchemelessSite. 1501 if (partitionPattern.Matches(originAttrs)) { 1502 aRv = entry->Remove(/* recursive */ false); 1503 if (NS_WARN_IF(aRv.Failed())) { 1504 MOZ_LOG(gMlsLog, LogLevel::Error, 1505 ("Failed to remove file: %s", decodedEntryName.get())); 1506 } 1507 MOZ_LOG(gMlsLog, LogLevel::Debug, 1508 ("Removed file: %s", decodedEntryName.get())); 1509 } 1510 } 1511 1512 // Close the directory enumerator 1513 dirEnum->Close(); 1514 } 1515 1516 void ChromeUtils::ClearMessagingLayerSecurityState(GlobalObject&, 1517 ErrorResult& aRv) { 1518 MOZ_LOG(gMlsLog, LogLevel::Debug, ("ClearMessagingLayerSecurityState")); 1519 1520 // Get the profile directory 1521 nsCOMPtr<nsIFile> file; 1522 aRv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(file)); 1523 if (NS_WARN_IF(aRv.Failed())) { 1524 MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get profile directory")); 1525 return; 1526 } 1527 1528 // Append the 'mls' directory 1529 aRv = file->AppendNative("mls"_ns); 1530 if (NS_WARN_IF(aRv.Failed())) { 1531 MOZ_LOG(gMlsLog, LogLevel::Error, 1532 ("Failed to append 'mls' to directory path")); 1533 return; 1534 } 1535 1536 // Check if the directory exists 1537 bool exists; 1538 aRv = file->Exists(&exists); 1539 if (NS_WARN_IF(aRv.Failed() || !exists)) { 1540 MOZ_LOG(gMlsLog, LogLevel::Debug, ("'mls' directory does not exist")); 1541 return; 1542 } 1543 1544 // Remove the MLS directory recursively 1545 aRv = file->Remove(/* recursive */ true); 1546 if (NS_WARN_IF(aRv.Failed())) { 1547 MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to remove MLS directory")); 1548 return; 1549 } 1550 1551 // Log the directory path 1552 MOZ_LOG(gMlsLog, LogLevel::Debug, 1553 ("Deleted MLS directory: %s", file->HumanReadablePath().get())); 1554 1555 // Recreate the MLS directory 1556 aRv = file->Create(nsIFile::DIRECTORY_TYPE, 0755); 1557 if (NS_WARN_IF(aRv.Failed())) { 1558 MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to recreate MLS directory")); 1559 return; 1560 } 1561 1562 MOZ_LOG(gMlsLog, LogLevel::Debug, ("Successfully cleared all MLS state")); 1563 } 1564 1565 void ChromeUtils::ClearResourceCache( 1566 GlobalObject& aGlobal, const dom::ClearResourceCacheOptions& aOptions, 1567 ErrorResult& aRv) { 1568 bool clearStyleSheet = false; 1569 bool clearScript = false; 1570 bool clearImage = false; 1571 1572 if (aOptions.mTypes.WasPassed()) { 1573 for (const auto& type : aOptions.mTypes.Value()) { 1574 switch (type) { 1575 case ResourceCacheType::Stylesheet: 1576 clearStyleSheet = true; 1577 break; 1578 case ResourceCacheType::Script: 1579 clearScript = true; 1580 break; 1581 case ResourceCacheType::Image: 1582 clearImage = true; 1583 break; 1584 } 1585 } 1586 } else { 1587 clearStyleSheet = true; 1588 clearScript = true; 1589 clearImage = true; 1590 } 1591 1592 int filterCount = 0; 1593 if (aOptions.mTarget.WasPassed()) { 1594 filterCount++; 1595 } 1596 if (aOptions.mPrincipal.WasPassed()) { 1597 filterCount++; 1598 } 1599 if (aOptions.mSchemelessSite.WasPassed()) { 1600 filterCount++; 1601 } 1602 if (aOptions.mUrl.WasPassed()) { 1603 filterCount++; 1604 } 1605 if (filterCount > 1) { 1606 aRv.ThrowInvalidStateError( 1607 "target, principal, schemelessSite, and url properties are mutually " 1608 "exclusive"); 1609 return; 1610 } 1611 1612 if (aOptions.mTarget.WasPassed()) { 1613 Maybe<bool> chrome; 1614 switch (aOptions.mTarget.Value()) { 1615 case ResourceCacheTarget::Chrome: 1616 chrome.emplace(true); 1617 break; 1618 case ResourceCacheTarget::Content: 1619 chrome.emplace(false); 1620 break; 1621 } 1622 1623 if (clearStyleSheet) { 1624 SharedStyleSheetCache::Clear(chrome); 1625 } 1626 if (clearScript) { 1627 SharedScriptCache::Clear(chrome); 1628 } 1629 if (clearImage) { 1630 imgLoader::ClearCache(Nothing(), chrome); 1631 } 1632 return; 1633 } 1634 1635 if (aOptions.mPrincipal.WasPassed()) { 1636 nsCOMPtr<nsIPrincipal> principal = aOptions.mPrincipal.Value().get(); 1637 1638 if (clearStyleSheet) { 1639 SharedStyleSheetCache::Clear(Nothing(), Some(principal)); 1640 } 1641 if (clearScript) { 1642 SharedScriptCache::Clear(Nothing(), Some(principal)); 1643 } 1644 if (clearImage) { 1645 imgLoader::ClearCache(Nothing(), Nothing(), Some(principal)); 1646 } 1647 return; 1648 } 1649 1650 if (aOptions.mSchemelessSite.WasPassed()) { 1651 nsCString schemelessSite(aOptions.mSchemelessSite.Value()); 1652 mozilla::OriginAttributesPattern pattern(aOptions.mPattern); 1653 1654 if (clearStyleSheet) { 1655 SharedStyleSheetCache::Clear(Nothing(), Nothing(), Some(schemelessSite), 1656 Some(pattern)); 1657 } 1658 if (clearScript) { 1659 SharedScriptCache::Clear(Nothing(), Nothing(), Some(schemelessSite), 1660 Some(pattern)); 1661 } 1662 if (clearImage) { 1663 imgLoader::ClearCache(Nothing(), Nothing(), Nothing(), 1664 Some(schemelessSite), Some(pattern)); 1665 } 1666 return; 1667 } 1668 1669 if (aOptions.mUrl.WasPassed()) { 1670 nsCString url(aOptions.mUrl.Value()); 1671 1672 if (clearStyleSheet) { 1673 SharedStyleSheetCache::Clear(Nothing(), Nothing(), Nothing(), Nothing(), 1674 Some(url)); 1675 } 1676 if (clearScript) { 1677 SharedScriptCache::Clear(Nothing(), Nothing(), Nothing(), Nothing(), 1678 Some(url)); 1679 } 1680 if (clearImage) { 1681 imgLoader::ClearCache(Nothing(), Nothing(), Nothing(), Nothing(), 1682 Nothing(), Some(url)); 1683 } 1684 return; 1685 } 1686 1687 if (clearStyleSheet) { 1688 SharedStyleSheetCache::Clear(); 1689 } 1690 if (clearScript) { 1691 SharedScriptCache::Clear(); 1692 } 1693 if (clearImage) { 1694 imgLoader::ClearCache(); 1695 } 1696 } 1697 1698 void ChromeUtils::InvalidateResourceCache(GlobalObject& aGlobal, 1699 ErrorResult& aRv) { 1700 SharedScriptCache::Invalidate(); 1701 } 1702 1703 void ChromeUtils::ClearBfcacheByPrincipal(GlobalObject& aGlobal, 1704 nsIPrincipal* aPrincipal, 1705 ErrorResult& aRv) { 1706 aRv = CanonicalBrowsingContext::ClearBfcacheByPrincipal(aPrincipal); 1707 } 1708 1709 #define PROCTYPE_TO_WEBIDL_CASE(_procType, _webidl) \ 1710 case mozilla::ProcType::_procType: \ 1711 return WebIDLProcType::_webidl 1712 1713 static WebIDLProcType ProcTypeToWebIDL(mozilla::ProcType aType) { 1714 // Max is the value of the last enum, not the length, so add one. 1715 static_assert( 1716 static_cast<size_t>(MaxContiguousEnumValue<WebIDLProcType>::value) == 1717 static_cast<size_t>(ProcType::Max), 1718 "In order for this static cast to be okay, " 1719 "WebIDLProcType must match ProcType exactly"); 1720 1721 // These must match the similar ones in E10SUtils.sys.mjs, RemoteTypes.h, 1722 // ProcInfo.h and ChromeUtils.webidl 1723 switch (aType) { 1724 PROCTYPE_TO_WEBIDL_CASE(Web, Web); 1725 PROCTYPE_TO_WEBIDL_CASE(WebIsolated, WebIsolated); 1726 PROCTYPE_TO_WEBIDL_CASE(File, File); 1727 PROCTYPE_TO_WEBIDL_CASE(Extension, Extension); 1728 PROCTYPE_TO_WEBIDL_CASE(PrivilegedAbout, Privilegedabout); 1729 PROCTYPE_TO_WEBIDL_CASE(PrivilegedMozilla, Privilegedmozilla); 1730 PROCTYPE_TO_WEBIDL_CASE(WebCOOPCOEP, WithCoopCoep); 1731 PROCTYPE_TO_WEBIDL_CASE(WebServiceWorker, WebServiceWorker); 1732 PROCTYPE_TO_WEBIDL_CASE(Inference, Inference); 1733 1734 #define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \ 1735 process_bin_type, procinfo_typename, \ 1736 webidl_typename, allcaps_name) \ 1737 PROCTYPE_TO_WEBIDL_CASE(procinfo_typename, webidl_typename); 1738 #define SKIP_PROCESS_TYPE_CONTENT 1739 #ifndef MOZ_ENABLE_FORKSERVER 1740 # define SKIP_PROCESS_TYPE_FORKSERVER 1741 #endif // MOZ_ENABLE_FORKSERVER 1742 #include "mozilla/GeckoProcessTypes.h" 1743 #undef SKIP_PROCESS_TYPE_CONTENT 1744 #ifndef MOZ_ENABLE_FORKSERVER 1745 # undef SKIP_PROCESS_TYPE_FORKSERVER 1746 #endif // MOZ_ENABLE_FORKSERVER 1747 #undef GECKO_PROCESS_TYPE 1748 1749 PROCTYPE_TO_WEBIDL_CASE(Preallocated, Preallocated); 1750 PROCTYPE_TO_WEBIDL_CASE(Unknown, Unknown); 1751 } 1752 1753 MOZ_ASSERT(false, "Unhandled case in ProcTypeToWebIDL"); 1754 return WebIDLProcType::Unknown; 1755 } 1756 1757 #undef PROCTYPE_TO_WEBIDL_CASE 1758 1759 /* static */ 1760 already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, 1761 ErrorResult& aRv) { 1762 // This function will use IPDL to enable threads info on macOS 1763 // see https://bugzilla.mozilla.org/show_bug.cgi?id=1529023 1764 if (!XRE_IsParentProcess()) { 1765 aRv.Throw(NS_ERROR_FAILURE); 1766 return nullptr; 1767 } 1768 // Prepare the JS promise that will hold our response. 1769 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 1770 MOZ_ASSERT(global); 1771 RefPtr<Promise> domPromise = Promise::Create(global, aRv); 1772 if (NS_WARN_IF(aRv.Failed())) { 1773 return nullptr; 1774 } 1775 MOZ_ASSERT(domPromise); 1776 1777 // Get a list of processes to examine and pre-fill them with available info. 1778 // Note that this is subject to race conditions: just because we have a 1779 // process in the list doesn't mean that the process will still be alive when 1780 // we attempt to get its information. Followup code MUST be able to fail 1781 // gracefully on some processes and still return whichever information is 1782 // available. 1783 1784 // Get all the content parents. 1785 // Note that this array includes even the long dead content parents, so we 1786 // might have some garbage, especially with Fission. 1787 // SAFETY NOTE: `contentParents` is only valid if used synchronously. 1788 // Anything else and you may end up dealing with dangling pointers. 1789 nsTArray<ContentParent*> contentParents; 1790 ContentParent::GetAll(contentParents); 1791 1792 // Prepare our background request. 1793 // We reserve one more slot for the browser process itself. 1794 nsTArray<ProcInfoRequest> requests(contentParents.Length() + 1); 1795 // Requesting process info for the browser process itself. 1796 requests.EmplaceBack( 1797 /* aPid = */ base::GetCurrentProcId(), 1798 /* aProcessType = */ ProcType::Browser, 1799 /* aOrigin = */ ""_ns, 1800 /* aWindowInfo = */ nsTArray<WindowInfo>(), 1801 /* aUtilityInfo = */ nsTArray<UtilityInfo>()); 1802 1803 // First handle non-ContentParent processes. 1804 mozilla::ipc::GeckoChildProcessHost::GetAll( 1805 [&requests](mozilla::ipc::GeckoChildProcessHost* aGeckoProcess) { 1806 base::ProcessId childPid = aGeckoProcess->GetChildProcessId(); 1807 if (childPid == 0) { 1808 // Something went wrong with this process, it may be dead already, 1809 // fail gracefully. 1810 return; 1811 } 1812 mozilla::ProcType type = mozilla::ProcType::Unknown; 1813 1814 switch (aGeckoProcess->GetProcessType()) { 1815 case GeckoProcessType::GeckoProcessType_Content: { 1816 // These processes are handled separately. 1817 return; 1818 } 1819 1820 #define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \ 1821 process_bin_type, procinfo_typename, \ 1822 webidl_typename, allcaps_name) \ 1823 case GeckoProcessType::GeckoProcessType_##enum_name: { \ 1824 type = mozilla::ProcType::procinfo_typename; \ 1825 break; \ 1826 } 1827 #define SKIP_PROCESS_TYPE_CONTENT 1828 #ifndef MOZ_ENABLE_FORKSERVER 1829 # define SKIP_PROCESS_TYPE_FORKSERVER 1830 #endif // MOZ_ENABLE_FORKSERVER 1831 #include "mozilla/GeckoProcessTypes.h" 1832 #ifndef MOZ_ENABLE_FORKSERVER 1833 # undef SKIP_PROCESS_TYPE_FORKSERVER 1834 #endif // MOZ_ENABLE_FORKSERVER 1835 #undef SKIP_PROCESS_TYPE_CONTENT 1836 #undef GECKO_PROCESS_TYPE 1837 default: 1838 // Leave the default Unknown value in |type|. 1839 break; 1840 } 1841 1842 // Attach utility actor information to the process. 1843 nsTArray<UtilityInfo> utilityActors; 1844 if (aGeckoProcess->GetProcessType() == 1845 GeckoProcessType::GeckoProcessType_Utility) { 1846 RefPtr<mozilla::ipc::UtilityProcessManager> upm = 1847 mozilla::ipc::UtilityProcessManager::GetSingleton(); 1848 if (!utilityActors.AppendElements(upm->GetActors(aGeckoProcess), 1849 fallible)) { 1850 NS_WARNING("Error adding actors"); 1851 return; 1852 } 1853 } 1854 1855 requests.EmplaceBack( 1856 /* aPid = */ childPid, 1857 /* aProcessType = */ type, 1858 /* aOrigin = */ ""_ns, 1859 /* aWindowInfo = */ nsTArray<WindowInfo>(), // Without a 1860 // ContentProcess, no 1861 // DOM windows. 1862 /* aUtilityInfo = */ std::move(utilityActors), 1863 /* aChild = */ 0 // Without a ContentProcess, no ChildId. 1864 #ifdef XP_MACOSX 1865 , 1866 /* aChildTask = */ aGeckoProcess->GetChildTask() 1867 #endif // XP_MACOSX 1868 ); 1869 }); 1870 1871 // Now handle ContentParents. 1872 for (const auto* contentParent : contentParents) { 1873 if (!contentParent || !contentParent->Process()) { 1874 // Presumably, the process is dead or dying. 1875 continue; 1876 } 1877 base::ProcessId pid = contentParent->Process()->GetChildProcessId(); 1878 if (pid == 0) { 1879 // Presumably, the process is dead or dying. 1880 continue; 1881 } 1882 if (contentParent->Process()->GetProcessType() != 1883 GeckoProcessType::GeckoProcessType_Content) { 1884 // We're probably racing against a process changing type. 1885 // We'll get it in the next call, skip it for the moment. 1886 continue; 1887 } 1888 1889 // Since this code is executed synchronously on the main thread, 1890 // processes cannot die while we're in this loop. 1891 mozilla::ProcType type = mozilla::ProcType::Unknown; 1892 1893 // Convert the remoteType into a ProcType. 1894 // Ideally, the remoteType should be strongly typed 1895 // upstream, this would make the conversion less brittle. 1896 const nsAutoCString remoteType(contentParent->GetRemoteType()); 1897 if (StringBeginsWith(remoteType, FISSION_WEB_REMOTE_TYPE)) { 1898 // WARNING: Do not change the order, as 1899 // `DEFAULT_REMOTE_TYPE` is a prefix of 1900 // `FISSION_WEB_REMOTE_TYPE`. 1901 type = mozilla::ProcType::WebIsolated; 1902 } else if (StringBeginsWith(remoteType, SERVICEWORKER_REMOTE_TYPE)) { 1903 type = mozilla::ProcType::WebServiceWorker; 1904 } else if (StringBeginsWith(remoteType, 1905 WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) { 1906 type = mozilla::ProcType::WebCOOPCOEP; 1907 } else if (remoteType == FILE_REMOTE_TYPE) { 1908 type = mozilla::ProcType::File; 1909 } else if (remoteType == EXTENSION_REMOTE_TYPE) { 1910 type = mozilla::ProcType::Extension; 1911 } else if (remoteType == PRIVILEGEDABOUT_REMOTE_TYPE) { 1912 type = mozilla::ProcType::PrivilegedAbout; 1913 } else if (remoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE) { 1914 type = mozilla::ProcType::PrivilegedMozilla; 1915 } else if (remoteType == PREALLOC_REMOTE_TYPE) { 1916 type = mozilla::ProcType::Preallocated; 1917 } else if (remoteType == INFERENCE_REMOTE_TYPE) { 1918 type = mozilla::ProcType::Inference; 1919 } else if (StringBeginsWith(remoteType, DEFAULT_REMOTE_TYPE)) { 1920 type = mozilla::ProcType::Web; 1921 } else { 1922 MOZ_CRASH_UNSAFE_PRINTF("Unknown remoteType '%s'", remoteType.get()); 1923 } 1924 1925 // By convention, everything after '=' is the origin. 1926 nsAutoCString origin; 1927 nsACString::const_iterator cursor; 1928 nsACString::const_iterator end; 1929 remoteType.BeginReading(cursor); 1930 remoteType.EndReading(end); 1931 if (FindCharInReadable('=', cursor, end)) { 1932 origin = Substring(++cursor, end); 1933 } 1934 1935 // Attach DOM window information to the process. 1936 nsTArray<WindowInfo> windows; 1937 for (const auto& browserParentWrapperKey : 1938 contentParent->ManagedPBrowserParent()) { 1939 for (const auto& windowGlobalParentWrapperKey : 1940 browserParentWrapperKey->ManagedPWindowGlobalParent()) { 1941 // WindowGlobalParent is the only immediate subclass of 1942 // PWindowGlobalParent. 1943 auto* windowGlobalParent = 1944 static_cast<WindowGlobalParent*>(windowGlobalParentWrapperKey); 1945 1946 nsString documentTitle; 1947 windowGlobalParent->GetDocumentTitle(documentTitle); 1948 WindowInfo* window = windows.EmplaceBack( 1949 fallible, 1950 /* aOuterWindowId = */ windowGlobalParent->OuterWindowId(), 1951 /* aDocumentURI = */ windowGlobalParent->GetDocumentURI(), 1952 /* aDocumentTitle = */ std::move(documentTitle), 1953 /* aIsProcessRoot = */ windowGlobalParent->IsProcessRoot(), 1954 /* aIsInProcess = */ windowGlobalParent->IsInProcess()); 1955 if (!window) { 1956 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 1957 return nullptr; 1958 } 1959 } 1960 } 1961 requests.EmplaceBack( 1962 /* aPid = */ pid, 1963 /* aProcessType = */ type, 1964 /* aOrigin = */ origin, 1965 /* aWindowInfo = */ std::move(windows), 1966 /* aUtilityInfo = */ nsTArray<UtilityInfo>(), 1967 /* aChild = */ contentParent->ChildID() 1968 #ifdef XP_MACOSX 1969 , 1970 /* aChildTask = */ contentParent->Process()->GetChildTask() 1971 #endif // XP_MACOSX 1972 ); 1973 } 1974 1975 // Now place background request. 1976 RefPtr<nsISerialEventTarget> target = global->SerialEventTarget(); 1977 mozilla::GetProcInfo(std::move(requests)) 1978 ->Then( 1979 target, __func__, 1980 [target, 1981 domPromise](const HashMap<base::ProcessId, ProcInfo>& aSysProcInfo) { 1982 ParentProcInfoDictionary parentInfo; 1983 if (aSysProcInfo.count() == 0) { 1984 // For some reason, we couldn't get *any* info. 1985 // Maybe a sandboxing issue? 1986 domPromise->MaybeReject(NS_ERROR_UNEXPECTED); 1987 return; 1988 } 1989 nsTArray<ChildProcInfoDictionary> childrenInfo( 1990 aSysProcInfo.count() - 1); 1991 for (auto iter = aSysProcInfo.iter(); !iter.done(); iter.next()) { 1992 const auto& sysProcInfo = iter.get().value(); 1993 nsresult rv; 1994 if (sysProcInfo.type == ProcType::Browser) { 1995 rv = mozilla::CopySysProcInfoToDOM(sysProcInfo, &parentInfo); 1996 if (NS_FAILED(rv)) { 1997 // Failing to copy? That's probably not something from we can 1998 // (or should) try to recover gracefully. 1999 domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 2000 return; 2001 } 2002 MOZ_ASSERT(sysProcInfo.childId == 0); 2003 MOZ_ASSERT(sysProcInfo.origin.IsEmpty()); 2004 } else { 2005 mozilla::dom::ChildProcInfoDictionary* childInfo = 2006 childrenInfo.AppendElement(fallible); 2007 if (!childInfo) { 2008 domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 2009 return; 2010 } 2011 rv = mozilla::CopySysProcInfoToDOM(sysProcInfo, childInfo); 2012 if (NS_FAILED(rv)) { 2013 domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 2014 return; 2015 } 2016 // Copy Firefox info. 2017 childInfo->mChildID = sysProcInfo.childId; 2018 childInfo->mOrigin = sysProcInfo.origin; 2019 childInfo->mType = ProcTypeToWebIDL(sysProcInfo.type); 2020 2021 for (const auto& source : sysProcInfo.windows) { 2022 auto* dest = childInfo->mWindows.AppendElement(fallible); 2023 if (!dest) { 2024 domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 2025 return; 2026 } 2027 dest->mOuterWindowId = source.outerWindowId; 2028 dest->mDocumentURI = source.documentURI; 2029 dest->mDocumentTitle = source.documentTitle; 2030 dest->mIsProcessRoot = source.isProcessRoot; 2031 dest->mIsInProcess = source.isInProcess; 2032 } 2033 2034 if (sysProcInfo.type == ProcType::Utility) { 2035 for (const auto& source : sysProcInfo.utilityActors) { 2036 auto* dest = 2037 childInfo->mUtilityActors.AppendElement(fallible); 2038 if (!dest) { 2039 domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 2040 return; 2041 } 2042 2043 dest->mActorName = source.actorName; 2044 } 2045 } 2046 } 2047 } 2048 2049 // Attach the children to the parent. 2050 mozilla::dom::Sequence<mozilla::dom::ChildProcInfoDictionary> 2051 children(std::move(childrenInfo)); 2052 parentInfo.mChildren = std::move(children); 2053 domPromise->MaybeResolve(parentInfo); 2054 }, 2055 [domPromise](nsresult aRv) { domPromise->MaybeReject(aRv); }); 2056 MOZ_ASSERT(domPromise); 2057 2058 // sending back the promise instance 2059 return domPromise.forget(); 2060 } 2061 2062 /* static */ 2063 uint64_t ChromeUtils::GetCurrentProcessMemoryUsage(GlobalObject& aGlobal, 2064 ErrorResult& aRv) { 2065 uint64_t retVal = 0; 2066 nsresult rv = mozilla::GetCurrentProcessMemoryUsage(&retVal); 2067 if (NS_FAILED(rv)) { 2068 aRv.Throw(rv); 2069 } 2070 return retVal; 2071 } 2072 2073 /* static */ 2074 uint64_t ChromeUtils::GetCpuTimeSinceProcessStart(GlobalObject& aGlobal, 2075 ErrorResult& aRv) { 2076 uint64_t retVal = 0; 2077 nsresult rv = mozilla::GetCpuTimeSinceProcessStartInMs(&retVal); 2078 if (NS_FAILED(rv)) { 2079 aRv.Throw(rv); 2080 } 2081 return retVal; 2082 } 2083 2084 /* static */ 2085 bool ChromeUtils::VsyncEnabled(GlobalObject& aGlobal) { 2086 return mozilla::gfx::VsyncSource::GetFastestVsyncRate().isSome(); 2087 } 2088 2089 void ChromeUtils::EnableAllPerfStatsFeatures(GlobalObject& aGlobal) { 2090 PerfStats::MetricMask mask = 2091 std::numeric_limits<PerfStats::MetricMask>::max(); 2092 PerfStats::SetCollectionMask(mask); 2093 } 2094 2095 void ChromeUtils::SetPerfStatsFeatures(GlobalObject& aGlobal, 2096 const Sequence<nsString>& aMetrics) { 2097 // Convert string array to bitmask 2098 PerfStats::MetricMask mask = 0; 2099 for (const auto& metricName : aMetrics) { 2100 // Convert string to corresponding enum value and set bit 2101 NS_ConvertUTF16toUTF8 utf8MetricName(metricName); 2102 mask |= PerfStats::GetFeatureMask(utf8MetricName.get()); 2103 } 2104 2105 PerfStats::SetCollectionMask(mask); 2106 } 2107 2108 already_AddRefed<Promise> ChromeUtils::CollectPerfStats(GlobalObject& aGlobal, 2109 ErrorResult& aRv) { 2110 // Creating a JS promise 2111 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2112 MOZ_ASSERT(global); 2113 2114 RefPtr<Promise> promise = Promise::Create(global, aRv); 2115 if (aRv.Failed()) { 2116 return nullptr; 2117 } 2118 2119 RefPtr<PerfStats::PerfStatsPromise> extPromise = 2120 PerfStats::CollectPerfStatsJSON(); 2121 2122 extPromise->Then( 2123 GetCurrentSerialEventTarget(), __func__, 2124 [promise](const nsCString& aResult) { 2125 promise->MaybeResolve(NS_ConvertUTF8toUTF16(aResult)); 2126 }, 2127 [promise](bool aValue) { promise->MaybeReject(NS_ERROR_FAILURE); }); 2128 2129 return promise.forget(); 2130 } 2131 2132 constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude; 2133 2134 /* static */ 2135 void ChromeUtils::GetCallerLocation(const GlobalObject& aGlobal, 2136 nsIPrincipal* aPrincipal, 2137 JS::MutableHandle<JSObject*> aRetval) { 2138 JSContext* cx = aGlobal.Context(); 2139 2140 auto* principals = nsJSPrincipals::get(aPrincipal); 2141 2142 JS::StackCapture captureMode(JS::FirstSubsumedFrame(cx, principals)); 2143 2144 JS::Rooted<JSObject*> frame(cx); 2145 if (!JS::CaptureCurrentStack(cx, &frame, std::move(captureMode))) { 2146 JS_ClearPendingException(cx); 2147 aRetval.set(nullptr); 2148 return; 2149 } 2150 2151 // FirstSubsumedFrame gets us a stack which stops at the first principal which 2152 // is subsumed by the given principal. That means that we may have a lot of 2153 // privileged frames that we don't care about at the top of the stack, though. 2154 // We need to filter those out to get the frame we actually want. 2155 aRetval.set( 2156 js::GetFirstSubsumedSavedFrame(cx, principals, frame, kSkipSelfHosted)); 2157 } 2158 2159 /* static */ 2160 void ChromeUtils::CreateError(const GlobalObject& aGlobal, 2161 const nsAString& aMessage, 2162 JS::Handle<JSObject*> aStack, 2163 JS::MutableHandle<JSObject*> aRetVal, 2164 ErrorResult& aRv) { 2165 if (aStack && !JS::IsMaybeWrappedSavedFrame(aStack)) { 2166 aRv.Throw(NS_ERROR_INVALID_ARG); 2167 return; 2168 } 2169 2170 JSContext* cx = aGlobal.Context(); 2171 2172 auto cleanup = MakeScopeExit([&]() { aRv.NoteJSContextException(cx); }); 2173 2174 JS::Rooted<JSObject*> retVal(cx); 2175 { 2176 JS::Rooted<JSString*> fileName(cx, JS_GetEmptyString(cx)); 2177 uint32_t line = 0; 2178 JS::TaggedColumnNumberOneOrigin column; 2179 2180 Maybe<JSAutoRealm> ar; 2181 JS::Rooted<JSObject*> stack(cx); 2182 if (aStack) { 2183 stack = UncheckedUnwrap(aStack); 2184 ar.emplace(cx, stack); 2185 2186 JSPrincipals* principals = 2187 JS::GetRealmPrincipals(js::GetContextRealm(cx)); 2188 if (JS::GetSavedFrameLine(cx, principals, stack, &line) != 2189 JS::SavedFrameResult::Ok || 2190 JS::GetSavedFrameColumn(cx, principals, stack, &column) != 2191 JS::SavedFrameResult::Ok || 2192 JS::GetSavedFrameSource(cx, principals, stack, &fileName) != 2193 JS::SavedFrameResult::Ok) { 2194 return; 2195 } 2196 } 2197 2198 JS::Rooted<JSString*> message(cx); 2199 { 2200 JS::Rooted<JS::Value> msgVal(cx); 2201 if (!xpc::NonVoidStringToJsval(cx, aMessage, &msgVal)) { 2202 return; 2203 } 2204 message = msgVal.toString(); 2205 } 2206 2207 JS::Rooted<JS::Value> err(cx); 2208 if (!JS::CreateError(cx, JSEXN_ERR, stack, fileName, line, 2209 JS::ColumnNumberOneOrigin(column.oneOriginValue()), 2210 nullptr, message, JS::NothingHandleValue, &err)) { 2211 return; 2212 } 2213 2214 MOZ_ASSERT(err.isObject()); 2215 retVal = &err.toObject(); 2216 } 2217 2218 if (aStack && !JS_WrapObject(cx, &retVal)) { 2219 return; 2220 } 2221 2222 cleanup.release(); 2223 aRetVal.set(retVal); 2224 } 2225 2226 /* static */ 2227 bool ChromeUtils::HasReportingHeaderForOrigin(GlobalObject& global, 2228 const nsAString& aOrigin, 2229 ErrorResult& aRv) { 2230 if (!XRE_IsParentProcess()) { 2231 aRv.Throw(NS_ERROR_FAILURE); 2232 return false; 2233 } 2234 2235 return ReportingHeader::HasReportingHeaderForOrigin( 2236 NS_ConvertUTF16toUTF8(aOrigin)); 2237 } 2238 2239 /* static */ 2240 PopupBlockerState ChromeUtils::GetPopupControlState(GlobalObject& aGlobal) { 2241 switch (PopupBlocker::GetPopupControlState()) { 2242 case PopupBlocker::PopupControlState::openAllowed: 2243 return PopupBlockerState::OpenAllowed; 2244 2245 case PopupBlocker::PopupControlState::openControlled: 2246 return PopupBlockerState::OpenControlled; 2247 2248 case PopupBlocker::PopupControlState::openBlocked: 2249 return PopupBlockerState::OpenBlocked; 2250 2251 case PopupBlocker::PopupControlState::openAbused: 2252 return PopupBlockerState::OpenAbused; 2253 2254 case PopupBlocker::PopupControlState::openOverridden: 2255 return PopupBlockerState::OpenOverridden; 2256 2257 default: 2258 MOZ_CRASH( 2259 "PopupBlocker::PopupControlState and PopupBlockerState are out of " 2260 "sync"); 2261 } 2262 } 2263 2264 /* static */ 2265 double ChromeUtils::LastExternalProtocolIframeAllowed(GlobalObject& aGlobal) { 2266 TimeStamp when = PopupBlocker::WhenLastExternalProtocolIframeAllowed(); 2267 if (when.IsNull()) { 2268 return 0; 2269 } 2270 2271 TimeDuration duration = TimeStamp::Now() - when; 2272 return duration.ToMilliseconds(); 2273 } 2274 2275 /* static */ 2276 void ChromeUtils::ResetLastExternalProtocolIframeAllowed( 2277 GlobalObject& aGlobal) { 2278 PopupBlocker::ResetLastExternalProtocolIframeAllowed(); 2279 } 2280 2281 /* static */ 2282 already_AddRefed<Promise> ChromeUtils::EndWheelTransaction( 2283 GlobalObject& aGlobal, WindowProxyHolder& aWindow, ErrorResult& aRv) { 2284 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2285 MOZ_ASSERT(global); 2286 2287 RefPtr<Promise> promise = Promise::Create(global, aRv); 2288 if (aRv.Failed()) { 2289 return nullptr; 2290 } 2291 2292 // This allows us to end the current wheel transaction from the browser 2293 // chrome. We do not need to perform any checks before calling 2294 // EndTransaction(), as it should do nothing in the case that there is 2295 // no current wheel transaction. 2296 WheelTransaction::EndTransaction(); 2297 2298 // We also need to end the wheel transaction in APZ. 2299 nsIDocShell* docShell = aWindow.get()->GetDocShell(); 2300 if (!docShell) { 2301 promise->MaybeResolveWithUndefined(); 2302 return promise.forget(); 2303 } 2304 2305 nsIWidget* widget = 2306 nsContentUtils::GetWidget(docShell->GetPresShell(), nullptr); 2307 if (!widget) { 2308 promise->MaybeResolveWithUndefined(); 2309 return promise.forget(); 2310 } 2311 2312 WindowRenderer* renderer = widget->GetWindowRenderer(); 2313 if (!renderer) { 2314 promise->MaybeResolveWithUndefined(); 2315 return promise.forget(); 2316 } 2317 2318 layers::WebRenderLayerManager* wr = renderer->AsWebRender(); 2319 if (!wr) { 2320 promise->MaybeResolveWithUndefined(); 2321 return promise.forget(); 2322 } 2323 2324 layers::WebRenderBridgeChild* wrbc = wr->WrBridge(); 2325 if (!wrbc) { 2326 promise->MaybeResolveWithUndefined(); 2327 return promise.forget(); 2328 } 2329 2330 wrbc->SendEndWheelTransaction()->Then( 2331 GetCurrentSerialEventTarget(), __func__, 2332 [promise](bool) { promise->MaybeResolveWithUndefined(); }, 2333 [promise](mozilla::ipc::ResponseRejectReason) { 2334 promise->MaybeRejectWithUnknownError( 2335 "actor died while ending wheel transaction"); 2336 }); 2337 2338 return promise.forget(); 2339 } 2340 2341 /* static */ 2342 void ChromeUtils::RegisterWindowActor(const GlobalObject& aGlobal, 2343 const nsACString& aName, 2344 const WindowActorOptions& aOptions, 2345 ErrorResult& aRv) { 2346 if (!XRE_IsParentProcess()) { 2347 aRv.ThrowNotAllowedError( 2348 "registerWindowActor() may only be called in the parent process"); 2349 return; 2350 } 2351 2352 RefPtr<JSActorService> service = JSActorService::GetSingleton(); 2353 service->RegisterWindowActor(aName, aOptions, aRv); 2354 } 2355 2356 /* static */ 2357 void ChromeUtils::UnregisterWindowActor(const GlobalObject& aGlobal, 2358 const nsACString& aName, 2359 ErrorResult& aRv) { 2360 if (!XRE_IsParentProcess()) { 2361 aRv.ThrowNotAllowedError( 2362 "unregisterWindowActor() may only be called in the parent process"); 2363 return; 2364 } 2365 2366 RefPtr<JSActorService> service = JSActorService::GetSingleton(); 2367 service->UnregisterWindowActor(aName); 2368 } 2369 2370 /* static */ 2371 void ChromeUtils::RegisterProcessActor(const GlobalObject& aGlobal, 2372 const nsACString& aName, 2373 const ProcessActorOptions& aOptions, 2374 ErrorResult& aRv) { 2375 if (!XRE_IsParentProcess()) { 2376 aRv.ThrowNotAllowedError( 2377 "registerProcessActor() may only be called in the parent process"); 2378 return; 2379 } 2380 2381 RefPtr<JSActorService> service = JSActorService::GetSingleton(); 2382 service->RegisterProcessActor(aName, aOptions, aRv); 2383 } 2384 2385 /* static */ 2386 void ChromeUtils::UnregisterProcessActor(const GlobalObject& aGlobal, 2387 const nsACString& aName, 2388 ErrorResult& aRv) { 2389 if (!XRE_IsParentProcess()) { 2390 aRv.ThrowNotAllowedError( 2391 "unregisterProcessActor() may only be called in the parent process"); 2392 return; 2393 } 2394 2395 RefPtr<JSActorService> service = JSActorService::GetSingleton(); 2396 service->UnregisterProcessActor(aName); 2397 } 2398 2399 /* static */ 2400 already_AddRefed<Promise> ChromeUtils::EnsureHeadlessContentProcess( 2401 const GlobalObject& aGlobal, const nsACString& aRemoteType, 2402 ErrorResult& aRv) { 2403 if (!XRE_IsParentProcess()) { 2404 aRv.ThrowNotAllowedError( 2405 "ensureHeadlessContentProcess() may only be called in the parent " 2406 "process"); 2407 return nullptr; 2408 } 2409 2410 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2411 RefPtr<Promise> promise = Promise::Create(global, aRv); 2412 if (aRv.Failed()) { 2413 return nullptr; 2414 } 2415 2416 ContentParent::GetNewOrUsedBrowserProcessAsync(aRemoteType) 2417 ->Then( 2418 GetCurrentSerialEventTarget(), __func__, 2419 [promise](UniqueContentParentKeepAlive&& aKeepAlive) { 2420 nsCOMPtr<nsIContentParentKeepAlive> jsKeepAlive = 2421 WrapContentParentKeepAliveForJS(std::move(aKeepAlive)); 2422 promise->MaybeResolve(jsKeepAlive); 2423 }, 2424 [promise](nsresult aError) { promise->MaybeReject(aError); }); 2425 return promise.forget(); 2426 } 2427 2428 /* static */ 2429 bool ChromeUtils::IsClassifierBlockingErrorCode(GlobalObject& aGlobal, 2430 uint32_t aError) { 2431 return net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode( 2432 static_cast<nsresult>(aError)); 2433 } 2434 2435 /* static */ 2436 void ChromeUtils::PrivateNoteIntentionalCrash(const GlobalObject& aGlobal, 2437 ErrorResult& aError) { 2438 if (XRE_IsContentProcess()) { 2439 NoteIntentionalCrash("tab"); 2440 return; 2441 } 2442 aError.Throw(NS_ERROR_NOT_IMPLEMENTED); 2443 } 2444 2445 /* static */ 2446 nsIDOMProcessChild* ChromeUtils::GetDomProcessChild(const GlobalObject&) { 2447 return nsIDOMProcessChild::GetSingleton(); 2448 } 2449 2450 /* static */ 2451 void ChromeUtils::GetAllDOMProcesses( 2452 GlobalObject& aGlobal, nsTArray<RefPtr<nsIDOMProcessParent>>& aParents, 2453 ErrorResult& aRv) { 2454 if (!XRE_IsParentProcess()) { 2455 aRv.ThrowNotAllowedError( 2456 "getAllDOMProcesses() may only be called in the parent process"); 2457 return; 2458 } 2459 aParents.Clear(); 2460 // Always add the parent process nsIDOMProcessParent first 2461 aParents.AppendElement(InProcessParent::Singleton()); 2462 2463 // Before adding nsIDOMProcessParent for all the content processes 2464 for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { 2465 aParents.AppendElement(cp); 2466 } 2467 } 2468 2469 /* static */ 2470 void ChromeUtils::ConsumeInteractionData( 2471 GlobalObject& aGlobal, Record<nsString, InteractionData>& aInteractions, 2472 ErrorResult& aRv) { 2473 if (!XRE_IsParentProcess()) { 2474 aRv.ThrowNotAllowedError( 2475 "consumeInteractionData() may only be called in the parent " 2476 "process"); 2477 return; 2478 } 2479 EventStateManager::ConsumeInteractionData(aInteractions); 2480 } 2481 2482 already_AddRefed<Promise> ChromeUtils::CollectScrollingData( 2483 GlobalObject& aGlobal, ErrorResult& aRv) { 2484 // Creating a JS promise 2485 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2486 MOZ_ASSERT(global); 2487 2488 RefPtr<Promise> promise = Promise::Create(global, aRv); 2489 if (aRv.Failed()) { 2490 return nullptr; 2491 } 2492 2493 RefPtr<ScrollingMetrics::ScrollingMetricsPromise> extPromise = 2494 ScrollingMetrics::CollectScrollingMetrics(); 2495 2496 extPromise->Then( 2497 GetCurrentSerialEventTarget(), __func__, 2498 [promise](const std::tuple<uint32_t, uint32_t>& aResult) { 2499 InteractionData out = {}; 2500 out.mInteractionTimeInMilliseconds = std::get<0>(aResult); 2501 out.mScrollingDistanceInPixels = std::get<1>(aResult); 2502 promise->MaybeResolve(out); 2503 }, 2504 [promise](bool aValue) { promise->MaybeReject(NS_ERROR_FAILURE); }); 2505 2506 return promise.forget(); 2507 } 2508 2509 /* static */ 2510 void ChromeUtils::GetFormAutofillConfidences( 2511 GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements, 2512 nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv) { 2513 FormAutofillNative::GetFormAutofillConfidences(aGlobal, aElements, aResults, 2514 aRv); 2515 } 2516 2517 bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) { 2518 nsIFrame* f = aElement.GetPrimaryFrame(FlushType::Frames); 2519 if (!f) { 2520 return false; 2521 } 2522 return nsNativeTheme::IsDarkBackground(f); 2523 } 2524 2525 double ChromeUtils::DateNow(GlobalObject&) { return JS_Now() / 1000.0; } 2526 2527 /* static */ 2528 double ChromeUtils::Now(GlobalObject&) { 2529 return (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds(); 2530 } 2531 2532 /* static */ 2533 void ChromeUtils::EnsureJSOracleStarted(GlobalObject&) { 2534 if (StaticPrefs::browser_opaqueResponseBlocking_javascriptValidator()) { 2535 JSOracleParent::WithJSOracle([](JSOracleParent* aParent) {}); 2536 } 2537 } 2538 2539 /* static */ 2540 unsigned ChromeUtils::AliveUtilityProcesses(const GlobalObject&) { 2541 const auto& utilityProcessManager = 2542 mozilla::ipc::UtilityProcessManager::GetIfExists(); 2543 return utilityProcessManager ? utilityProcessManager->AliveProcesses() : 0; 2544 } 2545 2546 /* static */ 2547 void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal, 2548 nsTArray<nsCString>& aNames) { 2549 aNames.Clear(); 2550 for (UtilityActorName idlName : 2551 MakeWebIDLEnumeratedRange<WebIDLUtilityActorName>()) { 2552 aNames.AppendElement(GetEnumString(idlName)); 2553 } 2554 } 2555 2556 /* static */ 2557 bool ChromeUtils::ShouldResistFingerprinting( 2558 GlobalObject& aGlobal, JSRFPTarget aTarget, 2559 nsIRFPTargetSetIDL* aOverriddenFingerprintingSettings, 2560 const Optional<bool>& aIsPBM) { 2561 RFPTarget target; 2562 #define JSRFP_TARGET_TO_RFP_TARGET(rfptarget) \ 2563 case JSRFPTarget::rfptarget: \ 2564 target = RFPTarget::rfptarget; \ 2565 break; 2566 switch (aTarget) { 2567 JSRFP_TARGET_TO_RFP_TARGET(RoundWindowSize); 2568 JSRFP_TARGET_TO_RFP_TARGET(SiteSpecificZoom); 2569 JSRFP_TARGET_TO_RFP_TARGET(CSSPrefersColorScheme); 2570 JSRFP_TARGET_TO_RFP_TARGET(JSLocalePrompt); 2571 JSRFP_TARGET_TO_RFP_TARGET(HttpUserAgent); 2572 default: 2573 MOZ_CRASH("Unhandled JSRFPTarget enum value"); 2574 } 2575 #undef JSRFP_TARGET_TO_RFP_TARGET 2576 2577 bool isPBM = false; 2578 if (aIsPBM.WasPassed()) { 2579 isPBM = aIsPBM.Value(); 2580 } else { 2581 nsCOMPtr<nsIGlobalObject> global = 2582 do_QueryInterface(aGlobal.GetAsSupports()); 2583 if (global) { 2584 nsPIDOMWindowInner* win = global->GetAsInnerWindow(); 2585 if (win) { 2586 nsIDocShell* docshell = win->GetDocShell(); 2587 if (docshell) { 2588 nsDocShell::Cast(docshell)->GetUsePrivateBrowsing(&isPBM); 2589 } 2590 } 2591 } 2592 } 2593 2594 Maybe<RFPTargetSet> overriddenFingerprintingSettings; 2595 if (aOverriddenFingerprintingSettings) { 2596 overriddenFingerprintingSettings.emplace( 2597 static_cast<nsRFPTargetSetIDL*>(aOverriddenFingerprintingSettings) 2598 ->ToRFPTargetSet()); 2599 } 2600 2601 // This global object appears to be the global window, not for individual 2602 // sites so to exempt individual sites (instead of just PBM/Not-PBM windows) 2603 // more work would be needed to get the correct context. 2604 return nsRFPService::IsRFPEnabledFor(isPBM, target, 2605 overriddenFingerprintingSettings); 2606 } 2607 2608 /* static */ 2609 void ChromeUtils::CallFunctionAndLogException( 2610 GlobalObject& aGlobal, JS::Handle<JS::Value> aTargetGlobal, 2611 JS::Handle<JS::Value> aFunction, JS::MutableHandle<JS::Value> aRetVal, 2612 ErrorResult& aRv) { 2613 JSContext* cx = aGlobal.Context(); 2614 if (!aTargetGlobal.isObject() || !aFunction.isObject()) { 2615 aRv.Throw(NS_ERROR_INVALID_ARG); 2616 return; 2617 } 2618 2619 JS::Rooted<JS::Realm*> contextRealm(cx, JS::GetCurrentRealmOrNull(cx)); 2620 if (!contextRealm) { 2621 aRv.Throw(NS_ERROR_INVALID_ARG); 2622 return; 2623 } 2624 2625 JS::Rooted<JSObject*> global( 2626 cx, js::CheckedUnwrapDynamic(&aTargetGlobal.toObject(), cx)); 2627 if (!global) { 2628 aRv.Throw(NS_ERROR_INVALID_ARG); 2629 return; 2630 } 2631 2632 // Use AutoJSAPI in order to trigger AutoJSAPI::ReportException 2633 // which will do most of the work required for this function. 2634 // 2635 // We only have to pick the right global for which we want to flag 2636 // the exception against. 2637 dom::AutoJSAPI jsapi; 2638 if (!jsapi.Init(global)) { 2639 aRv.Throw(NS_ERROR_UNEXPECTED); 2640 return; 2641 } 2642 JSContext* ccx = jsapi.cx(); 2643 2644 // AutoJSAPI picks `aTargetGlobal` as execution compartment 2645 // whereas we expect to run `aFunction` from the callsites compartment. 2646 JSAutoRealm ar(ccx, JS::GetRealmGlobalOrNull(contextRealm)); 2647 2648 JS::Rooted<JS::Value> funVal(ccx, aFunction); 2649 if (!JS_WrapValue(ccx, &funVal)) { 2650 aRv.Throw(NS_ERROR_FAILURE); 2651 return; 2652 } 2653 if (!JS_CallFunctionValue(ccx, nullptr, funVal, JS::HandleValueArray::empty(), 2654 aRetVal)) { 2655 // Ensure re-throwing the exception which may have been thrown by 2656 // `aFunction` 2657 if (JS_IsExceptionPending(ccx)) { 2658 JS::Rooted<JS::Value> exception(cx); 2659 if (JS_GetPendingException(ccx, &exception)) { 2660 if (JS_WrapValue(cx, &exception)) { 2661 aRv.MightThrowJSException(); 2662 aRv.ThrowJSException(cx, exception); 2663 } 2664 } 2665 } 2666 } 2667 } 2668 2669 static Atomic<uint32_t, Relaxed> sDevToolsOpenedCount{0}; 2670 2671 /* static */ 2672 bool ChromeUtils::IsDevToolsOpened() { return sDevToolsOpenedCount > 0; } 2673 2674 /* static */ 2675 bool ChromeUtils::IsDevToolsOpened(GlobalObject& aGlobal) { 2676 return IsDevToolsOpened(); 2677 } 2678 2679 /* static */ 2680 void ChromeUtils::NotifyDevToolsOpened(GlobalObject& aGlobal) { 2681 sDevToolsOpenedCount++; 2682 } 2683 2684 /* static */ 2685 void ChromeUtils::NotifyDevToolsClosed(GlobalObject& aGlobal) { 2686 MOZ_ASSERT(sDevToolsOpenedCount >= 1); 2687 sDevToolsOpenedCount--; 2688 } 2689 2690 /* static */ 2691 bool ChromeUtils::IsJSIdentifier(GlobalObject& aGlobal, const nsAString& aStr) { 2692 return JS_IsIdentifier(aStr.BeginReading(), aStr.Length()); 2693 } 2694 2695 #ifdef MOZ_WMF_CDM 2696 /* static */ 2697 already_AddRefed<Promise> ChromeUtils::GetWMFContentDecryptionModuleInformation( 2698 GlobalObject& aGlobal, ErrorResult& aRv) { 2699 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2700 MOZ_ASSERT(global); 2701 RefPtr<Promise> domPromise = Promise::Create(global, aRv); 2702 if (NS_WARN_IF(aRv.Failed())) { 2703 return nullptr; 2704 } 2705 MOZ_ASSERT(domPromise); 2706 MFCDMService::GetAllKeySystemsCapabilities(domPromise); 2707 return domPromise.forget(); 2708 } 2709 #endif 2710 2711 already_AddRefed<Promise> ChromeUtils::GetGMPContentDecryptionModuleInformation( 2712 GlobalObject& aGlobal, ErrorResult& aRv) { 2713 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2714 MOZ_ASSERT(global); 2715 RefPtr<Promise> domPromise = Promise::Create(global, aRv); 2716 if (NS_WARN_IF(aRv.Failed())) { 2717 return nullptr; 2718 } 2719 MOZ_ASSERT(domPromise); 2720 KeySystemConfig::GetGMPKeySystemConfigs(domPromise); 2721 return domPromise.forget(); 2722 } 2723 2724 void ChromeUtils::AndroidMoveTaskToBack(GlobalObject& aGlobal) { 2725 #ifdef MOZ_WIDGET_ANDROID 2726 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 2727 java::GeckoAppShell::MoveTaskToBack(); 2728 #endif 2729 } 2730 2731 already_AddRefed<nsIContentSecurityPolicy> ChromeUtils::CreateCSPFromHeader( 2732 GlobalObject& aGlobal, const nsAString& aHeader, nsIURI* aSelfURI, 2733 nsIPrincipal* aLoadingPrincipal, ErrorResult& aRv) { 2734 return CSP_CreateFromHeader(aHeader, aSelfURI, aLoadingPrincipal, aRv); 2735 } 2736 2737 Nullable<bool> ChromeUtils::GetGlobalWindowCommandEnabled( 2738 GlobalObject&, const nsACString& aName) { 2739 const auto* table = nsControllerCommandTable::WindowCommandTable(); 2740 RefPtr handler = table->FindCommandHandler(aName); 2741 if (!handler) { 2742 return nullptr; 2743 } 2744 return handler->IsCommandEnabled(aName, nullptr); 2745 } 2746 2747 already_AddRefed<Promise> ChromeUtils::FetchDecodedImage(GlobalObject& aGlobal, 2748 nsIURI* aURI, 2749 nsIChannel* aChannel, 2750 ErrorResult& aRv) { 2751 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 2752 MOZ_ASSERT(global); 2753 RefPtr<Promise> domPromise = Promise::Create(global, aRv); 2754 if (NS_WARN_IF(aRv.Failed())) { 2755 return nullptr; 2756 } 2757 2758 image::FetchDecodedImage(aURI, aChannel, gfx::IntSize{}) 2759 ->Then( 2760 GetCurrentSerialEventTarget(), __func__, 2761 [global, domPromise](already_AddRefed<imgIContainer> aImage) { 2762 nsCOMPtr<imgIContainer> image(std::move(aImage)); 2763 2764 AutoJSAPI jsapi; 2765 if (!jsapi.Init(global)) { 2766 domPromise->MaybeRejectWithUndefined(); 2767 return; 2768 } 2769 2770 JS::Rooted<JS::Value> value(jsapi.cx()); 2771 if (!WrapObject(jsapi.cx(), image, &NS_GET_IID(imgIContainer), 2772 &value)) { 2773 domPromise->MaybeRejectWithUndefined(); 2774 return; 2775 } 2776 2777 domPromise->MaybeResolve(value); 2778 }, 2779 [domPromise](nsresult aStatus) { domPromise->MaybeReject(aStatus); }); 2780 2781 return domPromise.forget(); 2782 } 2783 2784 void ChromeUtils::EncodeURIForSrcset(GlobalObject&, const nsACString& aIn, 2785 nsACString& aOut) { 2786 const auto inputLen = aIn.Length(); 2787 if (!inputLen) { 2788 return; 2789 } 2790 size_t start = 0; 2791 while (true) { 2792 auto idx = aIn.View().find_first_of(nsContentUtils::kHTMLWhitespace, start); 2793 if (idx == std::string_view::npos) { 2794 break; 2795 } 2796 aOut.Append(Substring(aIn, start, idx - start)); 2797 aOut.AppendPrintf("%%%x", aIn.CharAt(idx)); 2798 start = idx + 1; 2799 if (start == inputLen) { 2800 return; 2801 } 2802 } 2803 if (start == 0) { 2804 aOut.Assign(aIn); 2805 } else { 2806 aOut.Append(Substring(aIn, start)); 2807 } 2808 } 2809 2810 void ChromeUtils::GetLastOOMStackTrace(GlobalObject& aGlobal, 2811 nsAString& aRetval) { 2812 JSContext* cx = aGlobal.Context(); 2813 aRetval = NS_ConvertUTF8toUTF16(JS_GetLastOOMStackTrace(cx)); 2814 } 2815 2816 } // namespace mozilla::dom