nsFrameMessageManager.cpp (54913B)
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 "nsFrameMessageManager.h" 8 9 #include <algorithm> 10 #include <cmath> 11 #include <cstddef> 12 #include <cstdint> 13 #include <new> 14 #include <utility> 15 16 #include "ContentChild.h" 17 #include "ErrorList.h" 18 #include "base/process_util.h" 19 #include "chrome/common/ipc_channel.h" 20 #include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue 21 #include "js/CompilationAndEvaluation.h" 22 #include "js/CompileOptions.h" 23 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 24 #include "js/GCVector.h" 25 #include "js/JSON.h" 26 #include "js/PropertyAndElement.h" // JS_GetProperty 27 #include "js/RootingAPI.h" 28 #include "js/SourceText.h" 29 #include "js/StructuredClone.h" 30 #include "js/TypeDecls.h" 31 #include "js/Utility.h" // JS::FreePolicy 32 #include "js/Wrapper.h" 33 #include "js/experimental/JSStencil.h" 34 #include "jsapi.h" 35 #include "jsfriendapi.h" 36 #include "mozilla/AlreadyAddRefed.h" 37 #include "mozilla/Assertions.h" 38 #include "mozilla/ClearOnShutdown.h" 39 #include "mozilla/ErrorResult.h" 40 #include "mozilla/OwningNonNull.h" 41 #include "mozilla/ProfilerLabels.h" 42 #include "mozilla/RefPtr.h" 43 #include "mozilla/ScriptPreloader.h" 44 #include "mozilla/Services.h" 45 #include "mozilla/StaticPtr.h" 46 #include "mozilla/TimeStamp.h" 47 #include "mozilla/UniquePtr.h" 48 #include "mozilla/dom/AutoEntryScript.h" 49 #include "mozilla/dom/BindingDeclarations.h" 50 #include "mozilla/dom/CallbackObject.h" 51 #include "mozilla/dom/ChildProcessMessageManager.h" 52 #include "mozilla/dom/ChromeMessageBroadcaster.h" 53 #include "mozilla/dom/ContentProcessMessageManager.h" 54 #include "mozilla/dom/DOMTypes.h" 55 #include "mozilla/dom/MessageBroadcaster.h" 56 #include "mozilla/dom/MessageListenerManager.h" 57 #include "mozilla/dom/MessageManagerBinding.h" 58 #include "mozilla/dom/MessageManagerCallback.h" 59 #include "mozilla/dom/MessagePort.h" 60 #include "mozilla/dom/ParentProcessMessageManager.h" 61 #include "mozilla/dom/ProcessMessageManager.h" 62 #include "mozilla/dom/RootedDictionary.h" 63 #include "mozilla/dom/SameProcessMessageQueue.h" 64 #include "mozilla/dom/ScriptLoader.h" 65 #include "mozilla/dom/ScriptSettings.h" 66 #include "mozilla/dom/ToJSValue.h" 67 #include "mozilla/dom/ipc/SharedMap.h" 68 #include "mozilla/dom/ipc/StructuredCloneData.h" 69 #include "mozilla/scache/StartupCacheUtils.h" 70 #include "nsASCIIMask.h" 71 #include "nsBaseHashtable.h" 72 #include "nsCOMPtr.h" 73 #include "nsClassHashtable.h" 74 #include "nsComponentManagerUtils.h" 75 #include "nsContentUtils.h" 76 #include "nsCycleCollectionNoteChild.h" 77 #include "nsCycleCollectionParticipant.h" 78 #include "nsDebug.h" 79 #include "nsError.h" 80 #include "nsHashKeys.h" 81 #include "nsIChannel.h" 82 #include "nsIConsoleService.h" 83 #include "nsIContentPolicy.h" 84 #include "nsIInputStream.h" 85 #include "nsILoadInfo.h" 86 #include "nsIMemoryReporter.h" 87 #include "nsIMessageManager.h" 88 #include "nsIObserver.h" 89 #include "nsIObserverService.h" 90 #include "nsIProtocolHandler.h" 91 #include "nsIScriptError.h" 92 #include "nsISupports.h" 93 #include "nsISupportsUtils.h" 94 #include "nsIURI.h" 95 #include "nsIWeakReferenceUtils.h" 96 #include "nsIXPConnect.h" 97 #include "nsJSUtils.h" 98 #include "nsLiteralString.h" 99 #include "nsNetUtil.h" 100 #include "nsPrintfCString.h" 101 #include "nsQueryObject.h" 102 #include "nsServiceManagerUtils.h" 103 #include "nsString.h" 104 #include "nsStringFlags.h" 105 #include "nsStringFwd.h" 106 #include "nsTArray.h" 107 #include "nsTHashMap.h" 108 #include "nsTLiteralString.h" 109 #include "nsTObserverArray.h" 110 #include "nsTPromiseFlatString.h" 111 #include "nsTStringRepr.h" 112 #include "nsThreadUtils.h" 113 #include "nsXULAppAPI.h" 114 #include "nscore.h" 115 #include "xpcpublic.h" 116 117 #ifdef XP_WIN 118 # if defined(SendMessage) 119 # undef SendMessage 120 # endif 121 #endif 122 123 #ifdef FUZZING 124 # include "MessageManagerFuzzer.h" 125 #endif 126 127 using namespace mozilla; 128 using namespace mozilla::dom; 129 using namespace mozilla::dom::ipc; 130 131 struct FrameMessageMarker { 132 static constexpr Span<const char> MarkerTypeName() { 133 return MakeStringSpan("FrameMessage"); 134 } 135 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter, 136 const ProfilerString16View& aMessageName, 137 bool aIsSync) { 138 aWriter.UniqueStringProperty("name", NS_ConvertUTF16toUTF8(aMessageName)); 139 aWriter.BoolProperty("sync", aIsSync); 140 } 141 static MarkerSchema MarkerTypeDisplay() { 142 using MS = MarkerSchema; 143 MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable}; 144 schema.AddKeyLabelFormat("name", "Message Name", MS::Format::UniqueString, 145 MS::PayloadFlags::Searchable); 146 schema.AddKeyLabelFormat("sync", "Sync", MS::Format::String); 147 schema.SetTooltipLabel("FrameMessage - {marker.name}"); 148 schema.SetTableLabel("{marker.data.name}"); 149 return schema; 150 } 151 }; 152 153 #define CACHE_PREFIX(type) "mm/" type 154 155 nsFrameMessageManager::nsFrameMessageManager(MessageManagerCallback* aCallback, 156 MessageManagerFlags aFlags) 157 : mChrome(aFlags & MessageManagerFlags::MM_CHROME), 158 mGlobal(aFlags & MessageManagerFlags::MM_GLOBAL), 159 mIsProcessManager(aFlags & MessageManagerFlags::MM_PROCESSMANAGER), 160 mIsBroadcaster(aFlags & MessageManagerFlags::MM_BROADCASTER), 161 mOwnsCallback(aFlags & MessageManagerFlags::MM_OWNSCALLBACK), 162 mHandlingMessage(false), 163 mClosed(false), 164 mDisconnected(false), 165 mCallback(aCallback) { 166 NS_ASSERTION(!mIsBroadcaster || !mCallback, 167 "Broadcasters cannot have callbacks!"); 168 if (mOwnsCallback) { 169 mOwnedCallback = WrapUnique(aCallback); 170 } 171 } 172 173 nsFrameMessageManager::~nsFrameMessageManager() { 174 for (int32_t i = mChildManagers.Length(); i > 0; --i) { 175 mChildManagers[i - 1]->Disconnect(false); 176 } 177 if (mIsProcessManager) { 178 if (this == sParentProcessManager) { 179 sParentProcessManager = nullptr; 180 } 181 if (this == sChildProcessManager) { 182 sChildProcessManager = nullptr; 183 delete mozilla::dom::SameProcessMessageQueue::Get(); 184 } 185 if (this == sSameProcessParentManager) { 186 sSameProcessParentManager = nullptr; 187 } 188 } 189 } 190 191 inline void ImplCycleCollectionTraverse( 192 nsCycleCollectionTraversalCallback& aCallback, 193 nsMessageListenerInfo& aField, const char* aName, uint32_t aFlags = 0) { 194 ImplCycleCollectionTraverse(aCallback, aField.mStrongListener, aName, aFlags); 195 ImplCycleCollectionTraverse(aCallback, aField.mWeakListener, aName, aFlags); 196 } 197 198 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager) 199 200 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager) 201 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners) 202 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers) 203 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedData) 204 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 205 206 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager) 207 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitialProcessData) 208 NS_IMPL_CYCLE_COLLECTION_TRACE_END 209 210 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager) 211 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners) 212 for (int32_t i = tmp->mChildManagers.Length(); i > 0; --i) { 213 tmp->mChildManagers[i - 1]->Disconnect(false); 214 } 215 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers) 216 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedData) 217 tmp->mInitialProcessData.setNull(); 218 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 219 220 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager) 221 NS_INTERFACE_MAP_ENTRY(nsISupports) 222 223 /* Message managers in child process implement nsIMessageSender. 224 Message managers in the chrome process are 225 either broadcasters (if they have subordinate/child message 226 managers) or they're simple message senders. */ 227 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender, 228 !mChrome || !mIsBroadcaster) 229 230 NS_INTERFACE_MAP_END 231 232 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager) 233 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager) 234 235 void MessageManagerCallback::DoGetRemoteType(nsACString& aRemoteType, 236 ErrorResult& aError) const { 237 aRemoteType.Truncate(); 238 mozilla::dom::ProcessMessageManager* parent = GetProcessMessageManager(); 239 if (!parent) { 240 return; 241 } 242 243 parent->GetRemoteType(aRemoteType, aError); 244 } 245 246 bool MessageManagerCallback::BuildClonedMessageData( 247 StructuredCloneData& aData, ClonedMessageData& aClonedData) { 248 return aData.BuildClonedMessageData(aClonedData); 249 } 250 251 void mozilla::dom::ipc::UnpackClonedMessageData( 252 const ClonedMessageData& aClonedData, StructuredCloneData& aData) { 253 aData.BorrowFromClonedMessageData(aClonedData); 254 } 255 256 void nsFrameMessageManager::AddMessageListener(const nsAString& aMessageName, 257 MessageListener& aListener, 258 bool aListenWhenClosed, 259 ErrorResult& aError) { 260 auto* const listeners = mListeners.GetOrInsertNew(aMessageName); 261 uint32_t len = listeners->Length(); 262 for (uint32_t i = 0; i < len; ++i) { 263 MessageListener* strongListener = listeners->ElementAt(i).mStrongListener; 264 if (strongListener && *strongListener == aListener) { 265 return; 266 } 267 } 268 269 nsMessageListenerInfo* entry = listeners->AppendElement(); 270 entry->mStrongListener = &aListener; 271 entry->mListenWhenClosed = aListenWhenClosed; 272 } 273 274 void nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessageName, 275 MessageListener& aListener, 276 ErrorResult& aError) { 277 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = 278 mListeners.Get(aMessageName); 279 if (listeners) { 280 uint32_t len = listeners->Length(); 281 for (uint32_t i = 0; i < len; ++i) { 282 MessageListener* strongListener = listeners->ElementAt(i).mStrongListener; 283 if (strongListener && *strongListener == aListener) { 284 listeners->RemoveElementAt(i); 285 return; 286 } 287 } 288 } 289 } 290 291 static already_AddRefed<nsISupports> ToXPCOMMessageListener( 292 MessageListener& aListener) { 293 return CallbackObjectHolder<mozilla::dom::MessageListener, nsISupports>( 294 &aListener) 295 .ToXPCOMCallback(); 296 } 297 298 void nsFrameMessageManager::AddWeakMessageListener( 299 const nsAString& aMessageName, MessageListener& aListener, 300 ErrorResult& aError) { 301 nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener)); 302 nsWeakPtr weak = do_GetWeakReference(listener); 303 if (!weak) { 304 aError.Throw(NS_ERROR_NO_INTERFACE); 305 return; 306 } 307 308 #ifdef DEBUG 309 // It's technically possible that one object X could give two different 310 // nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want 311 // this to happen; it will break e.g. RemoveWeakMessageListener. So let's 312 // check that we're not getting ourselves into that situation. 313 nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener); 314 for (const auto& entry : mListeners) { 315 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak(); 316 uint32_t count = listeners->Length(); 317 for (uint32_t i = 0; i < count; i++) { 318 nsWeakPtr weakListener = listeners->ElementAt(i).mWeakListener; 319 if (weakListener) { 320 nsCOMPtr<nsISupports> otherCanonical = do_QueryReferent(weakListener); 321 MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener)); 322 } 323 } 324 } 325 #endif 326 327 auto* const listeners = mListeners.GetOrInsertNew(aMessageName); 328 uint32_t len = listeners->Length(); 329 for (uint32_t i = 0; i < len; ++i) { 330 if (listeners->ElementAt(i).mWeakListener == weak) { 331 return; 332 } 333 } 334 335 nsMessageListenerInfo* entry = listeners->AppendElement(); 336 entry->mWeakListener = weak; 337 entry->mListenWhenClosed = false; 338 } 339 340 void nsFrameMessageManager::RemoveWeakMessageListener( 341 const nsAString& aMessageName, MessageListener& aListener, 342 ErrorResult& aError) { 343 nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener)); 344 nsWeakPtr weak = do_GetWeakReference(listener); 345 if (!weak) { 346 aError.Throw(NS_ERROR_NO_INTERFACE); 347 return; 348 } 349 350 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = 351 mListeners.Get(aMessageName); 352 if (!listeners) { 353 return; 354 } 355 356 uint32_t len = listeners->Length(); 357 for (uint32_t i = 0; i < len; ++i) { 358 if (listeners->ElementAt(i).mWeakListener == weak) { 359 listeners->RemoveElementAt(i); 360 return; 361 } 362 } 363 } 364 365 void nsFrameMessageManager::LoadScript(const nsAString& aURL, 366 bool aAllowDelayedLoad, 367 bool aRunInGlobalScope, 368 ErrorResult& aError) { 369 if (aAllowDelayedLoad) { 370 // Cache for future windows or frames 371 mPendingScripts.AppendElement(aURL); 372 mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope); 373 } 374 375 if (mCallback) { 376 #ifdef DEBUG_smaug 377 printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get()); 378 #endif 379 if (!mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope)) { 380 aError.Throw(NS_ERROR_FAILURE); 381 return; 382 } 383 } 384 385 for (uint32_t i = 0; i < mChildManagers.Length(); ++i) { 386 RefPtr<nsFrameMessageManager> mm = mChildManagers[i]; 387 if (mm) { 388 // Use false here, so that child managers don't cache the script, which 389 // is already cached in the parent. 390 mm->LoadScript(aURL, false, aRunInGlobalScope, IgnoreErrors()); 391 } 392 } 393 } 394 395 void nsFrameMessageManager::RemoveDelayedScript(const nsAString& aURL) { 396 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { 397 if (mPendingScripts[i] == aURL) { 398 mPendingScripts.RemoveElementAt(i); 399 mPendingScriptsGlobalStates.RemoveElementAt(i); 400 break; 401 } 402 } 403 } 404 405 void nsFrameMessageManager::GetDelayedScripts( 406 JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList, ErrorResult& aError) { 407 // Frame message managers may return an incomplete list because scripts 408 // that were loaded after it was connected are not added to the list. 409 if (!IsGlobal() && !IsBroadcaster()) { 410 NS_WARNING( 411 "Cannot retrieve list of pending frame scripts for frame" 412 "message managers as it may be incomplete"); 413 aError.Throw(NS_ERROR_NOT_IMPLEMENTED); 414 return; 415 } 416 417 aError.MightThrowJSException(); 418 419 aList.SetCapacity(mPendingScripts.Length()); 420 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { 421 JS::Rooted<JS::Value> url(aCx); 422 if (!ToJSValue(aCx, mPendingScripts[i], &url)) { 423 aError.NoteJSContextException(aCx); 424 return; 425 } 426 427 nsTArray<JS::Value>* array = aList.AppendElement(2); 428 array->AppendElement(url); 429 array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i])); 430 } 431 } 432 433 /* static */ 434 bool nsFrameMessageManager::GetParamsForMessage(JSContext* aCx, 435 const JS::Value& aValue, 436 const JS::Value& aTransfer, 437 StructuredCloneData& aData) { 438 // First try to use structured clone on the whole thing. 439 JS::Rooted<JS::Value> v(aCx, aValue); 440 JS::Rooted<JS::Value> t(aCx, aTransfer); 441 ErrorResult rv; 442 aData.Write(aCx, v, t, JS::CloneDataPolicy(), rv); 443 if (!rv.Failed()) { 444 return true; 445 } 446 447 rv.SuppressException(); 448 JS_ClearPendingException(aCx); 449 450 nsCOMPtr<nsIConsoleService> console( 451 do_GetService(NS_CONSOLESERVICE_CONTRACTID)); 452 if (console) { 453 auto location = JSCallingLocation::Get(aCx); 454 nsCOMPtr<nsIScriptError> error( 455 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); 456 error->Init( 457 u"Sending message that cannot be cloned. Are " 458 "you trying to send an XPCOM object?"_ns, 459 location.FileName(), location.mLine, location.mColumn, 460 nsIScriptError::warningFlag, "chrome javascript"_ns, 461 false /* from private window */, true /* from chrome context */); 462 console->LogMessage(error); 463 } 464 465 // Not clonable, try JSON 466 // Bug 1749037 - This is ugly but currently structured cloning doesn't handle 467 // properly cases when interface is implemented in JS and used 468 // as a dictionary. 469 nsAutoString json; 470 if (!nsContentUtils::StringifyJSON(aCx, v, json, 471 UndefinedIsNullStringLiteral)) { 472 NS_WARNING("nsContentUtils::StringifyJSON() failed"); 473 JS_ClearPendingException(aCx); 474 return false; 475 } 476 NS_ENSURE_TRUE(!json.IsEmpty(), false); 477 478 JS::Rooted<JS::Value> val(aCx, JS::NullValue()); 479 if (!JS_ParseJSON(aCx, static_cast<const char16_t*>(json.get()), 480 json.Length(), &val)) { 481 NS_WARNING("JS_ParseJSON"); 482 JS_ClearPendingException(aCx); 483 return false; 484 } 485 486 aData.Write(aCx, val, rv); 487 if (NS_WARN_IF(rv.Failed())) { 488 rv.SuppressException(); 489 return false; 490 } 491 492 return true; 493 } 494 495 static bool sSendingSyncMessage = false; 496 497 void nsFrameMessageManager::SendSyncMessage(JSContext* aCx, 498 const nsAString& aMessageName, 499 JS::Handle<JS::Value> aObj, 500 nsTArray<JS::Value>& aResult, 501 ErrorResult& aError) { 502 NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome"); 503 NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome"); 504 NS_ASSERTION(!GetParentManager(), 505 "Should not have parent manager in content!"); 506 507 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING( 508 "nsFrameMessageManager::SendSyncMessage", OTHER, aMessageName); 509 profiler_add_marker("SendSyncMessage", geckoprofiler::category::IPC, {}, 510 FrameMessageMarker{}, aMessageName, true); 511 512 if (sSendingSyncMessage) { 513 // No kind of blocking send should be issued on top of a sync message. 514 aError.Throw(NS_ERROR_UNEXPECTED); 515 return; 516 } 517 518 StructuredCloneData data; 519 if (!aObj.isUndefined() && 520 !GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) { 521 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); 522 return; 523 } 524 525 #ifdef FUZZING 526 if (data.DataLength() > 0) { 527 MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, 528 JS::UndefinedHandleValue); 529 } 530 #endif 531 532 if (!mCallback) { 533 aError.Throw(NS_ERROR_NOT_INITIALIZED); 534 return; 535 } 536 537 nsTArray<UniquePtr<StructuredCloneData>> retval; 538 539 sSendingSyncMessage = true; 540 bool ok = mCallback->DoSendBlockingMessage(aMessageName, data, &retval); 541 sSendingSyncMessage = false; 542 543 if (!ok) { 544 return; 545 } 546 547 uint32_t len = retval.Length(); 548 aResult.SetCapacity(len); 549 for (uint32_t i = 0; i < len; ++i) { 550 JS::Rooted<JS::Value> ret(aCx); 551 retval[i]->Read(aCx, &ret, aError); 552 if (aError.Failed()) { 553 MOZ_ASSERT(false, "Unable to read structured clone in SendMessage"); 554 return; 555 } 556 aResult.AppendElement(ret); 557 } 558 } 559 560 nsresult nsFrameMessageManager::DispatchAsyncMessageInternal( 561 JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData) { 562 if (mIsBroadcaster) { 563 uint32_t len = mChildManagers.Length(); 564 for (uint32_t i = 0; i < len; ++i) { 565 mChildManagers[i]->DispatchAsyncMessageInternal(aCx, aMessage, aData); 566 } 567 return NS_OK; 568 } 569 570 if (!mCallback) { 571 return NS_ERROR_NOT_INITIALIZED; 572 } 573 574 nsresult rv = mCallback->DoSendAsyncMessage(aMessage, aData); 575 if (NS_FAILED(rv)) { 576 return rv; 577 } 578 return NS_OK; 579 } 580 581 void nsFrameMessageManager::DispatchAsyncMessage( 582 JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj, 583 JS::Handle<JS::Value> aTransfers, ErrorResult& aError) { 584 StructuredCloneData data; 585 if (!aObj.isUndefined() && 586 !GetParamsForMessage(aCx, aObj, aTransfers, data)) { 587 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); 588 return; 589 } 590 591 profiler_add_marker("SendAsyncMessage", geckoprofiler::category::IPC, {}, 592 FrameMessageMarker{}, aMessageName, false); 593 594 #ifdef FUZZING 595 if (data.DataLength()) { 596 MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, aTransfers); 597 } 598 #endif 599 600 aError = DispatchAsyncMessageInternal(aCx, aMessageName, data); 601 } 602 603 class MMListenerRemover { 604 public: 605 explicit MMListenerRemover(nsFrameMessageManager* aMM) 606 : mWasHandlingMessage(aMM->mHandlingMessage), mMM(aMM) { 607 mMM->mHandlingMessage = true; 608 } 609 ~MMListenerRemover() { 610 if (!mWasHandlingMessage) { 611 mMM->mHandlingMessage = false; 612 if (mMM->mDisconnected) { 613 mMM->mListeners.Clear(); 614 } 615 } 616 } 617 618 bool mWasHandlingMessage; 619 RefPtr<nsFrameMessageManager> mMM; 620 }; 621 622 void nsFrameMessageManager::ReceiveMessage( 623 nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed, 624 const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData, 625 nsTArray<UniquePtr<StructuredCloneData>>* aRetVal, ErrorResult& aError) { 626 MOZ_ASSERT(aTarget); 627 profiler_add_marker("ReceiveMessage", geckoprofiler::category::IPC, {}, 628 FrameMessageMarker{}, aMessage, aIsSync); 629 630 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = 631 mListeners.Get(aMessage); 632 if (listeners) { 633 MMListenerRemover lr(this); 634 635 nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator iter( 636 *listeners); 637 while (iter.HasMore()) { 638 nsMessageListenerInfo& listener = iter.GetNext(); 639 // Remove mListeners[i] if it's an expired weak listener. 640 nsCOMPtr<nsISupports> weakListener; 641 if (listener.mWeakListener) { 642 weakListener = do_QueryReferent(listener.mWeakListener); 643 if (!weakListener) { 644 iter.Remove(); 645 continue; 646 } 647 } 648 649 if (!listener.mListenWhenClosed && aTargetClosed) { 650 continue; 651 } 652 653 JS::RootingContext* rcx = RootingCx(); 654 JS::Rooted<JSObject*> object(rcx); 655 JS::Rooted<JSObject*> objectGlobal(rcx); 656 657 RefPtr<MessageListener> webIDLListener; 658 if (!weakListener) { 659 webIDLListener = listener.mStrongListener; 660 object = webIDLListener->CallbackOrNull(); 661 objectGlobal = webIDLListener->CallbackGlobalOrNull(); 662 } else { 663 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = 664 do_QueryInterface(weakListener); 665 if (!wrappedJS) { 666 continue; 667 } 668 669 object = wrappedJS->GetJSObject(); 670 objectGlobal = wrappedJS->GetJSObjectGlobal(); 671 } 672 673 if (!object) { 674 continue; 675 } 676 677 AutoEntryScript aes(js::UncheckedUnwrap(object), 678 "message manager handler"); 679 JSContext* cx = aes.cx(); 680 681 // We passed the unwrapped object to AutoEntryScript so we now need to 682 // enter the realm of the global object that represents the realm of our 683 // callback. 684 JSAutoRealm ar(cx, objectGlobal); 685 686 RootedDictionary<ReceiveMessageArgument> argument(cx); 687 688 JS::Rooted<JS::Value> json(cx, JS::NullValue()); 689 if (aCloneData && aCloneData->DataLength()) { 690 aCloneData->Read(cx, &json, aError); 691 if (NS_WARN_IF(aError.Failed())) { 692 aError.SuppressException(); 693 JS_ClearPendingException(cx); 694 return; 695 } 696 } 697 argument.mData = json; 698 argument.mJson = json; 699 700 // Get cloned MessagePort from StructuredCloneData. 701 if (aCloneData) { 702 Sequence<OwningNonNull<MessagePort>> ports; 703 if (!aCloneData->TakeTransferredPortsAsSequence(ports)) { 704 aError.Throw(NS_ERROR_FAILURE); 705 return; 706 } 707 argument.mPorts.Construct(std::move(ports)); 708 } 709 710 argument.mName = aMessage; 711 argument.mSync = aIsSync; 712 argument.mTarget = aTarget; 713 if (aTargetFrameLoader) { 714 argument.mTargetFrameLoader.Construct(*aTargetFrameLoader); 715 } 716 717 JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue()); 718 719 if (JS::IsCallable(object)) { 720 // A small hack to get 'this' value right on content side where 721 // messageManager is wrapped in BrowserChildMessageManager's global. 722 nsCOMPtr<nsISupports> defaultThisValue; 723 if (mChrome) { 724 defaultThisValue = do_QueryObject(this); 725 } else { 726 defaultThisValue = aTarget; 727 } 728 js::AssertSameCompartment(cx, object); 729 aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue); 730 if (aError.Failed()) { 731 return; 732 } 733 } 734 735 JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue()); 736 if (webIDLListener) { 737 webIDLListener->ReceiveMessage(thisValue, argument, &rval, aError); 738 if (aError.Failed()) { 739 // At this point the call to ReceiveMessage will have reported any 740 // exceptions (we kept the default of eReportExceptions). We suppress 741 // the failure in the ErrorResult and continue. 742 aError.SuppressException(); 743 continue; 744 } 745 } else { 746 JS::Rooted<JS::Value> funval(cx); 747 if (JS::IsCallable(object)) { 748 // If the listener is a JS function: 749 funval.setObject(*object); 750 } else { 751 // If the listener is a JS object which has receiveMessage function: 752 if (!JS_GetProperty(cx, object, "receiveMessage", &funval) || 753 !funval.isObject()) { 754 aError.Throw(NS_ERROR_UNEXPECTED); 755 return; 756 } 757 758 // Check if the object is even callable. 759 if (!JS::IsCallable(&funval.toObject())) { 760 aError.Throw(NS_ERROR_UNEXPECTED); 761 return; 762 } 763 thisValue.setObject(*object); 764 } 765 766 JS::Rooted<JS::Value> argv(cx); 767 if (!ToJSValue(cx, argument, &argv)) { 768 aError.Throw(NS_ERROR_UNEXPECTED); 769 return; 770 } 771 772 { 773 JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull()); 774 js::AssertSameCompartment(cx, thisObject); 775 if (!JS_CallFunctionValue(cx, thisObject, funval, 776 JS::HandleValueArray(argv), &rval)) { 777 // Because the AutoEntryScript is inside the loop this continue will 778 // make us report any exceptions (after which we'll move on to the 779 // next listener). 780 continue; 781 } 782 } 783 } 784 785 if (aRetVal) { 786 UniquePtr<StructuredCloneData>* data = 787 aRetVal->AppendElement(MakeUnique<StructuredCloneData>()); 788 (*data)->InitScope(JS::StructuredCloneScope::DifferentProcess); 789 (*data)->Write(cx, rval, aError); 790 if (NS_WARN_IF(aError.Failed())) { 791 aRetVal->RemoveLastElement(); 792 nsString msg = aMessage + 793 u": message reply cannot be cloned. Are " 794 "you trying to send an XPCOM object?"_ns; 795 796 nsCOMPtr<nsIConsoleService> console( 797 do_GetService(NS_CONSOLESERVICE_CONTRACTID)); 798 if (console) { 799 nsCOMPtr<nsIScriptError> error( 800 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); 801 error->Init(msg, ""_ns, 0, 0, nsIScriptError::warningFlag, 802 "chrome javascript"_ns, false /* from private window */, 803 true /* from chrome context */); 804 console->LogMessage(error); 805 } 806 807 JS_ClearPendingException(cx); 808 continue; 809 } 810 } 811 } 812 } 813 814 RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager(); 815 if (kungFuDeathGrip) { 816 kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed, 817 aMessage, aIsSync, aCloneData, aRetVal, 818 aError); 819 } 820 } 821 822 void nsFrameMessageManager::LoadPendingScripts( 823 nsFrameMessageManager* aManager, nsFrameMessageManager* aChildMM) { 824 // We have parent manager if we're a message broadcaster. 825 // In that case we want to load the pending scripts from all parent 826 // message managers in the hierarchy. Process the parent first so 827 // that pending scripts higher up in the hierarchy are loaded before others. 828 nsFrameMessageManager* parentManager = aManager->GetParentManager(); 829 if (parentManager) { 830 LoadPendingScripts(parentManager, aChildMM); 831 } 832 833 for (uint32_t i = 0; i < aManager->mPendingScripts.Length(); ++i) { 834 aChildMM->LoadScript(aManager->mPendingScripts[i], false, 835 aManager->mPendingScriptsGlobalStates[i], 836 IgnoreErrors()); 837 } 838 } 839 840 void nsFrameMessageManager::LoadPendingScripts() { 841 RefPtr<nsFrameMessageManager> kungfuDeathGrip = this; 842 LoadPendingScripts(this, this); 843 } 844 845 void nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback) { 846 MOZ_ASSERT(!mIsBroadcaster || !mCallback, 847 "Broadcasters cannot have callbacks!"); 848 if (aCallback && mCallback != aCallback) { 849 mCallback = aCallback; 850 if (mOwnsCallback) { 851 mOwnedCallback = WrapUnique(aCallback); 852 } 853 } 854 } 855 856 void nsFrameMessageManager::Close() { 857 if (!mClosed) { 858 if (nsCOMPtr<nsIObserverService> obs = 859 mozilla::services::GetObserverService()) { 860 obs->NotifyWhenScriptSafe(this, "message-manager-close", nullptr); 861 } 862 } 863 mClosed = true; 864 mCallback = nullptr; 865 mOwnedCallback = nullptr; 866 } 867 868 void nsFrameMessageManager::Disconnect(bool aRemoveFromParent) { 869 // Notify message-manager-close if we haven't already. 870 Close(); 871 872 if (!mDisconnected) { 873 if (nsCOMPtr<nsIObserverService> obs = 874 mozilla::services::GetObserverService()) { 875 obs->NotifyWhenScriptSafe(this, "message-manager-disconnect", nullptr); 876 } 877 } 878 879 ClearParentManager(aRemoveFromParent); 880 881 mDisconnected = true; 882 if (!mHandlingMessage) { 883 mListeners.Clear(); 884 } 885 } 886 887 void nsFrameMessageManager::SetInitialProcessData( 888 JS::Handle<JS::Value> aInitialData) { 889 MOZ_ASSERT(!mChrome); 890 MOZ_ASSERT(mIsProcessManager); 891 MOZ_ASSERT(aInitialData.isObject()); 892 mInitialProcessData = aInitialData; 893 } 894 895 void nsFrameMessageManager::GetInitialProcessData( 896 JSContext* aCx, JS::MutableHandle<JS::Value> aInitialProcessData, 897 ErrorResult& aError) { 898 MOZ_ASSERT(mIsProcessManager); 899 MOZ_ASSERT_IF(mChrome, IsBroadcaster()); 900 901 JS::Rooted<JS::Value> init(aCx, mInitialProcessData); 902 if (mChrome && init.isUndefined()) { 903 // We create the initial object in the junk scope. If we created it in a 904 // normal realm, that realm would leak until shutdown. 905 JS::Rooted<JSObject*> global(aCx, xpc::PrivilegedJunkScope()); 906 JSAutoRealm ar(aCx, global); 907 908 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); 909 if (!obj) { 910 aError.NoteJSContextException(aCx); 911 return; 912 } 913 914 mInitialProcessData.setObject(*obj); 915 init.setObject(*obj); 916 } 917 918 if (!mChrome && XRE_IsParentProcess()) { 919 // This is the cpmm in the parent process. We should use the same object as 920 // the ppmm. Create it first through do_GetService and use the cached 921 // pointer in sParentProcessManager. 922 nsCOMPtr<nsISupports> ppmm = 923 do_GetService("@mozilla.org/parentprocessmessagemanager;1"); 924 sParentProcessManager->GetInitialProcessData(aCx, &init, aError); 925 if (aError.Failed()) { 926 return; 927 } 928 mInitialProcessData = init; 929 } 930 931 if (!JS_WrapValue(aCx, &init)) { 932 aError.NoteJSContextException(aCx); 933 return; 934 } 935 aInitialProcessData.set(init); 936 } 937 938 WritableSharedMap* nsFrameMessageManager::SharedData() { 939 if (!mChrome || !mIsProcessManager) { 940 MOZ_ASSERT(false, "Should only call this binding method on ppmm"); 941 return nullptr; 942 } 943 if (!mSharedData) { 944 mSharedData = new WritableSharedMap(); 945 } 946 return mSharedData; 947 } 948 949 already_AddRefed<ProcessMessageManager> 950 nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError) { 951 RefPtr<ProcessMessageManager> pmm; 952 if (mCallback) { 953 pmm = mCallback->GetProcessMessageManager(); 954 } 955 return pmm.forget(); 956 } 957 958 void nsFrameMessageManager::GetRemoteType(nsACString& aRemoteType, 959 ErrorResult& aError) const { 960 aRemoteType.Truncate(); 961 if (mCallback) { 962 mCallback->DoGetRemoteType(aRemoteType, aError); 963 } 964 } 965 966 namespace { 967 968 struct MessageManagerReferentCount { 969 MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {} 970 size_t mStrong; 971 size_t mWeakAlive; 972 size_t mWeakDead; 973 nsTArray<nsString> mSuspectMessages; 974 nsTHashMap<nsStringHashKey, uint32_t> mMessageCounter; 975 }; 976 977 } // namespace 978 979 namespace mozilla::dom { 980 981 class MessageManagerReporter final : public nsIMemoryReporter { 982 ~MessageManagerReporter() = default; 983 984 public: 985 NS_DECL_ISUPPORTS 986 NS_DECL_NSIMEMORYREPORTER 987 988 static const size_t kSuspectReferentCount = 300; 989 990 protected: 991 void CountReferents(nsFrameMessageManager* aMessageManager, 992 MessageManagerReferentCount* aReferentCount); 993 }; 994 995 NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter) 996 997 void MessageManagerReporter::CountReferents( 998 nsFrameMessageManager* aMessageManager, 999 MessageManagerReferentCount* aReferentCount) { 1000 for (const auto& entry : aMessageManager->mListeners) { 1001 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak(); 1002 uint32_t listenerCount = listeners->Length(); 1003 if (listenerCount == 0) { 1004 continue; 1005 } 1006 1007 nsString key(entry.GetKey()); 1008 const uint32_t currentCount = 1009 (aReferentCount->mMessageCounter.LookupOrInsert(key, 0) += 1010 listenerCount); 1011 1012 // Keep track of messages that have a suspiciously large 1013 // number of referents (symptom of leak). 1014 if (currentCount >= MessageManagerReporter::kSuspectReferentCount) { 1015 aReferentCount->mSuspectMessages.AppendElement(key); 1016 } 1017 1018 for (uint32_t i = 0; i < listenerCount; ++i) { 1019 const nsMessageListenerInfo& listenerInfo = listeners->ElementAt(i); 1020 if (listenerInfo.mWeakListener) { 1021 nsCOMPtr<nsISupports> referent = 1022 do_QueryReferent(listenerInfo.mWeakListener); 1023 if (referent) { 1024 aReferentCount->mWeakAlive++; 1025 } else { 1026 aReferentCount->mWeakDead++; 1027 } 1028 } else { 1029 aReferentCount->mStrong++; 1030 } 1031 } 1032 } 1033 1034 // Add referent count in child managers because the listeners 1035 // participate in messages dispatched from parent message manager. 1036 for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) { 1037 RefPtr<nsFrameMessageManager> mm = aMessageManager->mChildManagers[i]; 1038 CountReferents(mm, aReferentCount); 1039 } 1040 } 1041 1042 static void ReportReferentCount( 1043 const char* aManagerType, const MessageManagerReferentCount& aReferentCount, 1044 nsIHandleReportCallback* aHandleReport, nsISupports* aData) { 1045 #define REPORT(_path, _amount, _desc) \ 1046 do { \ 1047 aHandleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_OTHER, \ 1048 nsIMemoryReporter::UNITS_COUNT, _amount, _desc, \ 1049 aData); \ 1050 } while (0) 1051 1052 REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType), 1053 aReferentCount.mStrong, 1054 nsPrintfCString("The number of strong referents held by the message " 1055 "manager in the %s manager.", 1056 aManagerType)); 1057 REPORT( 1058 nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType), 1059 aReferentCount.mWeakAlive, 1060 nsPrintfCString("The number of weak referents that are still alive " 1061 "held by the message manager in the %s manager.", 1062 aManagerType)); 1063 REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType), 1064 aReferentCount.mWeakDead, 1065 nsPrintfCString("The number of weak referents that are dead " 1066 "held by the message manager in the %s manager.", 1067 aManagerType)); 1068 1069 for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) { 1070 const uint32_t totalReferentCount = 1071 aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i]); 1072 NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]); 1073 REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)", 1074 aManagerType, suspect.get()), 1075 totalReferentCount, 1076 nsPrintfCString("A message in the %s message manager with a " 1077 "suspiciously large number of referents (symptom " 1078 "of a leak).", 1079 aManagerType)); 1080 } 1081 1082 #undef REPORT 1083 } 1084 1085 static StaticRefPtr<ChromeMessageBroadcaster> sGlobalMessageManager; 1086 1087 NS_IMETHODIMP 1088 MessageManagerReporter::CollectReports(nsIHandleReportCallback* aHandleReport, 1089 nsISupports* aData, bool aAnonymize) { 1090 if (XRE_IsParentProcess() && sGlobalMessageManager) { 1091 MessageManagerReferentCount count; 1092 CountReferents(sGlobalMessageManager, &count); 1093 ReportReferentCount("global-manager", count, aHandleReport, aData); 1094 } 1095 1096 if (nsFrameMessageManager::sParentProcessManager) { 1097 MessageManagerReferentCount count; 1098 CountReferents(nsFrameMessageManager::sParentProcessManager, &count); 1099 ReportReferentCount("parent-process-manager", count, aHandleReport, aData); 1100 } 1101 1102 if (nsFrameMessageManager::sChildProcessManager) { 1103 MessageManagerReferentCount count; 1104 CountReferents(nsFrameMessageManager::sChildProcessManager, &count); 1105 ReportReferentCount("child-process-manager", count, aHandleReport, aData); 1106 } 1107 1108 return NS_OK; 1109 } 1110 1111 } // namespace mozilla::dom 1112 1113 already_AddRefed<ChromeMessageBroadcaster> 1114 nsFrameMessageManager::GetGlobalMessageManager() { 1115 RefPtr<ChromeMessageBroadcaster> mm; 1116 if (sGlobalMessageManager) { 1117 mm = sGlobalMessageManager; 1118 } else { 1119 sGlobalMessageManager = mm = 1120 new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL); 1121 ClearOnShutdown(&sGlobalMessageManager); 1122 RegisterStrongMemoryReporter(new MessageManagerReporter()); 1123 } 1124 return mm.forget(); 1125 } 1126 1127 nsresult NS_NewGlobalMessageManager(nsISupports** aResult) { 1128 *aResult = nsFrameMessageManager::GetGlobalMessageManager().take(); 1129 return NS_OK; 1130 } 1131 1132 nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>* 1133 nsMessageManagerScriptExecutor::sCachedScripts = nullptr; 1134 StaticRefPtr<nsScriptCacheCleaner> 1135 nsMessageManagerScriptExecutor::sScriptCacheCleaner; 1136 1137 void nsMessageManagerScriptExecutor::DidCreateScriptLoader() { 1138 if (!sCachedScripts) { 1139 sCachedScripts = 1140 new nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>; 1141 sScriptCacheCleaner = new nsScriptCacheCleaner(); 1142 } 1143 } 1144 1145 // static 1146 void nsMessageManagerScriptExecutor::PurgeCache() { 1147 if (sCachedScripts) { 1148 NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts"); 1149 for (auto iter = sCachedScripts->Iter(); !iter.Done(); iter.Next()) { 1150 delete iter.Data(); 1151 iter.Remove(); 1152 } 1153 } 1154 } 1155 1156 // static 1157 void nsMessageManagerScriptExecutor::Shutdown() { 1158 if (sCachedScripts) { 1159 PurgeCache(); 1160 1161 delete sCachedScripts; 1162 sCachedScripts = nullptr; 1163 sScriptCacheCleaner = nullptr; 1164 } 1165 } 1166 1167 static void FillCompileOptionsForCachedStencil(JS::CompileOptions& aOptions) { 1168 ScriptPreloader::FillCompileOptionsForCachedStencil(aOptions); 1169 aOptions.setNonSyntacticScope(true); 1170 } 1171 1172 void nsMessageManagerScriptExecutor::LoadScriptInternal( 1173 JS::Handle<JSObject*> aMessageManager, const nsAString& aURL, 1174 bool aRunInUniqueScope) { 1175 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING( 1176 "nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER, aURL); 1177 1178 if (!sCachedScripts) { 1179 return; 1180 } 1181 1182 RefPtr<JS::Stencil> stencil; 1183 nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL); 1184 if (holder) { 1185 stencil = holder->mStencil; 1186 } else { 1187 stencil = 1188 TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, aMessageManager); 1189 } 1190 1191 AutoEntryScript aes(aMessageManager, "message manager script load"); 1192 JSContext* cx = aes.cx(); 1193 if (stencil) { 1194 JS::CompileOptions options(cx); 1195 FillCompileOptionsForCachedStencil(options); 1196 JS::InstantiateOptions instantiateOptions(options); 1197 JS::Rooted<JSScript*> script( 1198 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 1199 1200 if (script) { 1201 if (aRunInUniqueScope) { 1202 JS::Rooted<JSObject*> scope(cx); 1203 bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager, 1204 script, &scope); 1205 if (ok) { 1206 // Force the scope to stay alive. 1207 mAnonymousGlobalScopes.AppendElement(scope); 1208 } 1209 } else { 1210 JS::Rooted<JS::Value> rval(cx); 1211 JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No); 1212 if (!envChain.append(aMessageManager)) { 1213 return; 1214 } 1215 (void)JS_ExecuteScript(cx, envChain, script, &rval); 1216 } 1217 } 1218 } 1219 } 1220 1221 already_AddRefed<JS::Stencil> 1222 nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( 1223 const nsAString& aURL, bool aRunInUniqueScope, 1224 JS::Handle<JSObject*> aMessageManager) { 1225 nsCString url = NS_ConvertUTF16toUTF8(aURL); 1226 nsCOMPtr<nsIURI> uri; 1227 nsresult rv = NS_NewURI(getter_AddRefs(uri), url); 1228 if (NS_FAILED(rv)) { 1229 return nullptr; 1230 } 1231 1232 bool hasFlags; 1233 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, 1234 &hasFlags); 1235 if (NS_FAILED(rv) || !hasFlags) { 1236 NS_WARNING("Will not load a frame script!"); 1237 return nullptr; 1238 } 1239 1240 // If this script won't be cached, or there is only one of this type of 1241 // message manager per process, treat this script as run-once. Run-once 1242 // scripts can be compiled directly for the target global, and will be dropped 1243 // from the preloader cache after they're executed and serialized. 1244 // 1245 // NOTE: This does not affect the JS::CompileOptions. We generate the same 1246 // bytecode as though it were run multiple times. This is required for the 1247 // batch decoding from ScriptPreloader to work. 1248 bool isRunOnce = IsProcessScoped(); 1249 1250 // We don't cache data: scripts! 1251 nsAutoCString scheme; 1252 uri->GetScheme(scheme); 1253 bool isCacheable = !scheme.EqualsLiteral("data"); 1254 bool useScriptPreloader = isCacheable; 1255 1256 // If the script will be reused in this session, compile it in the compilation 1257 // scope instead of the current global to avoid keeping the current 1258 // compartment alive. 1259 AutoJSAPI jsapi; 1260 if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) { 1261 return nullptr; 1262 } 1263 JSContext* cx = jsapi.cx(); 1264 1265 RefPtr<JS::Stencil> stencil; 1266 if (useScriptPreloader) { 1267 nsAutoCString cachePath; 1268 rv = scache::PathifyURI(CACHE_PREFIX("script"), uri, cachePath); 1269 NS_ENSURE_SUCCESS(rv, nullptr); 1270 1271 JS::DecodeOptions decodeOptions; 1272 ScriptPreloader::FillDecodeOptionsForCachedStencil(decodeOptions); 1273 stencil = ScriptPreloader::GetChildSingleton().GetCachedStencil( 1274 cx, decodeOptions, cachePath); 1275 } 1276 1277 if (!stencil) { 1278 nsCOMPtr<nsIChannel> channel; 1279 NS_NewChannel(getter_AddRefs(channel), uri, 1280 nsContentUtils::GetSystemPrincipal(), 1281 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 1282 nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT); 1283 1284 if (!channel) { 1285 return nullptr; 1286 } 1287 1288 nsCOMPtr<nsIInputStream> input; 1289 rv = channel->Open(getter_AddRefs(input)); 1290 NS_ENSURE_SUCCESS(rv, nullptr); 1291 nsString dataString; 1292 UniquePtr<Utf8Unit[], JS::FreePolicy> dataStringBuf; 1293 size_t dataStringLength = 0; 1294 if (input) { 1295 nsCString buffer; 1296 uint64_t written; 1297 if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) { 1298 return nullptr; 1299 } 1300 1301 uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX); 1302 ScriptLoader::ConvertToUTF8(channel, (uint8_t*)buffer.get(), size, u""_ns, 1303 nullptr, dataStringBuf, dataStringLength); 1304 } 1305 1306 if (!dataStringBuf) { 1307 return nullptr; 1308 } 1309 1310 JS::CompileOptions options(cx); 1311 FillCompileOptionsForCachedStencil(options); 1312 options.setFileAndLine(url.get(), 1); 1313 1314 // If we are not encoding to the ScriptPreloader cache, we can now relax the 1315 // compile options and use the JS syntax-parser for lower latency. 1316 if (!useScriptPreloader || !ScriptPreloader::GetChildSingleton().Active()) { 1317 options.setSourceIsLazy(false); 1318 } 1319 1320 JS::SourceText<Utf8Unit> srcBuf; 1321 if (!srcBuf.init(cx, std::move(dataStringBuf), dataStringLength)) { 1322 return nullptr; 1323 } 1324 1325 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 1326 if (!stencil) { 1327 return nullptr; 1328 } 1329 1330 if (isCacheable && !isRunOnce) { 1331 // Store into our cache only when we compile it here. 1332 auto* holder = new nsMessageManagerScriptHolder(stencil); 1333 sCachedScripts->InsertOrUpdate(aURL, holder); 1334 } 1335 1336 #ifdef DEBUG 1337 // The above shouldn't touch any options for instantiation. 1338 JS::InstantiateOptions instantiateOptions(options); 1339 instantiateOptions.assertDefault(); 1340 #endif 1341 } 1342 1343 MOZ_ASSERT(stencil); 1344 1345 if (useScriptPreloader) { 1346 nsAutoCString cachePath; 1347 rv = scache::PathifyURI(CACHE_PREFIX("script"), uri, cachePath); 1348 NS_ENSURE_SUCCESS(rv, nullptr); 1349 ScriptPreloader::GetChildSingleton().NoteStencil(url, cachePath, stencil, 1350 isRunOnce); 1351 } 1352 1353 return stencil.forget(); 1354 } 1355 1356 void nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks, 1357 void* aClosure) { 1358 for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length; 1359 ++i) { 1360 aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]", 1361 aClosure); 1362 } 1363 } 1364 1365 void nsMessageManagerScriptExecutor::Unlink() { 1366 ImplCycleCollectionUnlink(mAnonymousGlobalScopes); 1367 } 1368 1369 bool nsMessageManagerScriptExecutor::Init() { 1370 DidCreateScriptLoader(); 1371 return true; 1372 } 1373 1374 void nsMessageManagerScriptExecutor::MarkScopesForCC() { 1375 for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) { 1376 mAnonymousGlobalScopes[i].exposeToActiveJS(); 1377 } 1378 } 1379 1380 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver) 1381 1382 ChildProcessMessageManager* nsFrameMessageManager::sChildProcessManager = 1383 nullptr; 1384 ParentProcessMessageManager* nsFrameMessageManager::sParentProcessManager = 1385 nullptr; 1386 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = 1387 nullptr; 1388 1389 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase, 1390 public Runnable { 1391 public: 1392 nsAsyncMessageToSameProcessChild() 1393 : mozilla::Runnable("nsAsyncMessageToSameProcessChild") {} 1394 NS_IMETHOD Run() override { 1395 nsFrameMessageManager* ppm = 1396 nsFrameMessageManager::GetChildProcessManager(); 1397 ReceiveMessage(ppm, nullptr, ppm); 1398 return NS_OK; 1399 } 1400 }; 1401 1402 /** 1403 * Send messages to an imaginary child process in a single-process scenario. 1404 */ 1405 class SameParentProcessMessageManagerCallback : public MessageManagerCallback { 1406 public: 1407 SameParentProcessMessageManagerCallback() { 1408 MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback); 1409 } 1410 ~SameParentProcessMessageManagerCallback() override { 1411 MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback); 1412 } 1413 1414 bool DoLoadMessageManagerScript(const nsAString& aURL, 1415 bool aRunInGlobalScope) override { 1416 auto* global = ContentProcessMessageManager::Get(); 1417 MOZ_ASSERT(!aRunInGlobalScope); 1418 return global && global->LoadScript(aURL); 1419 } 1420 1421 nsresult DoSendAsyncMessage(const nsAString& aMessage, 1422 StructuredCloneData& aData) override { 1423 RefPtr<nsAsyncMessageToSameProcessChild> ev = 1424 new nsAsyncMessageToSameProcessChild(); 1425 1426 nsresult rv = ev->Init(aMessage, aData); 1427 if (NS_FAILED(rv)) { 1428 return rv; 1429 } 1430 rv = NS_DispatchToCurrentThread(ev); 1431 if (NS_FAILED(rv)) { 1432 return rv; 1433 } 1434 return NS_OK; 1435 } 1436 }; 1437 1438 /** 1439 * Send messages to the parent process. 1440 */ 1441 class ChildProcessMessageManagerCallback : public MessageManagerCallback { 1442 public: 1443 ChildProcessMessageManagerCallback() { 1444 MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback); 1445 } 1446 ~ChildProcessMessageManagerCallback() override { 1447 MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback); 1448 } 1449 1450 bool DoSendBlockingMessage( 1451 const nsAString& aMessage, StructuredCloneData& aData, 1452 nsTArray<UniquePtr<StructuredCloneData>>* aRetVal) override { 1453 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); 1454 if (!cc) { 1455 return true; 1456 } 1457 ClonedMessageData data; 1458 if (!BuildClonedMessageData(aData, data)) { 1459 return false; 1460 } 1461 return cc->SendSyncMessage(PromiseFlatString(aMessage), data, aRetVal); 1462 } 1463 1464 nsresult DoSendAsyncMessage(const nsAString& aMessage, 1465 StructuredCloneData& aData) override { 1466 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); 1467 if (!cc) { 1468 return NS_OK; 1469 } 1470 ClonedMessageData data; 1471 if (!BuildClonedMessageData(aData, data)) { 1472 return NS_ERROR_DOM_DATA_CLONE_ERR; 1473 } 1474 if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), data)) { 1475 return NS_ERROR_UNEXPECTED; 1476 } 1477 1478 return NS_OK; 1479 } 1480 }; 1481 1482 class nsAsyncMessageToSameProcessParent 1483 : public nsSameProcessAsyncMessageBase, 1484 public SameProcessMessageQueue::Runnable { 1485 public: 1486 nsAsyncMessageToSameProcessParent() = default; 1487 nsresult HandleMessage() override { 1488 nsFrameMessageManager* ppm = 1489 nsFrameMessageManager::sSameProcessParentManager; 1490 ReceiveMessage(ppm, nullptr, ppm); 1491 return NS_OK; 1492 } 1493 }; 1494 1495 /** 1496 * Send messages to the imaginary parent process in a single-process scenario. 1497 */ 1498 class SameChildProcessMessageManagerCallback : public MessageManagerCallback { 1499 public: 1500 SameChildProcessMessageManagerCallback() { 1501 MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback); 1502 } 1503 ~SameChildProcessMessageManagerCallback() override { 1504 MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback); 1505 } 1506 1507 bool DoSendBlockingMessage( 1508 const nsAString& aMessage, StructuredCloneData& aData, 1509 nsTArray<UniquePtr<StructuredCloneData>>* aRetVal) override { 1510 SameProcessMessageQueue* queue = SameProcessMessageQueue::Get(); 1511 queue->Flush(); 1512 1513 if (nsFrameMessageManager::sSameProcessParentManager) { 1514 RefPtr<nsFrameMessageManager> ppm = 1515 nsFrameMessageManager::sSameProcessParentManager; 1516 ppm->ReceiveMessage(ppm, nullptr, aMessage, true, &aData, aRetVal, 1517 IgnoreErrors()); 1518 } 1519 return true; 1520 } 1521 1522 nsresult DoSendAsyncMessage(const nsAString& aMessage, 1523 StructuredCloneData& aData) override { 1524 SameProcessMessageQueue* queue = SameProcessMessageQueue::Get(); 1525 RefPtr<nsAsyncMessageToSameProcessParent> ev = 1526 new nsAsyncMessageToSameProcessParent(); 1527 nsresult rv = ev->Init(aMessage, aData); 1528 1529 if (NS_FAILED(rv)) { 1530 return rv; 1531 } 1532 queue->Push(ev); 1533 return NS_OK; 1534 } 1535 }; 1536 1537 // This creates the global parent process message manager. 1538 nsresult NS_NewParentProcessMessageManager(nsISupports** aResult) { 1539 NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager, 1540 "Re-creating sParentProcessManager"); 1541 RefPtr<ParentProcessMessageManager> mm = new ParentProcessMessageManager(); 1542 nsFrameMessageManager::sParentProcessManager = mm; 1543 nsFrameMessageManager::NewProcessMessageManager( 1544 false); // Create same process message manager. 1545 mm.forget(aResult); 1546 return NS_OK; 1547 } 1548 1549 ProcessMessageManager* nsFrameMessageManager::NewProcessMessageManager( 1550 bool aIsRemote) { 1551 if (!nsFrameMessageManager::sParentProcessManager) { 1552 nsCOMPtr<nsISupports> dummy = 1553 do_GetService("@mozilla.org/parentprocessmessagemanager;1"); 1554 } 1555 1556 MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager, 1557 "parent process manager not created"); 1558 ProcessMessageManager* mm; 1559 if (aIsRemote) { 1560 // Callback is set in ContentParent::InitInternal so that the process has 1561 // already started when we send pending scripts. 1562 mm = new ProcessMessageManager( 1563 nullptr, nsFrameMessageManager::sParentProcessManager); 1564 } else { 1565 mm = 1566 new ProcessMessageManager(new SameParentProcessMessageManagerCallback(), 1567 nsFrameMessageManager::sParentProcessManager, 1568 MessageManagerFlags::MM_OWNSCALLBACK); 1569 mm->SetOsPid(base::GetCurrentProcId()); 1570 sSameProcessParentManager = mm; 1571 } 1572 return mm; 1573 } 1574 1575 nsresult NS_NewChildProcessMessageManager(nsISupports** aResult) { 1576 NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(), 1577 "Re-creating sChildProcessManager"); 1578 1579 MessageManagerCallback* cb; 1580 if (XRE_IsParentProcess()) { 1581 cb = new SameChildProcessMessageManagerCallback(); 1582 } else { 1583 cb = new ChildProcessMessageManagerCallback(); 1584 RegisterStrongMemoryReporter(new MessageManagerReporter()); 1585 } 1586 auto* mm = new ChildProcessMessageManager(cb); 1587 nsFrameMessageManager::SetChildProcessManager(mm); 1588 auto global = MakeRefPtr<ContentProcessMessageManager>(mm); 1589 NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED); 1590 return CallQueryInterface(global, aResult); 1591 } 1592 1593 void nsFrameMessageManager::MarkForCC() { 1594 for (const auto& entry : mListeners) { 1595 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak(); 1596 uint32_t count = listeners->Length(); 1597 for (uint32_t i = 0; i < count; i++) { 1598 MessageListener* strongListener = listeners->ElementAt(i).mStrongListener; 1599 if (strongListener) { 1600 strongListener->MarkForCC(); 1601 } 1602 } 1603 } 1604 1605 if (mRefCnt.IsPurple()) { 1606 mRefCnt.RemovePurple(); 1607 } 1608 } 1609 1610 nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase() 1611 #ifdef DEBUG 1612 : mCalledInit(false) 1613 #endif 1614 { 1615 } 1616 1617 nsresult nsSameProcessAsyncMessageBase::Init(const nsAString& aMessage, 1618 StructuredCloneData& aData) { 1619 if (!mData.Copy(aData)) { 1620 return NS_ERROR_OUT_OF_MEMORY; 1621 } 1622 1623 mMessage = aMessage; 1624 #ifdef DEBUG 1625 mCalledInit = true; 1626 #endif 1627 1628 return NS_OK; 1629 } 1630 1631 void nsSameProcessAsyncMessageBase::ReceiveMessage( 1632 nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, 1633 nsFrameMessageManager* aManager) { 1634 // Make sure that we have called Init() and it has succeeded. 1635 MOZ_ASSERT(mCalledInit); 1636 if (aManager) { 1637 RefPtr<nsFrameMessageManager> mm = aManager; 1638 mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData, 1639 nullptr, IgnoreErrors()); 1640 } 1641 }