nsXPConnect.cpp (38073B)
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 /* High level class and public functions implementation. */ 8 9 #include "js/Transcoding.h" 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Base64.h" 12 #include "mozilla/Likely.h" 13 14 #include "XPCWrapper.h" 15 #include "jsfriendapi.h" 16 #include "js/AllocationLogging.h" // JS::SetLogCtorDtorFunctions 17 #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions 18 #include "js/Object.h" // JS::GetClass 19 #include "js/ProfilingStack.h" 20 #include "GeckoProfiler.h" 21 #include "mozJSModuleLoader.h" 22 #include "nsJSEnvironment.h" 23 #include "nsThreadUtils.h" 24 #include "nsDOMJSUtils.h" 25 26 #include "WrapperFactory.h" 27 #include "AccessCheck.h" 28 #include "JSServices.h" 29 30 #include "mozilla/BasePrincipal.h" 31 #include "mozilla/dom/BindingUtils.h" 32 #include "mozilla/dom/DOMException.h" 33 #include "mozilla/dom/Exceptions.h" 34 #include "mozilla/dom/Promise.h" 35 #include "mozilla/glean/bindings/Glean.h" 36 #include "mozilla/glean/bindings/GleanPings.h" 37 #include "mozilla/ScriptPreloader.h" 38 39 #include "nsDOMMutationObserver.h" 40 #include "nsICycleCollectorListener.h" 41 #include "nsCycleCollector.h" 42 #include "nsIOService.h" 43 #include "nsIObjectInputStream.h" 44 #include "nsIObjectOutputStream.h" 45 #include "nsScriptSecurityManager.h" 46 #include "nsContentUtils.h" 47 #include "nsScriptError.h" 48 #include "nsJSUtils.h" 49 #include "nsRFPService.h" 50 #include "prsystem.h" 51 52 #include "xpcprivate.h" 53 54 #ifdef XP_WIN 55 # include "mozilla/WinHeaderOnlyUtils.h" 56 #else 57 # include <sys/mman.h> 58 #endif 59 60 using namespace mozilla; 61 using namespace mozilla::dom; 62 using namespace xpc; 63 using namespace JS; 64 65 NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect) 66 67 nsXPConnect* nsXPConnect::gSelf = nullptr; 68 bool nsXPConnect::gOnceAliveNowDead = false; 69 70 // Global cache of the default script security manager (QI'd to 71 // nsIScriptSecurityManager) and the system principal. 72 nsIScriptSecurityManager* nsXPConnect::gScriptSecurityManager = nullptr; 73 nsIPrincipal* nsXPConnect::gSystemPrincipal = nullptr; 74 75 const char XPC_EXCEPTION_CONTRACTID[] = "@mozilla.org/js/xpc/Exception;1"; 76 const char XPC_CONSOLE_CONTRACTID[] = "@mozilla.org/consoleservice;1"; 77 const char XPC_SCRIPT_ERROR_CONTRACTID[] = "@mozilla.org/scripterror;1"; 78 79 /***************************************************************************/ 80 81 nsXPConnect::nsXPConnect() { 82 #ifdef MOZ_GECKO_PROFILER 83 JS::SetProfilingThreadCallbacks(profiler_register_thread, 84 profiler_unregister_thread); 85 #endif 86 } 87 88 // static 89 void nsXPConnect::InitJSContext() { 90 MOZ_ASSERT(!gSelf->mContext); 91 92 XPCJSContext* xpccx = XPCJSContext::NewXPCJSContext(); 93 if (!xpccx) { 94 MOZ_CRASH("Couldn't create XPCJSContext."); 95 } 96 gSelf->mContext = xpccx; 97 gSelf->mRuntime = xpccx->Runtime(); 98 99 mozJSModuleLoader::InitStatics(); 100 101 // Initialize the script preloader cache. 102 (void)mozilla::ScriptPreloader::GetSingleton(); 103 104 nsJSContext::EnsureStatics(); 105 } 106 107 void xpc::InitializeJSContext() { nsXPConnect::InitJSContext(); } 108 109 nsXPConnect::~nsXPConnect() { 110 MOZ_ASSERT(mRuntime); 111 112 mRuntime->DeleteSingletonScopes(); 113 114 // In order to clean up everything properly, we need to GC twice: once now, 115 // to clean anything that can go away on its own (like the Junk Scope, which 116 // we unrooted above), and once after forcing a bunch of shutdown in 117 // XPConnect, to clean the stuff we forcibly disconnected. The forced 118 // shutdown code defaults to leaking in a number of situations, so we can't 119 // get by with only the second GC. :-( 120 // 121 // Bug 1650075: These should really pass GCOptions::Shutdown but doing that 122 // seems to cause crashes. 123 mRuntime->GarbageCollect(JS::GCOptions::Normal, 124 JS::GCReason::XPCONNECT_SHUTDOWN); 125 126 XPCWrappedNativeScope::SystemIsBeingShutDown(); 127 128 // The above causes us to clean up a bunch of XPConnect data structures, 129 // after which point we need to GC to clean everything up. We need to do 130 // this before deleting the XPCJSContext, because doing so destroys the 131 // maps that our finalize callback depends on. 132 mRuntime->GarbageCollect(JS::GCOptions::Normal, 133 JS::GCReason::XPCONNECT_SHUTDOWN); 134 135 NS_RELEASE(gSystemPrincipal); 136 gScriptSecurityManager = nullptr; 137 138 // shutdown the logging system 139 XPC_LOG_FINISH(); 140 141 delete mContext; 142 143 MOZ_ASSERT(gSelf == this); 144 gSelf = nullptr; 145 gOnceAliveNowDead = true; 146 } 147 148 // static 149 void nsXPConnect::InitStatics() { 150 #ifdef NS_BUILD_REFCNT_LOGGING 151 // These functions are used for reporting leaks, so we register them as early 152 // as possible to avoid missing any classes' creations. 153 JS::SetLogCtorDtorFunctions(NS_LogCtor, NS_LogDtor); 154 #endif 155 156 gSelf = new nsXPConnect(); 157 gOnceAliveNowDead = false; 158 159 // Initial extra ref to keep the singleton alive 160 // balanced by explicit call to ReleaseXPConnectSingleton() 161 NS_ADDREF(gSelf); 162 163 // Fire up the SSM. 164 nsScriptSecurityManager::InitStatics(); 165 gScriptSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager(); 166 gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal); 167 MOZ_RELEASE_ASSERT(gSystemPrincipal); 168 } 169 170 // static 171 void nsXPConnect::ReleaseXPConnectSingleton() { 172 nsXPConnect* xpc = gSelf; 173 if (xpc) { 174 nsrefcnt cnt; 175 NS_RELEASE2(xpc, cnt); 176 } 177 178 mozJSModuleLoader::ShutdownLoaders(); 179 } 180 181 // static 182 XPCJSRuntime* nsXPConnect::GetRuntimeInstance() { 183 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 184 return gSelf->mRuntime; 185 } 186 187 void xpc::ErrorBase::Init(JSErrorBase* aReport) { 188 if (!aReport->filename) { 189 mFileName.SetIsVoid(true); 190 } else { 191 mFileName.Assign(aReport->filename.c_str()); 192 } 193 194 mSourceId = aReport->sourceId; 195 mLineNumber = aReport->lineno; 196 mColumn = aReport->column.oneOriginValue(); 197 } 198 199 void xpc::ErrorNote::Init(JSErrorNotes::Note* aNote) { 200 xpc::ErrorBase::Init(aNote); 201 202 ErrorNoteToMessageString(aNote, mErrorMsg); 203 } 204 205 void xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult, 206 bool aIsChrome, uint64_t aWindowID) { 207 xpc::ErrorBase::Init(aReport); 208 mCategory = aIsChrome ? "chrome javascript"_ns : "content javascript"_ns; 209 mWindowID = aWindowID; 210 211 if (aToStringResult) { 212 AppendUTF8toUTF16(mozilla::MakeStringSpan(aToStringResult), mErrorMsg); 213 } 214 if (mErrorMsg.IsEmpty()) { 215 ErrorReportToMessageString(aReport, mErrorMsg); 216 } 217 if (mErrorMsg.IsEmpty()) { 218 mErrorMsg.AssignLiteral("<unknown>"); 219 } 220 221 if (aReport->errorMessageName) { 222 mErrorMsgName.AssignASCII(aReport->errorMessageName); 223 } else { 224 mErrorMsgName.Truncate(); 225 } 226 227 mIsWarning = aReport->isWarning(); 228 mIsMuted = aReport->isMuted; 229 230 if (aReport->notes) { 231 if (!mNotes.SetLength(aReport->notes->length(), fallible)) { 232 return; 233 } 234 235 size_t i = 0; 236 for (auto&& note : *aReport->notes) { 237 mNotes.ElementAt(i).Init(note.get()); 238 i++; 239 } 240 } 241 } 242 243 void xpc::ErrorReport::Init(JSContext* aCx, mozilla::dom::Exception* aException, 244 bool aIsChrome, uint64_t aWindowID) { 245 mCategory = aIsChrome ? "chrome javascript"_ns : "content javascript"_ns; 246 mWindowID = aWindowID; 247 248 aException->GetErrorMessage(mErrorMsg); 249 250 aException->GetFilename(aCx, mFileName); 251 if (mFileName.IsEmpty()) { 252 mFileName.SetIsVoid(true); 253 } 254 mSourceId = aException->SourceId(aCx); 255 mLineNumber = aException->LineNumber(aCx); 256 mColumn = aException->ColumnNumber(aCx); 257 } 258 259 static LazyLogModule gJSDiagnostics("JSDiagnostics"); 260 261 void xpc::ErrorBase::AppendErrorDetailsTo(nsCString& error) { 262 error.Append(mFileName); 263 error.AppendLiteral(", line "); 264 error.AppendInt(mLineNumber, 10); 265 error.AppendLiteral(": "); 266 AppendUTF16toUTF8(mErrorMsg, error); 267 } 268 269 void xpc::ErrorNote::LogToStderr() { 270 if (!nsJSUtils::DumpEnabled()) { 271 return; 272 } 273 274 nsAutoCString error; 275 error.AssignLiteral("JavaScript note: "); 276 AppendErrorDetailsTo(error); 277 278 fprintf(stderr, "%s\n", error.get()); 279 fflush(stderr); 280 } 281 282 void xpc::ErrorReport::LogToStderr() { 283 if (!nsJSUtils::DumpEnabled()) { 284 return; 285 } 286 287 nsAutoCString error; 288 error.AssignLiteral("JavaScript "); 289 if (IsWarning()) { 290 error.AppendLiteral("warning: "); 291 } else { 292 error.AppendLiteral("error: "); 293 } 294 AppendErrorDetailsTo(error); 295 296 fprintf(stderr, "%s\n", error.get()); 297 fflush(stderr); 298 299 for (size_t i = 0, len = mNotes.Length(); i < len; i++) { 300 ErrorNote& note = mNotes[i]; 301 note.LogToStderr(); 302 } 303 } 304 305 void xpc::ErrorReport::LogToConsole() { 306 LogToConsoleWithStack(nullptr, JS::NothingHandleValue, nullptr, nullptr); 307 } 308 309 void xpc::ErrorReport::LogToConsoleWithStack( 310 nsGlobalWindowInner* aWin, JS::Handle<mozilla::Maybe<JS::Value>> aException, 311 JS::HandleObject aStack, JS::HandleObject aStackGlobal) { 312 if (aStack) { 313 MOZ_ASSERT(aStackGlobal); 314 MOZ_ASSERT(JS_IsGlobalObject(aStackGlobal)); 315 js::AssertSameCompartment(aStack, aStackGlobal); 316 } else { 317 MOZ_ASSERT(!aStackGlobal); 318 } 319 320 LogToStderr(); 321 322 MOZ_LOG(gJSDiagnostics, IsWarning() ? LogLevel::Warning : LogLevel::Error, 323 ("file %s, line %u\n%s", mFileName.get(), mLineNumber, 324 NS_ConvertUTF16toUTF8(mErrorMsg).get())); 325 326 // Log to the console. We do this last so that we can simply return if 327 // there's no console service without affecting the other reporting 328 // mechanisms. 329 nsCOMPtr<nsIConsoleService> consoleService = 330 do_GetService(NS_CONSOLESERVICE_CONTRACTID); 331 NS_ENSURE_TRUE_VOID(consoleService); 332 333 RefPtr<nsScriptErrorBase> errorObject = 334 CreateScriptError(aWin, aException, aStack, aStackGlobal); 335 errorObject->SetErrorMessageName(mErrorMsgName); 336 337 uint32_t flags = 338 mIsWarning ? nsIScriptError::warningFlag : nsIScriptError::errorFlag; 339 nsresult rv = errorObject->InitWithWindowID( 340 mErrorMsg, mFileName, mLineNumber, mColumn, flags, mCategory, mWindowID, 341 mCategory.Equals("chrome javascript"_ns)); 342 NS_ENSURE_SUCCESS_VOID(rv); 343 344 rv = errorObject->InitSourceId(mSourceId); 345 NS_ENSURE_SUCCESS_VOID(rv); 346 347 rv = errorObject->InitIsPromiseRejection(mIsPromiseRejection); 348 NS_ENSURE_SUCCESS_VOID(rv); 349 350 for (size_t i = 0, len = mNotes.Length(); i < len; i++) { 351 ErrorNote& note = mNotes[i]; 352 353 nsScriptErrorNote* noteObject = new nsScriptErrorNote(); 354 noteObject->Init(note.mErrorMsg, note.mFileName, note.mSourceId, 355 note.mLineNumber, note.mColumn); 356 errorObject->AddNote(noteObject); 357 } 358 359 consoleService->LogMessage(errorObject); 360 } 361 362 /* static */ 363 void xpc::ErrorNote::ErrorNoteToMessageString(JSErrorNotes::Note* aNote, 364 nsAString& aString) { 365 aString.Truncate(); 366 if (aNote->message()) { 367 aString.Append(NS_ConvertUTF8toUTF16(aNote->message().c_str())); 368 } 369 } 370 371 /* static */ 372 void xpc::ErrorReport::ErrorReportToMessageString(JSErrorReport* aReport, 373 nsAString& aString) { 374 aString.Truncate(); 375 if (aReport->message()) { 376 // Don't prefix warnings with an often misleading name like "Error: ". 377 if (!aReport->isWarning()) { 378 JSLinearString* name = js::GetErrorTypeName( 379 CycleCollectedJSContext::Get()->Context(), aReport->exnType); 380 if (name) { 381 AssignJSLinearString(aString, name); 382 aString.AppendLiteral(": "); 383 } 384 } 385 aString.Append(NS_ConvertUTF8toUTF16(aReport->message().c_str())); 386 } 387 } 388 389 /***************************************************************************/ 390 391 void xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS) { 392 // QIing to nsIXPConnectWrappedJSUnmarkGray may have side effects! 393 nsCOMPtr<nsIXPConnectWrappedJSUnmarkGray> wjsug = 394 do_QueryInterface(aWrappedJS); 395 (void)wjsug; 396 MOZ_ASSERT(!wjsug, 397 "One should never be able to QI to " 398 "nsIXPConnectWrappedJSUnmarkGray successfully!"); 399 } 400 401 /***************************************************************************/ 402 /***************************************************************************/ 403 // nsIXPConnect interface methods... 404 405 template <typename T> 406 static inline T UnexpectedFailure(T rv) { 407 NS_ERROR("This is not supposed to fail!"); 408 return rv; 409 } 410 411 void xpc::TraceXPCGlobal(JSTracer* trc, JSObject* obj) { 412 if (JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL) { 413 mozilla::dom::TraceProtoAndIfaceCache(trc, obj); 414 } 415 416 // We might be called from a GC during the creation of a global, before we've 417 // been able to set up the compartment private. 418 if (xpc::CompartmentPrivate* priv = xpc::CompartmentPrivate::Get(obj)) { 419 MOZ_ASSERT(priv->GetScope()); 420 priv->GetScope()->TraceInside(trc); 421 } 422 } 423 424 namespace xpc { 425 426 JSObject* CreateGlobalObject(JSContext* cx, const JSClass* clasp, 427 nsIPrincipal* principal, 428 JS::RealmOptions& aOptions) { 429 MOZ_ASSERT(NS_IsMainThread(), "using a principal off the main thread?"); 430 MOZ_ASSERT(principal); 431 432 MOZ_RELEASE_ASSERT( 433 principal != nsContentUtils::GetNullSubjectPrincipal(), 434 "The null subject principal is getting inherited - fix that!"); 435 436 RootedObject global(cx); 437 { 438 SiteIdentifier site; 439 nsresult rv = BasePrincipal::Cast(principal)->GetSiteIdentifier(site); 440 NS_ENSURE_SUCCESS(rv, nullptr); 441 442 global = JS_NewGlobalObject(cx, clasp, nsJSPrincipals::get(principal), 443 JS::DontFireOnNewGlobalHook, aOptions); 444 if (!global) { 445 return nullptr; 446 } 447 JSAutoRealm ar(cx, global); 448 449 RealmPrivate::Init(global, site); 450 451 if (clasp->flags & JSCLASS_DOM_GLOBAL) { 452 #ifdef DEBUG 453 // Verify that the right trace hook is called. Note that this doesn't 454 // work right for wrapped globals, since the tracing situation there is 455 // more complicated. Manual inspection shows that they do the right 456 // thing. Also note that we only check this for JSCLASS_DOM_GLOBAL 457 // classes because xpc::TraceXPCGlobal won't call TraceProtoAndIfaceCache 458 // unless that flag is set. 459 if (!((const JSClass*)clasp)->isWrappedNative()) { 460 VerifyTraceProtoAndIfaceCacheCalledTracer trc(cx); 461 TraceChildren(&trc, GCCellPtr(global.get())); 462 MOZ_ASSERT(trc.ok, 463 "Trace hook on global needs to call TraceXPCGlobal for " 464 "XPConnect compartments."); 465 } 466 #endif 467 468 const char* className = clasp->name; 469 AllocateProtoAndIfaceCache(global, 470 (strcmp(className, "Window") == 0 || 471 strcmp(className, "ChromeWindow") == 0) 472 ? ProtoAndIfaceCache::WindowLike 473 : ProtoAndIfaceCache::NonWindowLike); 474 } 475 } 476 477 return global; 478 } 479 480 void InitGlobalObjectOptions(JS::RealmOptions& aOptions, 481 bool aIsSystemPrincipal, bool aSecureContext, 482 bool aForceUTC, bool aAlwaysUseFdlibm, 483 bool aLocaleEnUS, 484 const nsACString& aLanguageOverride, 485 const nsAString& aTimezoneOverride) { 486 if (aIsSystemPrincipal) { 487 // Make toSource functions [ChromeOnly] 488 aOptions.creationOptions().setToSourceEnabled(true); 489 // Make sure [SecureContext] APIs are visible: 490 aOptions.creationOptions().setSecureContext(true); 491 aOptions.behaviors().setClampAndJitterTime(false); 492 aOptions.behaviors().setDiscardSource(ShouldDiscardSystemSource()); 493 MOZ_ASSERT(aSecureContext, 494 "aIsSystemPrincipal should imply aSecureContext"); 495 } else { 496 aOptions.creationOptions().setSecureContext(aSecureContext); 497 } 498 499 if (aForceUTC) { 500 nsCString timeZone = nsRFPService::GetSpoofedJSTimeZone(); 501 aOptions.behaviors().setTimeZoneOverride(timeZone.get()); 502 } else if (!aTimezoneOverride.IsEmpty()) { 503 aOptions.behaviors().setTimeZoneOverride( 504 NS_ConvertUTF16toUTF8(aTimezoneOverride).get()); 505 } 506 aOptions.creationOptions().setAlwaysUseFdlibm(aAlwaysUseFdlibm); 507 if (aLocaleEnUS) { 508 nsCString locale = nsRFPService::GetSpoofedJSLocale(); 509 aOptions.behaviors().setLocaleOverride(locale.get()); 510 } else if (!aLanguageOverride.IsEmpty()) { 511 aOptions.behaviors().setLocaleOverride( 512 PromiseFlatCString(aLanguageOverride).get()); 513 } 514 } 515 516 bool InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, 517 uint32_t aFlags) { 518 // Immediately enter the global's realm so that everything we create 519 // ends up there. 520 JSAutoRealm ar(aJSContext, aGlobal); 521 522 // Stuff coming through this path always ends up as a DOM global. 523 MOZ_ASSERT(JS::GetClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL); 524 525 if (!(aFlags & xpc::OMIT_COMPONENTS_OBJECT)) { 526 // XPCCallContext gives us an active request needed to save/restore. 527 if (!ObjectScope(aGlobal)->AttachComponentsObject(aJSContext) || 528 !XPCNativeWrapper::AttachNewConstructorObject(aJSContext, aGlobal)) { 529 return UnexpectedFailure(false); 530 } 531 532 if (!mozJSModuleLoader::Get()->DefineJSServices(aJSContext, aGlobal)) { 533 return UnexpectedFailure(false); 534 } 535 } 536 537 if (!(aFlags & xpc::DONT_FIRE_ONNEWGLOBALHOOK)) { 538 JS_FireOnNewGlobalObject(aJSContext, aGlobal); 539 } 540 541 return true; 542 } 543 544 nsresult InitClassesWithNewWrappedGlobal(JSContext* aJSContext, 545 nsISupports* aCOMObj, 546 nsIPrincipal* aPrincipal, 547 uint32_t aFlags, 548 JS::RealmOptions& aOptions, 549 MutableHandleObject aNewGlobal) { 550 MOZ_ASSERT(aJSContext, "bad param"); 551 MOZ_ASSERT(aCOMObj, "bad param"); 552 553 // We pass null for the 'extra' pointer during global object creation, so 554 // we need to have a principal. 555 MOZ_ASSERT(aPrincipal); 556 // All uses (at time of writing) were System Principal, meaning 557 // aShouldResistFingerprinting can be hardcoded to false. 558 // If this changes, ShouldRFP needs to be updated accordingly. 559 MOZ_RELEASE_ASSERT(aPrincipal->IsSystemPrincipal()); 560 561 // Similarly we can thus hardcode the RTPCallerType. 562 aOptions.behaviors().setReduceTimerPrecisionCallerType( 563 RTPCallerTypeToToken(RTPCallerType::SystemPrincipal)); 564 565 InitGlobalObjectOptions(aOptions, /* aSystemPrincipal */ true, 566 /* aSecureContext */ true, 567 /* aForceUTC */ false, /* aAlwaysUseFdlibm */ false, 568 /* aLocaleEnUS */ false, 569 /* aLanguageOverride */ ""_ns, 570 /* aTimezoneOverride */ u""_ns); 571 572 // Call into XPCWrappedNative to make a new global object, scope, and global 573 // prototype. 574 xpcObjectHelper helper(aCOMObj); 575 MOZ_ASSERT(helper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT); 576 RefPtr<XPCWrappedNative> wrappedGlobal; 577 nsresult rv = XPCWrappedNative::WrapNewGlobal( 578 aJSContext, helper, aPrincipal, aOptions, getter_AddRefs(wrappedGlobal)); 579 NS_ENSURE_SUCCESS(rv, rv); 580 581 // Grab a copy of the global and enter its compartment. 582 RootedObject global(aJSContext, wrappedGlobal->GetFlatJSObject()); 583 MOZ_ASSERT(JS_IsGlobalObject(global)); 584 585 if (!InitGlobalObject(aJSContext, global, aFlags)) { 586 return UnexpectedFailure(NS_ERROR_FAILURE); 587 } 588 589 { // Scope for JSAutoRealm 590 JSAutoRealm ar(aJSContext, global); 591 if (!JS_DefineProfilingFunctions(aJSContext, global)) { 592 return UnexpectedFailure(NS_ERROR_OUT_OF_MEMORY); 593 } 594 if (aPrincipal->IsSystemPrincipal()) { 595 if (!glean::Glean::DefineGlean(aJSContext, global) || 596 !glean::GleanPings::DefineGleanPings(aJSContext, global)) { 597 return UnexpectedFailure(NS_ERROR_FAILURE); 598 } 599 } 600 } 601 602 aNewGlobal.set(global); 603 return NS_OK; 604 } 605 606 nsCString GetFunctionName(JSContext* cx, HandleObject obj) { 607 RootedObject inner(cx, js::UncheckedUnwrap(obj)); 608 JSAutoRealm ar(cx, inner); 609 610 RootedFunction fun(cx, JS_GetObjectFunction(inner)); 611 if (!fun) { 612 // If the object isn't a function, it's likely that it has a single 613 // function property (for things like nsITimerCallback). In this case, 614 // return the name of that function property. 615 616 Rooted<IdVector> idArray(cx, IdVector(cx)); 617 if (!JS_Enumerate(cx, inner, &idArray)) { 618 JS_ClearPendingException(cx); 619 return nsCString("error"); 620 } 621 622 if (idArray.length() != 1) { 623 return nsCString("nonfunction"); 624 } 625 626 RootedId id(cx, idArray[0]); 627 RootedValue v(cx); 628 if (!JS_GetPropertyById(cx, inner, id, &v)) { 629 JS_ClearPendingException(cx); 630 return nsCString("nonfunction"); 631 } 632 633 if (!v.isObject()) { 634 return nsCString("nonfunction"); 635 } 636 637 RootedObject vobj(cx, &v.toObject()); 638 return GetFunctionName(cx, vobj); 639 } 640 641 RootedString funName(cx, JS_GetMaybePartialFunctionDisplayId(fun)); 642 RootedScript script(cx, JS_GetFunctionScript(cx, fun)); 643 const char* filename = script ? JS_GetScriptFilename(script) : "anonymous"; 644 const char* filenameSuffix = strrchr(filename, '/'); 645 646 if (filenameSuffix) { 647 filenameSuffix++; 648 } else { 649 filenameSuffix = filename; 650 } 651 652 nsCString displayName("anonymous"); 653 if (funName) { 654 RootedValue funNameVal(cx, StringValue(funName)); 655 if (!XPCConvert::JSData2Native(cx, &displayName, funNameVal, 656 {nsXPTType::T_UTF8STRING}, nullptr, 0, 657 nullptr)) { 658 JS_ClearPendingException(cx); 659 return nsCString("anonymous"); 660 } 661 } 662 663 displayName.Append('['); 664 displayName.Append(filenameSuffix, strlen(filenameSuffix)); 665 displayName.Append(']'); 666 return displayName; 667 } 668 669 } // namespace xpc 670 671 static nsresult NativeInterface2JSObject(JSContext* aCx, HandleObject aScope, 672 nsISupports* aCOMObj, 673 nsWrapperCache* aCache, 674 const nsIID* aIID, bool aAllowWrapping, 675 MutableHandleValue aVal) { 676 JSAutoRealm ar(aCx, aScope); 677 678 nsresult rv; 679 xpcObjectHelper helper(aCOMObj, aCache); 680 if (!XPCConvert::NativeInterface2JSObject(aCx, aVal, helper, aIID, 681 aAllowWrapping, &rv)) { 682 return rv; 683 } 684 685 MOZ_ASSERT( 686 aAllowWrapping || !xpc::WrapperFactory::IsXrayWrapper(&aVal.toObject()), 687 "Shouldn't be returning a xray wrapper here"); 688 689 return NS_OK; 690 } 691 692 nsresult nsIXPConnect::WrapNative(JSContext* aJSContext, JSObject* aScopeArg, 693 nsISupports* aCOMObj, const nsIID& aIID, 694 JSObject** aRetVal) { 695 MOZ_ASSERT(aJSContext, "bad param"); 696 MOZ_ASSERT(aScopeArg, "bad param"); 697 MOZ_ASSERT(aCOMObj, "bad param"); 698 699 RootedObject aScope(aJSContext, aScopeArg); 700 RootedValue v(aJSContext); 701 nsresult rv = NativeInterface2JSObject(aJSContext, aScope, aCOMObj, nullptr, 702 &aIID, true, &v); 703 if (NS_FAILED(rv)) { 704 return rv; 705 } 706 707 if (!v.isObjectOrNull()) { 708 return NS_ERROR_FAILURE; 709 } 710 711 *aRetVal = v.toObjectOrNull(); 712 return NS_OK; 713 } 714 715 nsresult nsIXPConnect::WrapNativeToJSVal(JSContext* aJSContext, 716 JSObject* aScopeArg, 717 nsISupports* aCOMObj, 718 nsWrapperCache* aCache, 719 const nsIID* aIID, bool aAllowWrapping, 720 MutableHandleValue aVal) { 721 MOZ_ASSERT(aJSContext, "bad param"); 722 MOZ_ASSERT(aScopeArg, "bad param"); 723 MOZ_ASSERT(aCOMObj, "bad param"); 724 725 RootedObject aScope(aJSContext, aScopeArg); 726 return NativeInterface2JSObject(aJSContext, aScope, aCOMObj, aCache, aIID, 727 aAllowWrapping, aVal); 728 } 729 730 nsresult nsIXPConnect::WrapJS(JSContext* aJSContext, JSObject* aJSObjArg, 731 const nsIID& aIID, void** result) { 732 MOZ_ASSERT(aJSContext, "bad param"); 733 MOZ_ASSERT(aJSObjArg, "bad param"); 734 MOZ_ASSERT(result, "bad param"); 735 736 *result = nullptr; 737 738 RootedObject aJSObj(aJSContext, aJSObjArg); 739 740 nsresult rv = NS_ERROR_UNEXPECTED; 741 if (!XPCConvert::JSObject2NativeInterface(aJSContext, result, aJSObj, &aIID, 742 nullptr, &rv)) 743 return rv; 744 return NS_OK; 745 } 746 747 nsresult nsIXPConnect::JSValToVariant(JSContext* cx, HandleValue aJSVal, 748 nsIVariant** aResult) { 749 MOZ_ASSERT(aResult, "bad param"); 750 751 RefPtr<XPCVariant> variant = XPCVariant::newVariant(cx, aJSVal); 752 variant.forget(aResult); 753 NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); 754 755 return NS_OK; 756 } 757 758 nsresult nsIXPConnect::WrapJSAggregatedToNative(nsISupports* aOuter, 759 JSContext* aJSContext, 760 JSObject* aJSObjArg, 761 const nsIID& aIID, 762 void** result) { 763 MOZ_ASSERT(aOuter, "bad param"); 764 MOZ_ASSERT(aJSContext, "bad param"); 765 MOZ_ASSERT(aJSObjArg, "bad param"); 766 MOZ_ASSERT(result, "bad param"); 767 768 *result = nullptr; 769 770 RootedObject aJSObj(aJSContext, aJSObjArg); 771 nsresult rv; 772 if (!XPCConvert::JSObject2NativeInterface(aJSContext, result, aJSObj, &aIID, 773 aOuter, &rv)) 774 return rv; 775 return NS_OK; 776 } 777 778 nsresult nsIXPConnect::GetWrappedNativeOfJSObject( 779 JSContext* aJSContext, JSObject* aJSObjArg, 780 nsIXPConnectWrappedNative** _retval) { 781 MOZ_ASSERT(aJSContext, "bad param"); 782 MOZ_ASSERT(aJSObjArg, "bad param"); 783 MOZ_ASSERT(_retval, "bad param"); 784 785 RootedObject aJSObj(aJSContext, aJSObjArg); 786 aJSObj = js::CheckedUnwrapDynamic(aJSObj, aJSContext, 787 /* stopAtWindowProxy = */ false); 788 if (!aJSObj || !IsWrappedNativeReflector(aJSObj)) { 789 *_retval = nullptr; 790 return NS_ERROR_FAILURE; 791 } 792 793 RefPtr<XPCWrappedNative> temp = XPCWrappedNative::Get(aJSObj); 794 temp.forget(_retval); 795 return NS_OK; 796 } 797 798 static already_AddRefed<nsISupports> ReflectorToISupports(JSObject* reflector) { 799 if (!reflector) { 800 return nullptr; 801 } 802 803 // Try XPCWrappedNatives. 804 if (IsWrappedNativeReflector(reflector)) { 805 XPCWrappedNative* wn = XPCWrappedNative::Get(reflector); 806 if (!wn) { 807 return nullptr; 808 } 809 nsCOMPtr<nsISupports> native = wn->Native(); 810 return native.forget(); 811 } 812 813 // Try DOM objects. This QI without taking a ref first is safe, because 814 // this if non-null our thing will definitely be a DOM object, and we know 815 // their QI to nsISupports doesn't do anything weird. 816 nsCOMPtr<nsISupports> canonical = 817 do_QueryInterface(mozilla::dom::UnwrapDOMObjectToISupports(reflector)); 818 return canonical.forget(); 819 } 820 821 already_AddRefed<nsISupports> xpc::ReflectorToISupportsStatic( 822 JSObject* reflector) { 823 // Unwrap security wrappers, if allowed. 824 return ReflectorToISupports(js::CheckedUnwrapStatic(reflector)); 825 } 826 827 already_AddRefed<nsISupports> xpc::ReflectorToISupportsDynamic( 828 JSObject* reflector, JSContext* cx) { 829 // Unwrap security wrappers, if allowed. 830 return ReflectorToISupports( 831 js::CheckedUnwrapDynamic(reflector, cx, 832 /* stopAtWindowProxy = */ false)); 833 } 834 835 nsresult nsIXPConnect::CreateSandbox(JSContext* cx, nsIPrincipal* principal, 836 JSObject** _retval) { 837 *_retval = nullptr; 838 839 RootedValue rval(cx); 840 SandboxOptions options; 841 nsresult rv = CreateSandboxObject(cx, &rval, principal, options); 842 MOZ_ASSERT(NS_FAILED(rv) || !rval.isPrimitive(), 843 "Bad return value from xpc_CreateSandboxObject()!"); 844 845 if (NS_SUCCEEDED(rv) && !rval.isPrimitive()) { 846 *_retval = rval.toObjectOrNull(); 847 } 848 849 return rv; 850 } 851 852 nsresult nsIXPConnect::EvalInSandboxObject(const nsAString& source, 853 const char* filename, JSContext* cx, 854 JSObject* sandboxArg, 855 MutableHandleValue rval) { 856 if (!sandboxArg) { 857 return NS_ERROR_INVALID_ARG; 858 } 859 860 RootedObject sandbox(cx, sandboxArg); 861 nsCString filenameStr; 862 if (filename) { 863 filenameStr.Assign(filename); 864 } else { 865 filenameStr = "x-bogus://XPConnect/Sandbox"_ns; 866 } 867 return EvalInSandbox(cx, sandbox, source, filenameStr, 1, 868 /* enforceFilenameRestrictions */ true, rval); 869 } 870 871 nsresult nsIXPConnect::DebugDump(int16_t depth) { 872 #ifdef DEBUG 873 auto* self = static_cast<nsXPConnect*>(this); 874 875 depth--; 876 XPC_LOG_ALWAYS( 877 ("nsXPConnect @ %p with mRefCnt = %" PRIuPTR, self, self->mRefCnt.get())); 878 XPC_LOG_INDENT(); 879 XPC_LOG_ALWAYS(("gSelf @ %p", self->gSelf)); 880 XPC_LOG_ALWAYS(("gOnceAliveNowDead is %d", (int)self->gOnceAliveNowDead)); 881 XPCWrappedNativeScope::DebugDumpAllScopes(depth); 882 XPC_LOG_OUTDENT(); 883 #endif 884 return NS_OK; 885 } 886 887 nsresult nsIXPConnect::DebugDumpObject(nsISupports* aCOMObj, int16_t depth) { 888 #ifdef DEBUG 889 if (!depth) { 890 return NS_OK; 891 } 892 if (!aCOMObj) { 893 XPC_LOG_ALWAYS(("*** Cound not dump object with NULL address")); 894 return NS_OK; 895 } 896 897 nsCOMPtr<nsIXPConnect> xpc; 898 nsCOMPtr<nsIXPConnectWrappedNative> wn; 899 nsCOMPtr<nsIXPConnectWrappedJS> wjs; 900 901 if (NS_SUCCEEDED(aCOMObj->QueryInterface(NS_GET_IID(nsIXPConnect), 902 getter_AddRefs(xpc)))) { 903 XPC_LOG_ALWAYS(("Dumping a nsIXPConnect...")); 904 xpc->DebugDump(depth); 905 } else if (NS_SUCCEEDED(aCOMObj->QueryInterface( 906 NS_GET_IID(nsIXPConnectWrappedNative), getter_AddRefs(wn)))) { 907 XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedNative...")); 908 wn->DebugDump(depth); 909 } else if (NS_SUCCEEDED(aCOMObj->QueryInterface( 910 NS_GET_IID(nsIXPConnectWrappedJS), getter_AddRefs(wjs)))) { 911 XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedJS...")); 912 wjs->DebugDump(depth); 913 } else { 914 XPC_LOG_ALWAYS(("*** Could not dump the nsISupports @ %p", aCOMObj)); 915 } 916 #endif 917 return NS_OK; 918 } 919 920 nsresult nsIXPConnect::DebugDumpJSStack(bool showArgs, bool showLocals, 921 bool showThisProps) { 922 xpc_DumpJSStack(showArgs, showLocals, showThisProps); 923 924 return NS_OK; 925 } 926 927 nsresult nsIXPConnect::VariantToJS(JSContext* ctx, JSObject* scopeArg, 928 nsIVariant* value, 929 MutableHandleValue _retval) { 930 MOZ_ASSERT(ctx, "bad param"); 931 MOZ_ASSERT(scopeArg, "bad param"); 932 MOZ_ASSERT(value, "bad param"); 933 934 RootedObject scope(ctx, scopeArg); 935 MOZ_ASSERT(js::IsObjectInContextCompartment(scope, ctx)); 936 937 nsresult rv = NS_OK; 938 if (!XPCVariant::VariantDataToJS(ctx, value, &rv, _retval)) { 939 if (NS_FAILED(rv)) { 940 return rv; 941 } 942 943 return NS_ERROR_FAILURE; 944 } 945 return NS_OK; 946 } 947 948 nsresult nsIXPConnect::JSToVariant(JSContext* ctx, HandleValue value, 949 nsIVariant** _retval) { 950 MOZ_ASSERT(ctx, "bad param"); 951 MOZ_ASSERT(_retval, "bad param"); 952 953 RefPtr<XPCVariant> variant = XPCVariant::newVariant(ctx, value); 954 variant.forget(_retval); 955 if (!(*_retval)) { 956 return NS_ERROR_FAILURE; 957 } 958 959 return NS_OK; 960 } 961 962 namespace xpc { 963 964 bool Base64Encode(JSContext* cx, HandleValue val, MutableHandleValue out) { 965 MOZ_ASSERT(cx); 966 967 nsAutoCString encodedString; 968 BindingCallContext callCx(cx, "Base64Encode"); 969 if (!ConvertJSValueToByteString(callCx, val, false, "value", encodedString)) { 970 return false; 971 } 972 973 nsAutoCString result; 974 if (NS_FAILED(mozilla::Base64Encode(encodedString, result))) { 975 JS_ReportErrorASCII(cx, "Failed to encode base64 data!"); 976 return false; 977 } 978 979 JSString* str = JS_NewStringCopyN(cx, result.get(), result.Length()); 980 if (!str) { 981 return false; 982 } 983 984 out.setString(str); 985 return true; 986 } 987 988 bool Base64Decode(JSContext* cx, HandleValue val, MutableHandleValue out) { 989 MOZ_ASSERT(cx); 990 991 nsAutoCString encodedString; 992 BindingCallContext callCx(cx, "Base64Decode"); 993 if (!ConvertJSValueToByteString(callCx, val, false, "value", encodedString)) { 994 return false; 995 } 996 997 nsAutoCString result; 998 if (NS_FAILED(mozilla::Base64Decode(encodedString, result))) { 999 JS_ReportErrorASCII(cx, "Failed to decode base64 string!"); 1000 return false; 1001 } 1002 1003 JSString* str = JS_NewStringCopyN(cx, result.get(), result.Length()); 1004 if (!str) { 1005 return false; 1006 } 1007 1008 out.setString(str); 1009 return true; 1010 } 1011 1012 void SetLocationForGlobal(JSObject* global, const nsACString& location) { 1013 MOZ_ASSERT(global); 1014 RealmPrivate::Get(global)->SetLocation(location); 1015 } 1016 1017 void SetLocationForGlobal(JSObject* global, nsIURI* locationURI) { 1018 MOZ_ASSERT(global); 1019 RealmPrivate::Get(global)->SetLocationURI(locationURI); 1020 } 1021 1022 } // namespace xpc 1023 1024 // static 1025 nsIXPConnect* nsIXPConnect::XPConnect() { 1026 // Do a release-mode assert that we're not doing anything significant in 1027 // XPConnect off the main thread. If you're an extension developer hitting 1028 // this, you need to change your code. See bug 716167. 1029 if (!MOZ_LIKELY(NS_IsMainThread())) { 1030 MOZ_CRASH(); 1031 } 1032 1033 return nsXPConnect::gSelf; 1034 } 1035 1036 /* These are here to be callable from a debugger */ 1037 extern "C" { 1038 1039 MOZ_EXPORT void DumpJSStack() { xpc_DumpJSStack(true, true, false); } 1040 1041 MOZ_EXPORT void DumpCompleteHeap() { 1042 nsCOMPtr<nsICycleCollectorListener> listener = 1043 nsCycleCollector_createLogger(); 1044 MOZ_ASSERT(listener); 1045 1046 nsCOMPtr<nsICycleCollectorListener> alltracesListener; 1047 listener->AllTraces(getter_AddRefs(alltracesListener)); 1048 if (!alltracesListener) { 1049 NS_WARNING("Failed to get all traces logger"); 1050 return; 1051 } 1052 1053 nsJSContext::CycleCollectNow(CCReason::DUMP_HEAP, alltracesListener); 1054 } 1055 1056 } // extern "C" 1057 1058 namespace xpc { 1059 1060 bool Atob(JSContext* cx, unsigned argc, Value* vp) { 1061 CallArgs args = CallArgsFromVp(argc, vp); 1062 if (!args.length()) { 1063 return true; 1064 } 1065 1066 return xpc::Base64Decode(cx, args[0], args.rval()); 1067 } 1068 1069 bool Btoa(JSContext* cx, unsigned argc, Value* vp) { 1070 CallArgs args = CallArgsFromVp(argc, vp); 1071 if (!args.length()) { 1072 return true; 1073 } 1074 1075 return xpc::Base64Encode(cx, args[0], args.rval()); 1076 } 1077 1078 bool IsXrayWrapper(JSObject* obj) { return WrapperFactory::IsXrayWrapper(obj); } 1079 1080 } // namespace xpc 1081 1082 namespace mozilla { 1083 namespace dom { 1084 1085 bool IsChromeOrUAWidget(JSContext* cx, JSObject* /* unused */) { 1086 MOZ_ASSERT(NS_IsMainThread()); 1087 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx); 1088 MOZ_ASSERT(realm); 1089 JS::Compartment* c = JS::GetCompartmentForRealm(realm); 1090 1091 return AccessCheck::isChrome(c) || IsUAWidgetCompartment(c); 1092 } 1093 1094 bool IsNotUAWidget(JSContext* cx, JSObject* /* unused */) { 1095 MOZ_ASSERT(NS_IsMainThread()); 1096 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx); 1097 MOZ_ASSERT(realm); 1098 JS::Compartment* c = JS::GetCompartmentForRealm(realm); 1099 1100 return !IsUAWidgetCompartment(c); 1101 } 1102 1103 extern bool IsCurrentThreadRunningChromeWorker(); 1104 1105 bool ThreadSafeIsChromeOrUAWidget(JSContext* cx, JSObject* obj) { 1106 if (NS_IsMainThread()) { 1107 return IsChromeOrUAWidget(cx, obj); 1108 } 1109 return IsCurrentThreadRunningChromeWorker(); 1110 } 1111 1112 } // namespace dom 1113 } // namespace mozilla 1114 1115 #ifdef MOZ_TSAN 1116 ReadOnlyPage ReadOnlyPage::sInstance; 1117 #else 1118 constexpr const volatile ReadOnlyPage ReadOnlyPage::sInstance; 1119 #endif 1120 1121 void xpc::ReadOnlyPage::Write(const volatile bool* aPtr, bool aValue) { 1122 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 1123 if (*aPtr == aValue) return; 1124 // Please modify the definition of kAutomationPageSize if a new platform 1125 // is running in automation and hits this assertion. 1126 MOZ_RELEASE_ASSERT(PR_GetPageSize() == alignof(ReadOnlyPage)); 1127 MOZ_RELEASE_ASSERT( 1128 reinterpret_cast<uintptr_t>(&sInstance) % alignof(ReadOnlyPage) == 0); 1129 #ifdef XP_WIN 1130 AutoVirtualProtect prot(const_cast<ReadOnlyPage*>(&sInstance), 1131 alignof(ReadOnlyPage), PAGE_READWRITE); 1132 MOZ_RELEASE_ASSERT(prot && (prot.PrevProt() & 0xFF) == PAGE_READONLY); 1133 #else 1134 int ret = mprotect(const_cast<ReadOnlyPage*>(&sInstance), 1135 alignof(ReadOnlyPage), PROT_READ | PROT_WRITE); 1136 MOZ_RELEASE_ASSERT(ret == 0); 1137 #endif 1138 MOZ_RELEASE_ASSERT(aPtr == &sInstance.mNonLocalConnectionsDisabled || 1139 aPtr == &sInstance.mTurnOffAllSecurityPref); 1140 #ifdef XP_WIN 1141 BOOL ret = WriteProcessMemory(GetCurrentProcess(), const_cast<bool*>(aPtr), 1142 &aValue, sizeof(bool), nullptr); 1143 MOZ_RELEASE_ASSERT(ret); 1144 #else 1145 *const_cast<volatile bool*>(aPtr) = aValue; 1146 ret = mprotect(const_cast<ReadOnlyPage*>(&sInstance), alignof(ReadOnlyPage), 1147 PROT_READ); 1148 MOZ_RELEASE_ASSERT(ret == 0); 1149 #endif 1150 } 1151 1152 void xpc::ReadOnlyPage::Init() { 1153 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 1154 static_assert(alignof(ReadOnlyPage) == kAutomationPageSize); 1155 static_assert(sizeof(sInstance) == alignof(ReadOnlyPage)); 1156 1157 // Make sure that initialization is not too late. 1158 MOZ_DIAGNOSTIC_ASSERT(!net::gIOService); 1159 char* s = getenv("MOZ_DISABLE_NONLOCAL_CONNECTIONS"); 1160 const bool disabled = s && *s != '0'; 1161 Write(&sInstance.mNonLocalConnectionsDisabled, disabled); 1162 if (!disabled) { 1163 // not bothered to check automation prefs. 1164 return; 1165 } 1166 1167 // The obvious thing is to make this pref a static pref. But then it would 1168 // always be defined and always show up in about:config, and users could flip 1169 // it, which we don't want. Instead we roll our own callback so that if the 1170 // pref is undefined (the normal case) then sAutomationPrefIsSet is false and 1171 // nothing shows up in about:config. 1172 nsresult rv = Preferences::RegisterCallbackAndCall( 1173 [](const char* aPrefName, void* /* aClosure */) { 1174 Write(&sInstance.mTurnOffAllSecurityPref, 1175 Preferences::GetBool(aPrefName, /* aFallback */ false)); 1176 }, 1177 "security." 1178 "turn_off_all_security_so_that_viruses_can_take_over_this_computer"); 1179 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); 1180 }