From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/html/HTMLContentElement.cpp | 393 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 dom/html/HTMLContentElement.cpp (limited to 'dom/html/HTMLContentElement.cpp') diff --git a/dom/html/HTMLContentElement.cpp b/dom/html/HTMLContentElement.cpp new file mode 100644 index 000000000..01c0158a0 --- /dev/null +++ b/dom/html/HTMLContentElement.cpp @@ -0,0 +1,393 @@ +/* -*- 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/. */ + +#include "mozilla/dom/HTMLContentElement.h" +#include "mozilla/dom/HTMLContentElementBinding.h" +#include "mozilla/dom/HTMLUnknownElement.h" +#include "mozilla/dom/NodeListBinding.h" +#include "mozilla/dom/ShadowRoot.h" +#include "mozilla/css/StyleRule.h" +#include "nsGkAtoms.h" +#include "nsStyleConsts.h" +#include "nsIAtom.h" +#include "nsCSSRuleProcessor.h" +#include "nsRuleData.h" +#include "nsRuleProcessorData.h" +#include "nsRuleWalker.h" +#include "nsCSSParser.h" +#include "nsDocument.h" + +// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Content) to add check for web components +// being enabled. +nsGenericHTMLElement* +NS_NewHTMLContentElement(already_AddRefed&& aNodeInfo, + mozilla::dom::FromParser aFromParser) +{ + // When this check is removed, remove the nsDocument.h and + // HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLContentElement. + // + // We have to jump through some hoops to be able to produce both NodeInfo* and + // already_AddRefed& for our callees. + RefPtr nodeInfo(aNodeInfo); + if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) { + already_AddRefed nodeInfoArg(nodeInfo.forget()); + return new mozilla::dom::HTMLUnknownElement(nodeInfoArg); + } + + already_AddRefed nodeInfoArg(nodeInfo.forget()); + return new mozilla::dom::HTMLContentElement(nodeInfoArg); +} + +using namespace mozilla::dom; + +HTMLContentElement::HTMLContentElement(already_AddRefed& aNodeInfo) + : nsGenericHTMLElement(aNodeInfo), mValidSelector(true), mIsInsertionPoint(false) +{ +} + +HTMLContentElement::~HTMLContentElement() +{ +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLContentElement, + nsGenericHTMLElement, + mMatchedNodes) + +NS_IMPL_ADDREF_INHERITED(HTMLContentElement, Element) +NS_IMPL_RELEASE_INHERITED(HTMLContentElement, Element) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLContentElement) +NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) + +NS_IMPL_ELEMENT_CLONE(HTMLContentElement) + +JSObject* +HTMLContentElement::WrapNode(JSContext *aCx, JS::Handle aGivenProto) +{ + return HTMLContentElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsresult +HTMLContentElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + RefPtr oldContainingShadow = GetContainingShadow(); + + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + ShadowRoot* containingShadow = GetContainingShadow(); + if (containingShadow && !oldContainingShadow) { + nsINode* parentNode = nsINode::GetParentNode(); + while (parentNode && parentNode != containingShadow) { + if (parentNode->IsHTMLContentElement()) { + // Content element in fallback content is not an insertion point. + return NS_OK; + } + parentNode = parentNode->GetParentNode(); + } + + // If the content element is being inserted into a ShadowRoot, + // add this element to the list of insertion points. + mIsInsertionPoint = true; + containingShadow->AddInsertionPoint(this); + containingShadow->SetInsertionPointChanged(); + } + + return NS_OK; +} + +void +HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + RefPtr oldContainingShadow = GetContainingShadow(); + + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + + if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) { + oldContainingShadow->RemoveInsertionPoint(this); + + // Remove all the matched nodes now that the + // insertion point is no longer an insertion point. + ClearMatchedNodes(); + oldContainingShadow->SetInsertionPointChanged(); + + mIsInsertionPoint = false; + } +} + +void +HTMLContentElement::AppendMatchedNode(nsIContent* aContent) +{ + mMatchedNodes.AppendElement(aContent); + nsTArray& destInsertionPoint = aContent->DestInsertionPoints(); + destInsertionPoint.AppendElement(this); + + if (mMatchedNodes.Length() == 1) { + // Fallback content gets dropped so we need to updated fallback + // content distribution. + UpdateFallbackDistribution(); + } +} + +void +HTMLContentElement::UpdateFallbackDistribution() +{ + for (nsIContent* child = nsINode::GetFirstChild(); + child; + child = child->GetNextSibling()) { + nsTArray& destInsertionPoint = child->DestInsertionPoints(); + destInsertionPoint.Clear(); + if (mMatchedNodes.IsEmpty()) { + destInsertionPoint.AppendElement(this); + } + } +} + +void +HTMLContentElement::RemoveMatchedNode(nsIContent* aContent) +{ + mMatchedNodes.RemoveElement(aContent); + ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints()); + + if (mMatchedNodes.IsEmpty()) { + // Fallback content is activated so we need to update fallback + // content distribution. + UpdateFallbackDistribution(); + } +} + +void +HTMLContentElement::InsertMatchedNode(uint32_t aIndex, nsIContent* aContent) +{ + mMatchedNodes.InsertElementAt(aIndex, aContent); + nsTArray& destInsertionPoint = aContent->DestInsertionPoints(); + destInsertionPoint.AppendElement(this); + + if (mMatchedNodes.Length() == 1) { + // Fallback content gets dropped so we need to updated fallback + // content distribution. + UpdateFallbackDistribution(); + } +} + +void +HTMLContentElement::ClearMatchedNodes() +{ + for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) { + ShadowRoot::RemoveDestInsertionPoint(this, mMatchedNodes[i]->DestInsertionPoints()); + } + + mMatchedNodes.Clear(); + + UpdateFallbackDistribution(); +} + +static bool +IsValidContentSelectors(nsCSSSelector* aSelector) +{ + nsCSSSelector* currentSelector = aSelector; + while (currentSelector) { + // Blacklist invalid selector fragments. + if (currentSelector->IsPseudoElement() || + currentSelector->mPseudoClassList || + currentSelector->mNegations || + currentSelector->mOperator) { + return false; + } + + currentSelector = currentSelector->mNext; + } + + return true; +} + +nsresult +HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + bool aNotify) +{ + nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, + aValue, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) { + // Select attribute was updated, the insertion point may match different + // elements. + nsIDocument* doc = OwnerDoc(); + nsCSSParser parser(doc->CSSLoader()); + + mValidSelector = true; + mSelectorList = nullptr; + + nsresult rv = parser.ParseSelectorString(aValue, + doc->GetDocumentURI(), + // Bug 11240 + 0, // XXX get the line number! + getter_Transfers(mSelectorList)); + + // We don't want to return an exception if parsing failed because + // the spec does not define it as an exception case. + if (NS_SUCCEEDED(rv)) { + // Ensure that all the selectors are valid + nsCSSSelectorList* selectors = mSelectorList; + while (selectors) { + if (!IsValidContentSelectors(selectors->mSelectors)) { + // If we have an invalid selector, we can not match anything. + mValidSelector = false; + mSelectorList = nullptr; + break; + } + selectors = selectors->mNext; + } + } + + ShadowRoot* containingShadow = GetContainingShadow(); + if (containingShadow) { + containingShadow->DistributeAllNodes(); + } + } + + return NS_OK; +} + +nsresult +HTMLContentElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) +{ + nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, + aAttribute, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::select) { + // The select attribute was removed. This insertion point becomes + // a universal selector. + mValidSelector = true; + mSelectorList = nullptr; + + ShadowRoot* containingShadow = GetContainingShadow(); + if (containingShadow) { + containingShadow->DistributeAllNodes(); + } + } + + return NS_OK; +} + +bool +HTMLContentElement::Match(nsIContent* aContent) +{ + if (!mValidSelector) { + return false; + } + + if (mSelectorList) { + nsIDocument* doc = OwnerDoc(); + ShadowRoot* containingShadow = GetContainingShadow(); + nsIContent* host = containingShadow->GetHost(); + + TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited, + doc, TreeMatchContext::eNeverMatchVisited); + doc->FlushPendingLinkUpdates(); + matchingContext.SetHasSpecifiedScope(); + matchingContext.AddScopeElement(host->AsElement()); + + if (!aContent->IsElement()) { + return false; + } + + return nsCSSRuleProcessor::SelectorListMatches(aContent->AsElement(), + matchingContext, + mSelectorList); + } + + return true; +} + +already_AddRefed +HTMLContentElement::GetDistributedNodes() +{ + RefPtr list = new DistributedContentList(this); + return list.forget(); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DistributedContentList, mParent, + mDistributedNodes) + +NS_INTERFACE_TABLE_HEAD(DistributedContentList) + NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY + NS_INTERFACE_TABLE(DistributedContentList, nsINodeList, nsIDOMNodeList) + NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DistributedContentList) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DistributedContentList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DistributedContentList) + +DistributedContentList::DistributedContentList(HTMLContentElement* aHostElement) + : mParent(aHostElement) +{ + MOZ_COUNT_CTOR(DistributedContentList); + + if (aHostElement->IsInsertionPoint()) { + if (aHostElement->MatchedNodes().IsEmpty()) { + // Fallback content. + nsINode* contentNode = aHostElement; + for (nsIContent* content = contentNode->GetFirstChild(); + content; + content = content->GetNextSibling()) { + mDistributedNodes.AppendElement(content); + } + } else { + mDistributedNodes.AppendElements(aHostElement->MatchedNodes()); + } + } +} + +DistributedContentList::~DistributedContentList() +{ + MOZ_COUNT_DTOR(DistributedContentList); +} + +nsIContent* +DistributedContentList::Item(uint32_t aIndex) +{ + return mDistributedNodes.SafeElementAt(aIndex); +} + +NS_IMETHODIMP +DistributedContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn) +{ + nsIContent* item = Item(aIndex); + if (!item) { + return NS_ERROR_FAILURE; + } + + return CallQueryInterface(item, aReturn); +} + +NS_IMETHODIMP +DistributedContentList::GetLength(uint32_t* aLength) +{ + *aLength = mDistributedNodes.Length(); + return NS_OK; +} + +int32_t +DistributedContentList::IndexOf(nsIContent* aContent) +{ + return mDistributedNodes.IndexOf(aContent); +} + +JSObject* +DistributedContentList::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return NodeListBinding::Wrap(aCx, this, aGivenProto); +} + -- cgit v1.2.3