dom/workers/ServiceWorkerManagerService.cpp
author J. Ryan Stinnett <jryans@gmail.com>
Wed, 17 Feb 2016 22:35:45 -0600
changeset 324714 76944f0d24abc3e3d968f324f18a8a3835e535dc
parent 324710 b2a21a014436551942179dd3e70c2642bb6e4e7a
child 328910 04bc6ea89170fabae71551f128c7c0f195120bbd
permissions -rw-r--r--
Bug 1238160 - Set frame type on TabContext. r=billm,mayhemer This change renames TabContext::IsBrowserElement to IsIsolatedMozBrowserElement. Other methods that pass these values around also have name changes. Adds TabContext::IsMozBrowserElement which is set by the frame loader for all browser frames. This is in contrast to its previous implementation, which has since been renamed IsIsolatedMozBrowserElement, since it checks isolated state in OriginAttributes. TabContext methods related to browser elements (and their callers) are updated to use IsIsolatedMozBrowserElement when check isolation / origins and IsMozBrowserElement when checking frame types. MozReview-Commit-ID: DDMZTkSn5yd

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "ServiceWorkerManagerService.h"
#include "ServiceWorkerManagerParent.h"
#include "ServiceWorkerRegistrar.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/unused.h"

namespace mozilla {

using namespace ipc;

namespace dom {
namespace workers {

namespace {

ServiceWorkerManagerService* sInstance = nullptr;

struct NotifySoftUpdateData
{
  RefPtr<ServiceWorkerManagerParent> mParent;
  RefPtr<ContentParent> mContentParent;

  ~NotifySoftUpdateData()
  {
    MOZ_ASSERT(!mContentParent);
  }
};

class NotifySoftUpdateIfPrincipalOkRunnable final : public nsRunnable
{
public:
  NotifySoftUpdateIfPrincipalOkRunnable(
      nsAutoPtr<nsTArray<NotifySoftUpdateData>>& aData,
      const PrincipalOriginAttributes& aOriginAttributes,
      const nsAString& aScope)
    : mData(aData)
    , mOriginAttributes(aOriginAttributes)
    , mScope(aScope)
    , mBackgroundThread(NS_GetCurrentThread())
  {
    AssertIsInMainProcess();
    AssertIsOnBackgroundThread();

    MOZ_ASSERT(mData && !aData);
    MOZ_ASSERT(mBackgroundThread);
  }

  NS_IMETHODIMP
  Run() override
  {
    if (NS_IsMainThread()) {
      for (uint32_t i = 0; i < mData->Length(); ++i) {
        NotifySoftUpdateData& data = mData->ElementAt(i);
        nsTArray<TabContext> contextArray =
          data.mContentParent->GetManagedTabContext();
        // mContentParent needs to be released in the main thread.
        data.mContentParent = nullptr;
        // We only send the notification about the soft update to the
        // tabs/apps with the same appId and inIsolatedMozBrowser values.
        // Sending a notification to the wrong process will make the process
        // to be killed.
        for (uint32_t j = 0; j < contextArray.Length(); ++j) {
          if ((contextArray[j].OwnOrContainingAppId() == mOriginAttributes.mAppId) &&
              (contextArray[j].IsIsolatedMozBrowserElement() == mOriginAttributes.mInIsolatedMozBrowser)) {
            continue;
          }
          // Array entries with no mParent won't receive any notification.
          data.mParent = nullptr;
        }
      }
      nsresult rv = mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL);
      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
      return NS_OK;
    }

    AssertIsOnBackgroundThread();

    for (uint32_t i = 0; i < mData->Length(); ++i) {
      NotifySoftUpdateData& data = mData->ElementAt(i);
      MOZ_ASSERT(!(data.mContentParent));
      ServiceWorkerManagerParent* parent = data.mParent;
      if (parent && !parent->ActorDestroyed()) {
        Unused << parent->SendNotifySoftUpdate(mOriginAttributes, mScope);
      }
    }
    return NS_OK;
  }

private:
  nsAutoPtr<nsTArray<NotifySoftUpdateData>> mData;
  PrincipalOriginAttributes mOriginAttributes;
  nsString mScope;
  nsCOMPtr<nsIThread> mBackgroundThread;
};

} // namespace

ServiceWorkerManagerService::ServiceWorkerManagerService()
{
  AssertIsOnBackgroundThread();

  // sInstance is a raw ServiceWorkerManagerService*.
  MOZ_ASSERT(!sInstance);
  sInstance = this;
}

ServiceWorkerManagerService::~ServiceWorkerManagerService()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(sInstance == this);
  MOZ_ASSERT(mAgents.Count() == 0);

  sInstance = nullptr;
}

/* static */ already_AddRefed<ServiceWorkerManagerService>
ServiceWorkerManagerService::Get()
{
  AssertIsOnBackgroundThread();

  RefPtr<ServiceWorkerManagerService> instance = sInstance;
  return instance.forget();
}

/* static */ already_AddRefed<ServiceWorkerManagerService>
ServiceWorkerManagerService::GetOrCreate()
{
  AssertIsOnBackgroundThread();

  RefPtr<ServiceWorkerManagerService> instance = sInstance;
  if (!instance) {
    instance = new ServiceWorkerManagerService();
  }
  return instance.forget();
}

void
ServiceWorkerManagerService::RegisterActor(ServiceWorkerManagerParent* aParent)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(aParent);
  MOZ_ASSERT(!mAgents.Contains(aParent));

