tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }