Bug 1303196 - Add TabGroup and DocGroup objects, r=billm

MozReview-Commit-ID: QHxYnXmuQN
This commit is contained in:
Michael Layzell 2016-09-15 18:31:15 -04:00
parent 036d6b9816
commit eb68349900
6 changed files with 282 additions and 44 deletions

81
dom/base/DocGroup.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "mozilla/dom/DocGroup.h"
#include "nsIURI.h"
#include "nsIEffectiveTLDService.h"
namespace mozilla {
namespace dom {
/* static */ void
DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
{
aKey.Truncate();
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (tldService) {
rv = tldService->GetBaseDomain(uri, 0, aKey);
if (NS_FAILED(rv)) {
aKey.Truncate();
}
}
}
}
void
DocGroup::Remove(nsPIDOMWindowInner* aWindow)
{
MOZ_ASSERT(mWindows.Contains(aWindow));
mWindows.RemoveElement(aWindow);
}
DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey)
: mKey(aKey), mTabGroup(aTabGroup)
{
// This method does not add itself to mTabGroup->mDocGroups as the caller does it for us
}
DocGroup::~DocGroup()
{
MOZ_ASSERT(mWindows.IsEmpty());
mTabGroup->mDocGroups.RemoveEntry(mKey);
}
TabGroup::~TabGroup()
{
MOZ_ASSERT(mDocGroups.IsEmpty());
}
already_AddRefed<DocGroup>
TabGroup::GetDocGroup(const nsACString& aKey)
{
RefPtr<DocGroup> docGroup(mDocGroups.GetEntry(aKey)->mDocGroup);
return docGroup.forget();
}
already_AddRefed<DocGroup>
TabGroup::JoinDocGroup(const nsACString& aKey, nsPIDOMWindowInner* aWindow)
{
HashEntry* entry = mDocGroups.PutEntry(aKey);
RefPtr<DocGroup> docGroup;
if (entry->mDocGroup) {
docGroup = entry->mDocGroup;
} else {
docGroup = new DocGroup(this, aKey);
entry->mDocGroup = docGroup;
}
// Make sure that the hashtable was updated and now contains the correct value
MOZ_ASSERT(RefPtr<DocGroup>(GetDocGroup(aKey)) == docGroup);
docGroup->mWindows.AppendElement(aWindow);
return docGroup.forget();
}
TabGroup::HashEntry::HashEntry(const nsACString* aKey)
: nsCStringHashKey(aKey), mDocGroup(nullptr)
{}
}
}

115
dom/base/DocGroup.h Normal file
View File

@ -0,0 +1,115 @@
/* -*- 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/. */
#ifndef DocGroup_h
#define DocGroup_h
#include "nsISupports.h"
#include "nsISupportsImpl.h"
#include "nsIPrincipal.h"
#include "nsTHashtable.h"
#include "nsString.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
namespace dom {
// Two browsing contexts are considered "related" if they are reachable from one
// another through window.opener, window.parent, or window.frames. This is the
// spec concept of a "unit of related browsing contexts"
//
// Two browsing contexts are considered "similar-origin" if they can be made to
// have the same origin by setting document.domain. This is the spec concept of
// a "unit of similar-origin related browsing contexts"
//
// A TabGroup is a set of browsing contexts which are all "related". Within a
// TabGroup, browsing contexts are broken into "similar-origin" DocGroups. In
// more detail, a DocGroup is actually a collection of inner windows, and a
// TabGroup is a collection of DocGroups. A TabGroup typically will contain
// (through its DocGroups) the inner windows from one or more tabs related by
// window.opener. A DocGroup is a member of exactly one TabGroup. Inner windows
// that aren't the current window of an outer window are not part of any
// DocGroup.
class TabGroup;
class DocGroup {
private:
typedef nsTArray<nsPIDOMWindowInner*> WindowArray;
public:
typedef WindowArray::iterator Iterator;
friend class TabGroup;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DocGroup)
static void GetKey(nsIPrincipal* aPrincipal, nsACString& aString);
bool MatchesKey(const nsACString& aKey) {
return aKey == mKey;
}
TabGroup* GetTabGroup() {
return mTabGroup;
}
void Remove(nsPIDOMWindowInner* aWindow);
// Iterators for iterating over every window within the DocGroup
Iterator begin() {
return mWindows.begin();
}
Iterator end() {
return mWindows.end();
}
private:
DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
~DocGroup();
nsCString mKey;
RefPtr<TabGroup> mTabGroup;
WindowArray mWindows;
};
class TabGroup {
private:
class HashEntry : public nsCStringHashKey {
public:
// NOTE: Weak reference. The DocGroup destructor removes itself from itw
// owning TabGroup.
DocGroup* mDocGroup;
explicit HashEntry(const nsACString* aKey);
};
typedef nsTHashtable<HashEntry> DocGroupMap;
public:
typedef DocGroupMap::Iterator Iterator;
friend class DocGroup;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TabGroup)
TabGroup() {}
// Get the docgroup for the corresponding doc group key.
// Returns null if the given key hasn't been seen yet.
already_AddRefed<DocGroup>
GetDocGroup(const nsACString& aKey);
already_AddRefed<DocGroup>
JoinDocGroup(const nsACString& aKey, nsPIDOMWindowInner* aWindow);
Iterator Iter() {
return mDocGroups.Iter();
}
private:
~TabGroup();
DocGroupMap mDocGroups;
};
} // namespace dom
} // namespace mozilla
#endif // defined(DocGroup_h)