  mAgents.PutEntry(aParent);
}

void
ServiceWorkerManagerService::UnregisterActor(ServiceWorkerManagerParent* aParent)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(aParent);
  MOZ_ASSERT(mAgents.Contains(aParent));

  mAgents.RemoveEntry(aParent);
}

void
ServiceWorkerManagerService::PropagateRegistration(
                                           uint64_t aParentID,
                                           ServiceWorkerRegistrationData& aData)
{
  AssertIsOnBackgroundThread();

  DebugOnly<bool> parentFound = false;
  for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
    RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
    MOZ_ASSERT(parent);

    if (parent->ID() != aParentID) {
      Unused << parent->SendNotifyRegister(aData);
#ifdef DEBUG
    } else {
      parentFound = true;
#endif
    }
  }

#ifdef DEBUG
  MOZ_ASSERT(parentFound);
#endif
}

void
ServiceWorkerManagerService::PropagateSoftUpdate(
                                      uint64_t aParentID,
                                      const PrincipalOriginAttributes& aOriginAttributes,
                                      const nsAString& aScope)
{
  AssertIsOnBackgroundThread();

  nsAutoPtr<nsTArray<NotifySoftUpdateData>> notifySoftUpdateDataArray(
      new nsTArray<NotifySoftUpdateData>());
  DebugOnly<bool> parentFound = false;
  for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
    RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
    MOZ_ASSERT(parent);

#ifdef DEBUG
    if (parent->ID() == aParentID) {
      parentFound = true;
    }
#endif

    RefPtr<ContentParent> contentParent = parent->GetContentParent();

    // If the ContentParent is null we are dealing with a same-process actor.
    if (!contentParent) {
      Unused << parent->SendNotifySoftUpdate(aOriginAttributes,
                                             nsString(aScope));
      continue;
    }

    NotifySoftUpdateData* data = notifySoftUpdateDataArray->AppendElement();
    data->mContentParent.swap(contentParent);
    data->mParent.swap(parent);
  }

  if (notifySoftUpdateDataArray->IsEmpty()) {
    return;
  }

  RefPtr<NotifySoftUpdateIfPrincipalOkRunnable> runnable =
    new NotifySoftUpdateIfPrincipalOkRunnable(notifySoftUpdateDataArray,
                                              aOriginAttributes, aScope);
  MOZ_ASSERT(!notifySoftUpdateDataArray);
  nsresult rv = NS_DispatchToMainThread(runnable);
  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));

#ifdef DEBUG
  MOZ_ASSERT(parentFound);
#endif
}

void
ServiceWorkerManagerService::PropagateUnregister(
                                            uint64_t aParentID,
                                            const PrincipalInfo& aPrincipalInfo,
                                            const nsAString& aScope)
{
  AssertIsOnBackgroundThread();

  RefPtr<dom::ServiceWorkerRegistrar> service =
    dom::ServiceWorkerRegistrar::Get();
  MOZ_ASSERT(service);

  // It's possible that we don't have any ServiceWorkerManager managing this
  // scope but we still need to unregister it from the ServiceWorkerRegistrar.
  service->UnregisterServiceWorker(aPrincipalInfo,
                                   NS_ConvertUTF16toUTF8(aScope));

  DebugOnly<bool> parentFound = false;
  for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
    RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
    MOZ_ASSERT(parent);

    if (parent->ID() != aParentID) {
      nsString scope(aScope);
      Unused << parent->SendNotifyUnregister(aPrincipalInfo, scope);
#ifdef DEBUG
    } else {
      parentFound = true;
#endif
    }
  }

#ifdef DEBUG
  MOZ_ASSERT(parentFound);
#endif
}

void
ServiceWorkerManagerService::PropagateRemove(uint64_t aParentID,
                                             const nsACString& aHost)
{
  AssertIsOnBackgroundThread();

  DebugOnly<bool> parentFound = false;
  for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
    RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
    MOZ_ASSERT(parent);

    if (parent->ID() != aParentID) {
      nsCString host(aHost);
      Unused << parent->SendNotifyRemove(host);
#ifdef DEBUG
    } else {
      parentFound = true;
#endif
    }
  }

#ifdef DEBUG
  MOZ_ASSERT(parentFound);
#endif
}

void
ServiceWorkerManagerService::PropagateRemoveAll(uint64_t aParentID)
{
  AssertIsOnBackgroundThread();

  RefPtr<dom::ServiceWorkerRegistrar> service =
    dom::ServiceWorkerRegistrar::Get();
  MOZ_ASSERT(service);

  service->RemoveAll();

  DebugOnly<bool> parentFound = false;
  for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
    RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
    MOZ_ASSERT(parent);

    if (parent->ID() != aParentID) {
      Unused << parent->SendNotifyRemoveAll();
#ifdef DEBUG
    } else {
      parentFound = true;
#endif
    }
  }

#ifdef DEBUG
  MOZ_ASSERT(parentFound);
#endif
}

} // namespace workers
} // namespace dom
} // namespace mozilla