mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1515646 - Add FindWithName and FindChildWithName to BrowsingContext. r=peterv
This implements the step of choosing a browsing context with FindWithName, which should be equivalent to calling nsIDocShellTreeItem.findItemWithName passing null for 'aRequestor' and 'aOriginalRequestor' and false for 'aSkipTabGroup'. Differential Revision: https://phabricator.services.mozilla.com/D15190 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
3b21ed28db
commit
48da8e2402
@ -221,16 +221,16 @@ void BrowsingContext::Detach() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
|
||||
sCachedBrowsingContexts->remove(p);
|
||||
} else {
|
||||
auto* children = mParent ? &mParent->mChildren : &mGroup->Toplevels();
|
||||
Children& children = mParent ? mParent->mChildren : mGroup->Toplevels();
|
||||
|
||||
// TODO(farre): This assert looks extremely fishy, I know, but
|
||||
// what we're actually saying is this: if we're detaching, but our
|
||||
// parent doesn't have any children, it is because we're being
|
||||
// detached by the cycle collector destroying docshells out of
|
||||
// order.
|
||||
MOZ_DIAGNOSTIC_ASSERT(children->IsEmpty() || children->Contains(this));
|
||||
MOZ_DIAGNOSTIC_ASSERT(children.IsEmpty() || children.Contains(this));
|
||||
|
||||
children->RemoveElement(this);
|
||||
children.RemoveElement(this);
|
||||
}
|
||||
|
||||
Group()->Unregister(this);
|
||||
@ -292,6 +292,160 @@ void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
|
||||
cc->SendSetOpenerBrowsingContext(this, aOpener);
|
||||
}
|
||||
|
||||
// FindWithName follows the rules for choosing a browsing context,
|
||||
// with the exception of sandboxing for iframes. The implementation
|
||||
// for arbitrarily choosing between two browsing contexts with the
|
||||
// same name is as follows:
|
||||
//
|
||||
// 1) The start browsing context, i.e. 'this'
|
||||
// 2) Descendants in insertion order
|
||||
// 3) The parent
|
||||
// 4) Siblings and their children, both in insertion order
|
||||
// 5) After this we iteratively follow the parent chain, repeating 3
|
||||
// and 4 until
|
||||
// 6) If there is no parent, consider all other top level browsing
|
||||
// contexts and their children, both in insertion order
|
||||
//
|
||||
// See
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
|
||||
BrowsingContext* BrowsingContext::FindWithName(const nsAString& aName) {
|
||||
BrowsingContext* found = nullptr;
|
||||
if (aName.IsEmpty()) {
|
||||
// You can't find a browsing context with an empty name.
|
||||
found = nullptr;
|
||||
} else if (BrowsingContext* special = FindWithSpecialName(aName)) {
|
||||
found = special;
|
||||
} else if (aName.LowerCaseEqualsLiteral("_blank")) {
|
||||
// Just return null. Caller must handle creating a new window with
|
||||
// a blank name.
|
||||
found = nullptr;
|
||||
} else if (BrowsingContext* child = FindWithNameInSubtree(aName, this)) {
|
||||
found = child;
|
||||
} else {
|
||||
BrowsingContext* current = this;
|
||||
|
||||
do {
|
||||
Children* siblings;
|
||||
BrowsingContext* parent = current->mParent;
|
||||
|
||||
if (!parent) {
|
||||
// We've reached the root of the tree, consider browsing
|
||||
// contexts in the same browsing context group.
|
||||
siblings = &mGroup->Toplevels();
|
||||
} else if (parent->NameEquals(aName) && CanAccess(parent) &&
|
||||
parent->IsActive()) {
|
||||
found = parent;
|
||||
break;
|
||||
} else {
|
||||
siblings = &parent->mChildren;
|
||||
}
|
||||
|
||||
for (BrowsingContext* sibling : *siblings) {
|
||||
if (sibling == current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BrowsingContext* relative =
|
||||
sibling->FindWithNameInSubtree(aName, this)) {
|
||||
found = relative;
|
||||
// Breaks the outer loop
|
||||
parent = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current = parent;
|
||||
} while (current);
|
||||
}
|
||||
|
||||
// Helpers should perform access control checks, which means that we
|
||||
// only need to assert that we can access found.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!found || CanAccess(found));
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
BrowsingContext* BrowsingContext::FindChildWithName(const nsAString& aName) {
|
||||
if (aName.IsEmpty()) {
|
||||
// You can't find a browsing context with the empty name.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (BrowsingContext* child : mChildren) {
|
||||
if (child->NameEquals(aName) && CanAccess(child) && child->IsActive()) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BrowsingContext* BrowsingContext::FindWithSpecialName(const nsAString& aName) {
|
||||
// TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
|
||||
// browsing context pointed to by a special name is active. Should
|
||||
// it be? See Bug 1527913.
|
||||
if (aName.LowerCaseEqualsLiteral("_self")) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (aName.LowerCaseEqualsLiteral("_parent")) {
|
||||
return mParent && CanAccess(mParent.get()) ? mParent.get() : this;
|
||||
}
|
||||
|
||||
if (aName.LowerCaseEqualsLiteral("_top")) {
|
||||
BrowsingContext* top = TopLevelBrowsingContext();
|
||||
|
||||
return CanAccess(top) ? top : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BrowsingContext* BrowsingContext::FindWithNameInSubtree(
|
||||
const nsAString& aName, BrowsingContext* aRequestingContext) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
|
||||
|
||||
if (NameEquals(aName) && aRequestingContext->CanAccess(this) && IsActive()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
for (BrowsingContext* child : mChildren) {
|
||||
if (BrowsingContext* found =
|
||||
child->FindWithNameInSubtree(aName, aRequestingContext)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BrowsingContext::CanAccess(BrowsingContext* aContext) {
|
||||
// TODO(farre): Bouncing this to nsDocShell::CanAccessItem is
|
||||
// temporary, we should implement a replacement for this in
|
||||
// BrowsingContext. See Bug 151590.
|
||||
return aContext && nsDocShell::CanAccessItem(aContext->mDocShell, mDocShell);
|
||||
}
|
||||
|
||||
bool BrowsingContext::IsActive() const {
|
||||
// TODO(farre): Mimicking the bahaviour from
|
||||
// ItemIsActive(nsIDocShellTreeItem* aItem) is temporary, we should
|
||||
// implement a replacement for this using mClosed only. See Bug
|
||||
// 1527321.
|
||||
|
||||
if (!mDocShell) {
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
|
||||
auto* win = nsGlobalWindowOuter::Cast(window);
|
||||
if (!win->GetClosedOuter()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BrowsingContext::~BrowsingContext() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
|
||||
@ -496,22 +650,6 @@ void BrowsingContext::PostMessageMoz(JSContext* aCx,
|
||||
aSubjectPrincipal, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<BrowsingContext> BrowsingContext::FindChildWithName(
|
||||
const nsAString& aName) {
|
||||
// FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1515646 will reimplement
|
||||
// this on top of the BC tree.
|
||||
MOZ_ASSERT(mDocShell);
|
||||
nsCOMPtr<nsIDocShellTreeItem> child;
|
||||
mDocShell->FindChildWithName(aName, false, true, nullptr, nullptr,
|
||||
getter_AddRefs(child));
|
||||
nsCOMPtr<nsIDocShell> childDS = do_QueryInterface(child);
|
||||
RefPtr<BrowsingContext> bc;
|
||||
if (childDS) {
|
||||
childDS->GetBrowsingContext(getter_AddRefs(bc));
|
||||
}
|
||||
return bc.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
namespace ipc {
|
||||
|
@ -130,6 +130,7 @@ class BrowsingContext : public nsWrapperCache,
|
||||
// process. [Bug 1490303]
|
||||
void SetName(const nsAString& aName) { mName = aName; }
|
||||
const nsString& Name() const { return mName; }
|
||||
void GetName(nsAString& aName) { aName = mName; }
|
||||
bool NameEquals(const nsAString& aName) { return mName.Equals(aName); }
|
||||
|
||||
bool IsContent() const { return mType == Type::Content; }
|
||||
@ -146,6 +147,24 @@ class BrowsingContext : public nsWrapperCache,
|
||||
|
||||
BrowsingContextGroup* Group() { return mGroup; }
|
||||
|
||||
// Using the rules for choosing a browsing context we try to find
|
||||
// the browsing context with the given name in the set of
|
||||
// transitively reachable browsing contexts. Performs access control
|
||||
// with regards to this.
|
||||
// See
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
|
||||
//
|
||||
// BrowsingContext::FindWithName(const nsAString&) is equivalent to
|
||||
// calling nsIDocShellTreeItem::FindItemWithName(aName, nullptr,
|
||||
// nullptr, false, <return value>).
|
||||
BrowsingContext* FindWithName(const nsAString& aName);
|
||||
|
||||
// Find a browsing context in this context's list of
|
||||
// children. Doesn't consider the special names, '_self', '_parent',
|
||||
// '_top', or '_blank'. Performs access control with regard to
|
||||
// 'this'.
|
||||
BrowsingContext* FindChildWithName(const nsAString& aName);
|
||||
|
||||
nsISupports* GetParentObject() const;
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
@ -204,8 +223,6 @@ class BrowsingContext : public nsWrapperCache,
|
||||
const WindowPostMessageOptions& aOptions,
|
||||
nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
|
||||
|
||||
already_AddRefed<BrowsingContext> FindChildWithName(const nsAString& aName);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx);
|
||||
|
||||
protected:
|
||||
@ -215,6 +232,22 @@ class BrowsingContext : public nsWrapperCache,
|
||||
Type aType);
|
||||
|
||||
private:
|
||||
// Find the special browsing context if aName is '_self', '_parent',
|
||||
// '_top', but not '_blank'. The latter is handled in FindWithName
|
||||
BrowsingContext* FindWithSpecialName(const nsAString& aName);
|
||||
|
||||
// Find a browsing context in the subtree rooted at 'this' Doesn't
|
||||
// consider the special names, '_self', '_parent', '_top', or
|
||||
// '_blank'. Performs access control with regard to
|
||||
// 'aRequestingContext'.
|
||||
BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
|
||||
BrowsingContext* aRequestingContext);
|
||||
|
||||
// Performs access control to check that 'this' can access 'aContext'.
|
||||
bool CanAccess(BrowsingContext* aContext);
|
||||
|
||||
bool IsActive() const;
|
||||
|
||||
friend class ::nsOuterWindowProxy;
|
||||
friend class ::nsGlobalWindowOuter;
|
||||
// Update the window proxy object that corresponds to this browsing context.
|
||||
|
@ -402,6 +402,7 @@ class nsDocShell final : public nsDocLoader,
|
||||
friend class FramingChecker;
|
||||
friend class OnLinkClickEvent;
|
||||
friend class nsIDocShell;
|
||||
friend class mozilla::dom::BrowsingContext;
|
||||
|
||||
// It is necessary to allow adding a timeline marker wherever a docshell
|
||||
// instance is available. This operation happens frequently and needs to
|
||||
|
@ -3933,7 +3933,7 @@ already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
|
||||
const nsAString& aName) {
|
||||
NS_ENSURE_TRUE(mBrowsingContext, nullptr);
|
||||
|
||||
return mBrowsingContext->FindChildWithName(aName);
|
||||
return do_AddRef(mBrowsingContext->FindChildWithName(aName));
|
||||
}
|
||||
|
||||
bool nsGlobalWindowOuter::DispatchCustomEvent(const nsAString& aEventName) {
|
||||
|
@ -9,6 +9,11 @@ interface nsIDocShell;
|
||||
interface BrowsingContext {
|
||||
static BrowsingContext? get(unsigned long long aId);
|
||||
|
||||
BrowsingContext? findChildWithName(DOMString name);
|
||||
BrowsingContext? findWithName(DOMString name);
|
||||
|
||||
readonly attribute DOMString name;
|
||||
|
||||
readonly attribute BrowsingContext? parent;
|
||||
|
||||
sequence<BrowsingContext> getChildren();
|
||||
|
Loading…
Reference in New Issue
Block a user