Commit d706a983 authored by Nika Layzell's avatar Nika Layzell
Browse files

Bug 1780614 - Track potential crossoriginisolated status on BCG, r=smaug,kmag

This patch changes how BrowsingContextGroups track CrossOriginIsolated
status such that it should be more consistently tracked and easier to
assert in the places which depend on it. In the new state of the world,
a flag is stored within the BCG's ID which tracks whether it was
created for cross-origin isolated documents, and that is also checked
when making decisions about how to isolate initial about:blank
documents, and whether to allow certain changes to
CrossOriginOpenerPolicy.

This flag is stashed within the ID, as it needs to be preserved if the
BCG is destroyed and then re-created from the ID (which may be e.g.
round-tripped through JS code). I also considered making the ID be a
string instead, to make it easier to include extra information like
this, and more clear where the information is stored, however :kmag
generally preferred using a bit within the integer ID.

These new assertions should now be less likely to spuriously fail due to
a DocShell disappearing or similar as well, which should help fix the
original issue.

Differential Revision: https://phabricator.services.mozilla.com/D152695
parent ea063324
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -371,12 +371,25 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
    // origin is same origin with the creator's top-level origin.
    // If it is cross origin we should not inherit the CrossOriginOpenerPolicy
    fields.mOpenerPolicy = aOpener->Top()->GetOpenerPolicy();

    // If we inherit a policy which is potentially cross-origin isolated, we
    // must be in a potentially cross-origin isolated BCG.
    bool isPotentiallyCrossOriginIsolated =
        fields.mOpenerPolicy ==
        nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
    MOZ_RELEASE_ASSERT(isPotentiallyCrossOriginIsolated ==
                       group->IsPotentiallyCrossOriginIsolated());
  } else if (aOpener) {
    // They are not same origin
    auto topPolicy = aOpener->Top()->GetOpenerPolicy();
    MOZ_RELEASE_ASSERT(topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
                       topPolicy ==
                           nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS);
  } else if (!aParent && group->IsPotentiallyCrossOriginIsolated()) {
    // If we're creating a brand-new toplevel BC in a potentially cross-origin
    // isolated group, it should start out with a strict opener policy.
    fields.mOpenerPolicy =
        nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
  }

  fields.mHistoryID = nsID::GenerateUUID();
@@ -752,6 +765,11 @@ void BrowsingContext::Attach(bool aFromIPC, ContentParent* aOriginProcess) {
  MOZ_DIAGNOSTIC_ASSERT(mGroup);
  MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);

  MOZ_DIAGNOSTIC_ASSERT(
      mGroup->IsPotentiallyCrossOriginIsolated() ==
      (Top()->GetOpenerPolicy() ==
       nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP));

  AssertCoherentLoadContext();

  // Add ourselves either to our parent or BrowsingContextGroup's child list.
@@ -3018,6 +3036,21 @@ void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
  });
}

bool BrowsingContext::CanSet(FieldIndex<IDX_OpenerPolicy>,
                             nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
                             ContentParent* aSource) {
  // A potentially cross-origin isolated BC can't change opener policy, nor can
  // a BC become potentially cross-origin isolated. An unchanged policy is
  // always OK.
  return GetOpenerPolicy() == aPolicy ||
         (GetOpenerPolicy() !=
              nsILoadInfo::
                  OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
          aPolicy !=
              nsILoadInfo::
                  OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP);
}

auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
                             const bool& aAllowContentRetargeting,
                             ContentParent* aSource) -> CanSetResult {
+3 −0
Original line number Diff line number Diff line
@@ -1018,6 +1018,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
    return true;
  }

  bool CanSet(FieldIndex<IDX_OpenerPolicy>,
              nsILoadInfo::CrossOriginOpenerPolicy, ContentParent*);

  bool CanSet(FieldIndex<IDX_ServiceWorkersTestingEnabled>, bool,
              ContentParent*) {
    return IsTop();
+58 −2
Original line number Diff line number Diff line
@@ -51,8 +51,59 @@ already_AddRefed<BrowsingContextGroup> BrowsingContextGroup::GetExisting(
  return nullptr;
}

already_AddRefed<BrowsingContextGroup> BrowsingContextGroup::Create() {
  return GetOrCreate(nsContentUtils::GenerateBrowsingContextId());
// Only use 53 bits for the BrowsingContextGroup ID.
static constexpr uint64_t kBrowsingContextGroupIdTotalBits = 53;
static constexpr uint64_t kBrowsingContextGroupIdProcessBits = 22;
static constexpr uint64_t kBrowsingContextGroupIdFlagBits = 1;
static constexpr uint64_t kBrowsingContextGroupIdBits =
    kBrowsingContextGroupIdTotalBits - kBrowsingContextGroupIdProcessBits -
    kBrowsingContextGroupIdFlagBits;

// IDs for the relevant flags
static constexpr uint64_t kPotentiallyCrossOriginIsolatedFlag = 0x1;

// The next ID value which will be used.
static uint64_t sNextBrowsingContextGroupId = 1;

// Generate the next ID with the given flags.
static uint64_t GenerateBrowsingContextGroupId(uint64_t aFlags) {
  MOZ_RELEASE_ASSERT(aFlags < (uint64_t(1) << kBrowsingContextGroupIdFlagBits));
  uint64_t childId = XRE_IsContentProcess()
                         ? ContentChild::GetSingleton()->GetID()
                         : uint64_t(0);
  MOZ_RELEASE_ASSERT(childId <
                     (uint64_t(1) << kBrowsingContextGroupIdProcessBits));
  uint64_t id = sNextBrowsingContextGroupId++;
  MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kBrowsingContextGroupIdBits));

  return (childId << (kBrowsingContextGroupIdBits +
                      kBrowsingContextGroupIdFlagBits)) |
         (id << kBrowsingContextGroupIdFlagBits) | aFlags;
}

