/* -*- Mode: C++; tab-width: 8; 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 "mozilla/dom/TabGroup.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" #include "mozilla/ThrottledEventQueue.h" #include "nsIDocShell.h" #include "nsIEffectiveTLDService.h" #include "nsIURI.h" namespace mozilla { namespace dom { static StaticRefPtr sChromeTabGroup; TabGroup::TabGroup(bool aIsChrome) { // Do not throttle runnables from chrome windows. In theory we should // not have abuse issues from these windows and many browser chrome // tests have races that fail if we do throttle chrome runnables. if (aIsChrome) { MOZ_ASSERT(!sChromeTabGroup); return; } nsCOMPtr mainThread; NS_GetMainThread(getter_AddRefs(mainThread)); MOZ_DIAGNOSTIC_ASSERT(mainThread); // This may return nullptr during xpcom shutdown. This is ok as we // do not guarantee a ThrottledEventQueue will be present. mThrottledEventQueue = ThrottledEventQueue::Create(mainThread); } TabGroup::~TabGroup() { MOZ_ASSERT(mDocGroups.IsEmpty()); MOZ_ASSERT(mWindows.IsEmpty()); } TabGroup* TabGroup::GetChromeTabGroup() { if (!sChromeTabGroup) { sChromeTabGroup = new TabGroup(true /* chrome tab group */); ClearOnShutdown(&sChromeTabGroup); } return sChromeTabGroup; } already_AddRefed TabGroup::GetDocGroup(const nsACString& aKey) { RefPtr docGroup(mDocGroups.GetEntry(aKey)->mDocGroup); return docGroup.forget(); } already_AddRefed TabGroup::AddDocument(const nsACString& aKey, nsIDocument* aDocument) { HashEntry* entry = mDocGroups.PutEntry(aKey); RefPtr 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(GetDocGroup(aKey)) == docGroup); docGroup->mDocuments.AppendElement(aDocument); return docGroup.forget(); } /* static */ already_AddRefed TabGroup::Join(nsPIDOMWindowOuter* aWindow, TabGroup* aTabGroup) { RefPtr tabGroup = aTabGroup; if (!tabGroup) { tabGroup = new TabGroup(); } MOZ_ASSERT(!tabGroup->mWindows.Contains(aWindow)); tabGroup->mWindows.AppendElement(aWindow); return tabGroup.forget(); } void TabGroup::Leave(nsPIDOMWindowOuter* aWindow) { MOZ_ASSERT(mWindows.Contains(aWindow)); mWindows.RemoveElement(aWindow); } nsresult TabGroup::FindItemWithName(const nsAString& aName, nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aFoundItem) { NS_ENSURE_ARG_POINTER(aFoundItem); *aFoundItem = nullptr; MOZ_ASSERT(!aName.LowerCaseEqualsLiteral("_blank") && !aName.LowerCaseEqualsLiteral("_top") && !aName.LowerCaseEqualsLiteral("_parent") && !aName.LowerCaseEqualsLiteral("_self")); for (nsPIDOMWindowOuter* outerWindow : mWindows) { // Ignore non-toplevel windows if (outerWindow->GetScriptableParentOrNull()) { continue; } nsCOMPtr docshell = outerWindow->GetDocShell(); if (!docshell) { continue; } nsCOMPtr root; docshell->GetSameTypeRootTreeItem(getter_AddRefs(root)); MOZ_RELEASE_ASSERT(docshell == root); if (root && aRequestor != root) { root->FindItemWithName(aName, this, aOriginalRequestor, aFoundItem); if (*aFoundItem) { break; } } } return NS_OK; } nsTArray TabGroup::GetTopLevelWindows() { nsTArray array; for (nsPIDOMWindowOuter* outerWindow : mWindows) { if (!outerWindow->GetScriptableParentOrNull()) { array.AppendElement(outerWindow); } } return array; } ThrottledEventQueue* TabGroup::GetThrottledEventQueue() const { return mThrottledEventQueue; } NS_IMPL_ISUPPORTS(TabGroup, nsISupports) TabGroup::HashEntry::HashEntry(const nsACString* aKey) : nsCStringHashKey(aKey), mDocGroup(nullptr) {} } }