Bug 1303196 - Add TabGroup and DocGroup objects, r=billm
☠☠ backed out by aef020a0ba34 ☠ ☠
authorMichael Layzell <michael@thelayzells.com>
Thu, 15 Sep 2016 18:31:15 -0400
changeset 315142 b0be7d1b7a9a
parent 315141 27b9a320f8d7
child 315143 04cda7684b40
push id82094
push usermichael@thelayzells.com
push dateSat, 24 Sep 2016 23:05:52 +0000
treeherdermozilla-inbound@b0be7d1b7a9a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1303196
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1303196 - Add TabGroup and DocGroup objects, r=billm MozReview-Commit-ID: QHxYnXmuQN
dom/base/DocGroup.cpp
dom/base/DocGroup.h
dom/base/FormData.cpp
dom/base/moz.build
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
new file mode 100644
--- /dev/null
+++ b/dom/base/DocGroup.cpp
@@ -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)
+{}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/DocGroup.h
@@ -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)
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -3,16 +3,17 @@
 /* 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 "FormData.h"
 #include "nsIVariant.h"
 #include "nsIInputStream.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/HTMLFormElement.h"
 
 #include "MultipartBlobImpl.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 FormData::FormData(nsISupports* aOwner)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -154,16 +154,17 @@ EXPORTS.mozilla.dom += [
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
     'CustomElementsRegistry.h',
     'DirectionalityUtils.h',
+    'DocGroup.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMMatrix.h',
     'DOMParser.h',
@@ -217,16 +218,17 @@ UNIFIED_SOURCES += [
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
     'CustomElementsRegistry.cpp',
     'DirectionalityUtils.cpp',
+    'DocGroup.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
     'DOMParser.cpp',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -75,16 +75,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "AudioChannelService.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
 #include "PostMessageEvent.h"
+#include "DocGroup.h"
 
 // Interfaces Needed
 #include "nsIFrame.h"
 #include "nsCanvasFrame.h"
 #include "nsIWidget.h"
 #include "nsIWidgetListener.h"
 #include "nsIBaseWindow.h"
 #include "nsIDeviceSensors.h"
@@ -1221,18 +1222,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
     mCleanedUp(false),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
     mCanSkipCCGeneration(0),
-    mStaticConstellation(0),
-    mConstellation(NullCString())
+    mTabGroup(new TabGroup())
 {
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
@@ -1257,21 +1257,16 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // 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
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   gRefCnt++;
 
@@ -1425,16 +1420,22 @@ nsGlobalWindow::~nsGlobalWindow()
     // If our outer window's inner window is this window, null out the
     // outer window's reference to this window that's being deleted.
     nsGlobalWindow *outer = GetOuterWindowInternal();
     if (outer) {
       outer->MaybeClearInnerWindow(this);
     }
   }
 
+  // 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
   // involve auditing all of the references that inners and outers can have, and
   // separating the handling into CleanUp() and FreeInnerObjects.
   if (IsInnerWindow()) {
     CleanUp();
   } else {
@@ -3009,16 +3010,19 @@ nsGlobalWindow::InnerSetNewDocument(JSCo
   }
 
   mDoc = aDocument;
   ClearDocumentDependentSlots(aCx);
   mFocusedNode = nullptr;
   mLocalStorage = nullptr;
   mSessionStorage = nullptr;
 
+  // Change which DocGroup this InnerWindow is in.
+  SwitchDocGroup();
+
 #ifdef DEBUG
   mLastOpenedURI = aDocument->GetDocumentURI();
 #endif
 
   Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
                         mMutationBits ? 1 : 0);
 
   // Clear our mutation bitfield.
@@ -3032,20 +3036,19 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
   MOZ_ASSERT(aDocShell);
 
   if (aDocShell == mDocShell) {
     return;
   }
 
   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!");
 
   if (mFrames) {
     mFrames->SetDocShell(aDocShell);
   }
 
@@ -3149,19 +3152,18 @@ nsGlobalWindow::SetOpenerWindow(nsPIDOMW
                "aOriginalOpener is true, but not first call to "
                "SetOpenerWindow!");
   NS_ASSERTION(aOpener || !aOriginalOpener,
                "Shouldn't set mHadOriginalOpener if aOpener is null");
 
   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) {
     MOZ_ASSERT(!mHadOriginalOpener,
                "Probably too late to call ComputeIsSecureContext again");
     mHadOriginalOpener = true;
     mOriginalOpenerWasSecureContext =
       nsGlobalWindow::Cast(aOpener->GetCurrentInnerWindow())->IsSecureContext();
@@ -14406,54 +14408,84 @@ nsGlobalWindow::CheckForDPIChange()
     if (presContext) {
       if (presContext->DeviceContext()->CheckDPIChange()) {
         presContext->UIResolutionChanged();
       }
     }
   }
 }
 
-void
-nsGlobalWindow::GetConstellation(nsACString& aConstellation)
-{
-  FORWARD_TO_INNER_VOID(GetConstellation, (aConstellation));
+TabGroup*
+nsGlobalWindow::GetTabGroup()
+{
+  FORWARD_TO_OUTER(GetTabGroup, (), nullptr);
+
+#ifdef DEBUG
+  // 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
+
+  return mTabGroup;
+}
+
+DocGroup*
+nsGlobalWindow::GetDocGroup()
+{
+  FORWARD_TO_INNER(GetDocGroup, (), 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 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
 
-  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();
-        }
-      }
-    }
-
-    // Get the static constellation from the outer window object.
-    mConstellation.AppendPrintf("^%llu", GetOuterWindowInternal()->mStaticConstellation);
-  }
-
-  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(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -101,31 +101,33 @@ class nsWindowSizes;
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
 class CustomElementsRegistry;
+class DocGroup;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint32_t;
 class Location;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
 class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
+class TabGroup;
 class U2F;
 class VRDisplay;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class WindowOrientationObserver;
 #endif
 namespace cache {
@@ -1704,19 +1706,24 @@ private:
   void FireOnNewGlobalObject();
 
   void DisconnectEventTargetObjects();
 
   // Called only on outer windows to compute the value that will be returned by
   // 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
   // for slightly different purposes. On inner windows it means the
   // inner window is held onto by session history and should not
   // change. On outer windows it means that the window is in a state
   // where we don't want to force creation of a new inner window since
   // we're in the middle of doing just that.
@@ -1925,18 +1932,18 @@ protected:
   // This is the CC generation the last time we called CanSkip.
   uint32_t mCanSkipCCGeneration;
 
   // The VR Displays for this window
   nsTArray<RefPtr<mozilla::dom::VRDisplay>> mVRDisplays;
 
   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;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;