netwerk/ipc/SocketProcessBridgeChild.cpp
author Steve Fink <sfink@mozilla.com>
Sun, 25 Sep 2022 18:14:15 +0000
changeset 636414 43c2ed80ba049f83b4da8549c6a5779275876a6d
parent 607812 6cd66e5a2ac36270ea2097c43456bb613fb30b10
permissions -rw-r--r--
Bug 1790149 - Fix spelling of perfherder-data.json output file for awsy r=perftest-reviewers,sparky Differential Revision: https://phabricator.services.mozilla.com/D157002

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "SocketProcessBridgeChild.h"
#include "SocketProcessLogging.h"

#include "mozilla/AppShutdown.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/net/NeckoChild.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_network.h"

namespace mozilla {

using dom::ContentChild;

namespace net {

StaticRefPtr<SocketProcessBridgeChild>
    SocketProcessBridgeChild::sSocketProcessBridgeChild;

NS_IMPL_ISUPPORTS(SocketProcessBridgeChild, nsIObserver)

// static
bool SocketProcessBridgeChild::Create(
    Endpoint<PSocketProcessBridgeChild>&& aEndpoint) {
  MOZ_ASSERT(NS_IsMainThread());

  sSocketProcessBridgeChild =
      new SocketProcessBridgeChild(std::move(aEndpoint));
  if (sSocketProcessBridgeChild->Inited()) {
    mozilla::ipc::BackgroundChild::InitSocketBridgeStarter(
        sSocketProcessBridgeChild);
    return true;
  }

  sSocketProcessBridgeChild = nullptr;
  return false;
}

// static
already_AddRefed<SocketProcessBridgeChild>
SocketProcessBridgeChild::GetSingleton() {
  RefPtr<SocketProcessBridgeChild> child = sSocketProcessBridgeChild;
  return child.forget();
}

// static
RefPtr<SocketProcessBridgeChild::GetPromise>
SocketProcessBridgeChild::GetSocketProcessBridge() {
  MOZ_ASSERT(NS_IsMainThread());

  if (!StaticPrefs::network_process_enabled()) {
    return GetPromise::CreateAndReject(nsCString("Socket process disabled!"),
                                       __func__);
  }

  if (!gNeckoChild) {
    return GetPromise::CreateAndReject(nsCString("No NeckoChild!"), __func__);
  }

  // ContentChild is shutting down, we should not try to create
  // SocketProcessBridgeChild.
  ContentChild* content = ContentChild::GetSingleton();
  if (!content || content->IsShuttingDown()) {
    return GetPromise::CreateAndReject(
        nsCString("ContentChild is shutting down."), __func__);
  }

  if (sSocketProcessBridgeChild) {
    return GetPromise::CreateAndResolve(sSocketProcessBridgeChild, __func__);
  }

  return gNeckoChild->SendInitSocketProcessBridge()->Then(
      GetMainThreadSerialEventTarget(), __func__,
      [](NeckoChild::InitSocketProcessBridgePromise::ResolveOrRejectValue&&
             aResult) {
        ContentChild* content = ContentChild::GetSingleton();
        if (!content || content->IsShuttingDown()) {
          return GetPromise::CreateAndReject(
              nsCString("ContentChild is shutting down."), __func__);
        }
        if (!sSocketProcessBridgeChild) {
          if (aResult.IsReject()) {
            return GetPromise::CreateAndReject(
                nsCString("SendInitSocketProcessBridge failed"), __func__);
          }

          if (!aResult.ResolveValue().IsValid()) {
            return GetPromise::CreateAndReject(
                nsCString(
                    "SendInitSocketProcessBridge resolved with an invalid "
                    "endpoint!"),
                __func__);
          }

          if (!SocketProcessBridgeChild::Create(
                  std::move(aResult.ResolveValue()))) {
            return GetPromise::CreateAndReject(
                nsCString("SendInitSocketProcessBridge resolved with a valid "
                          "endpoint, "
                          "but SocketProcessBridgeChild::Create failed!"),
                __func__);
          }
        }

        return GetPromise::CreateAndResolve(sSocketProcessBridgeChild,
                                            __func__);
      });
}

SocketProcessBridgeChild::SocketProcessBridgeChild(
    Endpoint<PSocketProcessBridgeChild>&& aEndpoint)
    : mShuttingDown(false) {
  LOG(("CONSTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));

  mInited = aEndpoint.Bind(this);
  if (!mInited) {
    MOZ_ASSERT(false, "Bind failed!");
    return;
  }

  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  if (os) {
    os->AddObserver(this, "content-child-shutdown", false);
  }

  mSocketProcessPid = aEndpoint.OtherPid();
}

SocketProcessBridgeChild::~SocketProcessBridgeChild() {
  LOG(("DESTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
}

mozilla::ipc::IPCResult SocketProcessBridgeChild::RecvTest() {
  LOG(("SocketProcessBridgeChild::RecvTest\n"));
  return IPC_OK();
}

void SocketProcessBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
  LOG(("SocketProcessBridgeChild::ActorDestroy\n"));
  if (AbnormalShutdown == aWhy) {
    if (gNeckoChild &&
        !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
      // Let NeckoParent know that the socket process connections must be
      // rebuilt.
      gNeckoChild->SendResetSocketProcessBridge();
    }

    nsresult res;
    nsCOMPtr<nsISerialEventTarget> mSTSThread =
        do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
    if (NS_SUCCEEDED(res) && mSTSThread) {
      // This must be called off the main thread.  If we don't make this call
      // ipc::BackgroundChild::GetOrCreateSocketActorForCurrentThread() will
      // return the previous actor that is no longer able to send. This causes
      // rebuilding the socket process connections to fail.
      MOZ_ALWAYS_SUCCEEDS(mSTSThread->Dispatch(NS_NewRunnableFunction(
          "net::SocketProcessBridgeChild::ActorDestroy",
          []() { ipc::BackgroundChild::CloseForCurrentThread(); })));
    }
  }

  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  if (os) {
    os->RemoveObserver(this, "content-child-shutdown");
  }
  GetCurrentSerialEventTarget()->Dispatch(
      NewRunnableMethod("net::SocketProcessBridgeChild::DeferredDestroy", this,
                        &SocketProcessBridgeChild::DeferredDestroy));
  mShuttingDown = true;
}

NS_IMETHODIMP
SocketProcessBridgeChild::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData) {
  if (!strcmp(aTopic, "content-child-shutdown")) {
    PSocketProcessBridgeChild::Close();
  }
  return NS_OK;
}

void SocketProcessBridgeChild::DeferredDestroy() {
  MOZ_ASSERT(NS_IsMainThread());

  sSocketProcessBridgeChild = nullptr;
}

}  // namespace net
}  // namespace mozilla