View File

@ -8,6 +8,7 @@
#include "nsIVariant.h"
#include "nsIInputStream.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "MultipartBlobImpl.h"

View File

@ -159,6 +159,7 @@ EXPORTS.mozilla.dom += [
'Comment.h',
'CustomElementsRegistry.h',
'DirectionalityUtils.h',
'DocGroup.h',
'DocumentFragment.h',
'DocumentType.h',
'DOMCursor.h',
@ -222,6 +223,7 @@ UNIFIED_SOURCES += [
'Crypto.cpp',
'CustomElementsRegistry.cpp',
'DirectionalityUtils.cpp',
'DocGroup.cpp',
'DocumentFragment.cpp',
'DocumentType.cpp',
'DOMCursor.cpp',

View File

@ -80,6 +80,7 @@
#include "nsAboutProtocolUtils.h"
#include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
#include "PostMessageEvent.h"
#include "DocGroup.h"
// Interfaces Needed
#include "nsIFrame.h"
@ -1226,8 +1227,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mDialogAbuseCount(0),
mAreDialogsEnabled(true),
mCanSkipCCGeneration(0),
mStaticConstellation(0),
mConstellation(NullCString())
mTabGroup(new TabGroup())
{
AssertIsOnMainThread();
@ -1262,11 +1262,6 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
// remain frozen until they get an inner window, so freeze this
// outer window here.
Freeze();
// As an outer window, we may be the root of a constellation. This initial
// static constellation may be overridden as this window is given a parent
// window or an opener.
mStaticConstellation = WindowID();
}
// We could have failed the first time through trying
@ -1430,6 +1425,12 @@ nsGlobalWindow::~nsGlobalWindow()
}
}
// Ensure that the docgroup doesn't hold a now-dead reference to our window
if (mDocGroup) {
MOZ_ASSERT(IsInnerWindow());
mDocGroup->Remove(AsInner());
}
// Outer windows are always supposed to call CleanUp before letting themselves
// be destroyed. And while CleanUp generally seems to be intended to clean up
// outers, we've historically called it for both. Changing this would probably
@ -3014,6 +3015,9 @@ nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument)
mLocalStorage = nullptr;
mSessionStorage = nullptr;
// Change which DocGroup this InnerWindow is in.
SwitchDocGroup();
#ifdef DEBUG
mLastOpenedURI = aDocument->GetDocumentURI();
#endif
@ -3037,10 +3041,9 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
mDocShell = aDocShell; // Weak Reference
// Copy over the static constellation from our new parent.
nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetParent();
if (parentWindow) {
mStaticConstellation = Cast(parentWindow)->mStaticConstellation;
InheritTabGroupFrom(parentWindow);
}
NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
@ -3154,9 +3157,8 @@ nsGlobalWindow::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
mOpener = do_GetWeakReference(aOpener);
NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
// Copy over the static constellation from our new opener
if (aOpener) {
mStaticConstellation = Cast(aOpener)->mStaticConstellation;
InheritTabGroupFrom(aOpener);
}
if (aOriginalOpener) {
@ -14411,44 +14413,74 @@ nsGlobalWindow::CheckForDPIChange()
}
}
void
nsGlobalWindow::GetConstellation(nsACString& aConstellation)
TabGroup*
nsGlobalWindow::GetTabGroup()
{
FORWARD_TO_INNER_VOID(GetConstellation, (aConstellation));
FORWARD_TO_OUTER(GetTabGroup, (), nullptr);
#ifdef DEBUG
RefPtr<nsGlobalWindow> outer = GetOuterWindowInternal();
MOZ_ASSERT(outer, "We should have an outer window");
RefPtr<nsGlobalWindow> top = outer->GetTopInternal();
RefPtr<nsPIDOMWindowOuter> opener = outer->GetOpener();
MOZ_ASSERT(!top || (top->mStaticConstellation ==
outer->mStaticConstellation));
MOZ_ASSERT(!opener || (Cast(opener)->mStaticConstellation ==
outer->mStaticConstellation));
// Sanity check that our tabgroup matches our opener or parent
RefPtr<nsGlobalWindow> top = GetTopInternal();
RefPtr<nsPIDOMWindowOuter> opener = GetOpener();
MOZ_ASSERT_IF(top, top->mTabGroup == mTabGroup);
MOZ_ASSERT_IF(opener, Cast(opener)->mTabGroup == mTabGroup);
#endif
if (mConstellation.IsVoid()) {
mConstellation.Truncate();
// The dynamic constellation part comes from the eTLD+1 for the principal's URI.
nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
nsCOMPtr<nsIURI> uri;
nsresult rv = principal->GetURI(getter_AddRefs(uri));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (tldService) {
rv = tldService->GetBaseDomain(uri, 0, mConstellation);
if (NS_FAILED(rv)) {
mConstellation.Truncate();
}
}
}
return mTabGroup;
}
// Get the static constellation from the outer window object.
mConstellation.AppendPrintf("^%llu", GetOuterWindowInternal()->mStaticConstellation);
DocGroup*
nsGlobalWindow::GetDocGroup()
{
FORWARD_TO_INNER(GetDocGroup, (), nullptr);
#ifdef DEBUG
// Sanity check that we have an up-to-date and accurate docgroup
if (mDocGroup) {
nsAutoCString docGroupKey;
DocGroup::GetKey(GetPrincipal(), docGroupKey);
MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
MOZ_ASSERT(mDocGroup->GetTabGroup() == GetTabGroup());
}
#endif
aConstellation.Assign(mConstellation);
return mDocGroup;
}
void
nsGlobalWindow::SwitchDocGroup()
{
MOZ_RELEASE_ASSERT(IsInnerWindow() && mTabGroup);
nsAutoCString docGroupKey;
DocGroup::GetKey(GetPrincipal(), docGroupKey);
if (mDocGroup) {
if (mDocGroup->MatchesKey(docGroupKey)) {
return;
}
MOZ_CRASH("The docgroup of an inner window should not change");
}
mDocGroup = GetTabGroup()->JoinDocGroup(docGroupKey, AsInner());
}
void
nsGlobalWindow::InheritTabGroupFrom(nsPIDOMWindowOuter* aWindow)
{
MOZ_RELEASE_ASSERT(IsOuterWindow());
// If we have an inner window, then that inner window's current doc group is
// within our current tab group. As we are inheriting our tab group (and thus
// changing away from the default of having a new tab group per window), we
// need to remove that inner window from its doc group before we switch to the
// new tab group.
RefPtr<nsGlobalWindow> inner = GetCurrentInnerWindowInternal();
if (inner) {
inner->mDocGroup->Remove(inner->AsInner());
inner->mDocGroup = nullptr;
}
mTabGroup = Cast(aWindow)->mTabGroup;
if (inner) {
inner->SwitchDocGroup();
}
}
nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(

View File

@ -106,6 +106,7 @@ struct ChannelPixelLayout;
class Console;
class Crypto;
class CustomElementsRegistry;
class DocGroup;
class External;
class Function;
class Gamepad;
@ -121,6 +122,7 @@ struct RequestInit;
class RequestOrUSVString;
class Selection;
class SpeechSynthesis;
class TabGroup;
class U2F;
class VRDisplay;
class VREventObserver;
@ -1709,9 +1711,14 @@ private:
// IsSecureContext() for the inner window that corresponds to aDocument.
bool ComputeIsSecureContext(nsIDocument* aDocument);
// Called on inner/outer windows to update the current doc/tab group
void InheritTabGroupFrom(nsPIDOMWindowOuter* aWindow); // Outer only
void SwitchDocGroup(); // Inner only
public:
void GetConstellation(nsACString& aConstellation);
mozilla::dom::TabGroup* GetTabGroup();
mozilla::dom::DocGroup* GetDocGroup();
protected:
// This member is also used on both inner and outer windows, but
@ -1930,8 +1937,8 @@ protected:
nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
uint64_t mStaticConstellation; // Only used on outer windows
nsCString mConstellation; // Only used on inner windows
RefPtr<mozilla::dom::DocGroup> mDocGroup; // Inner window only
RefPtr<mozilla::dom::TabGroup> mTabGroup; // Outer window only
friend class nsDOMScriptableHelper;
friend class nsDOMWindowUtils;