// Extract flags from the given ID.
static uint64_t GetBrowsingContextGroupIdFlags(uint64_t aId) {
  return aId & ((uint64_t(1) << kBrowsingContextGroupIdFlagBits) - 1);
}

uint64_t BrowsingContextGroup::CreateId(bool aPotentiallyCrossOriginIsolated) {
  // We encode the potentially cross-origin isolated bit within the ID so that
  // the information can be recovered whenever the group needs to be re-created
  // due to e.g. being garbage-collected.
  //
  // In the future if we end up needing more complex information stored within
  // the ID, we can consider converting it to a more complex type, like a
  // string.
  uint64_t flags =
      aPotentiallyCrossOriginIsolated ? kPotentiallyCrossOriginIsolatedFlag : 0;
  uint64_t id = GenerateBrowsingContextGroupId(flags);
  MOZ_ASSERT(GetBrowsingContextGroupIdFlags(id) == flags);
  return id;
}

already_AddRefed<BrowsingContextGroup> BrowsingContextGroup::Create(
    bool aPotentiallyCrossOriginIsolated) {
  return GetOrCreate(CreateId(aPotentiallyCrossOriginIsolated));
}

BrowsingContextGroup::BrowsingContextGroup(uint64_t aId) : mId(aId) {
@@ -503,6 +554,11 @@ bool BrowsingContextGroup::DialogsAreBeingAbused() {
  return false;
}

bool BrowsingContextGroup::IsPotentiallyCrossOriginIsolated() {
  return GetBrowsingContextGroupIdFlags(mId) &
         kPotentiallyCrossOriginIsolatedFlag;
}

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BrowsingContextGroup, mContexts,
                                      mToplevels, mHosts, mSubscribers,
                                      mTimerEventQueue, mWorkerEventQueue,
+14 −1
Original line number Diff line number Diff line
@@ -114,10 +114,15 @@ class BrowsingContextGroup final : public nsWrapperCache {
  // Get or create a BrowsingContextGroup with the given ID.
  static already_AddRefed<BrowsingContextGroup> GetOrCreate(uint64_t aId);
  static already_AddRefed<BrowsingContextGroup> GetExisting(uint64_t aId);
  static already_AddRefed<BrowsingContextGroup> Create();
  static already_AddRefed<BrowsingContextGroup> Create(
      bool aPotentiallyCrossOriginIsolated = false);
  static already_AddRefed<BrowsingContextGroup> Select(
      WindowContext* aParent, BrowsingContext* aOpener);

  // Like `Create` but only generates and reserves a new ID without actually
  // creating the BrowsingContextGroup object.
  static uint64_t CreateId(bool aPotentiallyCrossOriginIsolated = false);

  // For each 'ContentParent', except for 'aExcludedParent',
  // associated with this group call 'aCallback'.
  template <typename Func>
@@ -187,6 +192,14 @@ class BrowsingContextGroup final : public nsWrapperCache {
    mLastDialogQuitTime = aLastDialogQuitTime;
  }

  // Whether all toplevel documents loaded in this group are allowed to be
  // Cross-Origin Isolated.
  //
  // This does not reflect the actual value of `crossOriginIsolated`, as that
  // also requires that the document is loaded within a `webCOOP+COEP` content
  // process.
  bool IsPotentiallyCrossOriginIsolated();

  static void GetAllGroups(nsTArray<RefPtr<BrowsingContextGroup>>& aGroups);

  void IncInputEventSuspensionLevel();
+7 −2
Original line number Diff line number Diff line
@@ -193,9 +193,14 @@ void DocGroup::AddDocument(Document* aDocument) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!mDocuments.Contains(aDocument));
  MOZ_ASSERT(mBrowsingContextGroup);
  // If the document is loaded as data it may not have a container, in which
  // case it can be difficult to determine the BrowsingContextGroup it's
  // associated with. XSLT can also add the document to the DocGroup before it
  // gets a container in some cases, in which case this will be asserted
  // elsewhere.
  MOZ_ASSERT_IF(
      FissionAutostart() && !mDocuments.IsEmpty(),
      mDocuments[0]->CrossOriginIsolated() == aDocument->CrossOriginIsolated());
      aDocument->GetBrowsingContext(),
      aDocument->GetBrowsingContext()->Group() == mBrowsingContextGroup);
  mDocuments.AppendElement(aDocument);
}

Loading