/* -*- 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/. */
/** * A Comparator suitable for mozilla::BinarySearchIf for searching a collection * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
*/ class IsItemInRangeComparator { public: // @param aStartOffset has to be less or equal to aEndOffset.
IsItemInRangeComparator(const nsINode& aNode, const uint32_t aStartOffset, const uint32_t aEndOffset,
nsContentUtils::NodeIndexCache* aCache)
: mNode(aNode),
mStartOffset(aStartOffset),
mEndOffset(aEndOffset),
mCache(aCache) {
MOZ_ASSERT(aStartOffset <= aEndOffset);
}
bool nsINode::IsSelected(const uint32_t aStartOffset, const uint32_t aEndOffset,
SelectionNodeCache* aCache) const {
MOZ_ASSERT(aStartOffset <= aEndOffset); const nsINode* n = GetClosestCommonInclusiveAncestorForRangeInSelection(this);
NS_ASSERTION(n || !IsMaybeSelected(), "A node without a common inclusive ancestor for a range in " "Selection is for sure not selected.");
// Collect the selection objects for potential ranges.
AutoTArray<Selection*, 1> ancestorSelections; for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection(
n->GetParentNode())) { const LinkedList<AbstractRange>* ranges =
n->GetExistingClosestCommonInclusiveAncestorRanges(); if (!ranges) { continue;
} for (const AbstractRange* range : *ranges) {
MOZ_ASSERT(range->IsInAnySelection(), "Why is this range registered with a node?"); // Looks like that IsInSelection() assert fails sometimes... if (range->IsInAnySelection()) { for (const WeakPtr<Selection>& selection : range->GetSelections()) { if (selection && !ancestorSelections.Contains(selection)) {
ancestorSelections.AppendElement(selection);
}
}
}
}
} if (aCache && aCache->MaybeCollectNodesAndCheckIfFullySelectedInAnyOf( this, ancestorSelections)) { returntrue;
}
nsContentUtils::NodeIndexCache cache;
IsItemInRangeComparator comparator{*this, aStartOffset, aEndOffset, &cache}; for (Selection* selection : ancestorSelections) { // Binary search the sorted ranges in this selection. // (Selection::GetRangeAt returns its ranges ordered).
size_t low = 0;
size_t high = selection->RangeCount();
const AbstractRange* const range = selection->GetAbstractRangeAt(middle); int result = comparator(range); if (result == 0) { if (!range->Collapsed()) { returntrue;
}
if (range->MayCrossShadowBoundary()) {
MOZ_ASSERT(range->IsDynamicRange(), "range->MayCrossShadowBoundary() can only return true for " "dynamic range");
StaticRange* crossBoundaryRange =
range->AsDynamicRange()->GetCrossShadowBoundaryRange();
MOZ_ASSERT(crossBoundaryRange); if (!crossBoundaryRange->Collapsed()) { returntrue;
}
}
const AbstractRange* middlePlus1; const AbstractRange* middleMinus1; // if node end > start of middle+1, result = 1 if (middle + 1 < high &&
(middlePlus1 = selection->GetAbstractRangeAt(middle + 1)) &&
nsContentUtils::ComparePoints_Deprecated( this, aEndOffset, middlePlus1->GetStartContainer(),
middlePlus1->StartOffset(), nullptr, &cache) > 0) {
result = 1; // if node start < end of middle - 1, result = -1
} elseif (middle >= 1 &&
(middleMinus1 = selection->GetAbstractRangeAt(middle - 1)) &&
nsContentUtils::ComparePoints_Deprecated( this, aStartOffset, middleMinus1->GetEndContainer(),
middleMinus1->EndOffset(), nullptr, &cache) < 0) {
result = -1;
} else { break;
}
}
if (result < 0) {
high = middle;
} else {
low = middle + 1;
}
}
}
returnfalse;
}
Element* nsINode::GetAnonymousRootElementOfTextEditor(
TextEditor** aTextEditor) { if (aTextEditor) {
*aTextEditor = nullptr;
}
RefPtr<TextControlElement> textControlElement; if (IsInNativeAnonymousSubtree()) {
textControlElement = TextControlElement::FromNodeOrNull(
GetClosestNativeAnonymousSubtreeRootParentOrHost());
} else {
textControlElement = TextControlElement::FromNode(this);
} if (!textControlElement) { return nullptr;
}
RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor(); if (!textEditor) { // The found `TextControlElement` may be an input element which is not a // text control element. In this case, such element must not be in a // native anonymous tree of a `TextEditor` so this node is not in any // `TextEditor`. return nullptr;
}
// There are four cases of interest here. nsINodes that are really: // 1. Document nodes - Are always in the document. // 2.a nsIContent nodes not in a shadow tree - Are either in the document, // or mSubtreeRoot is updated in BindToTree/UnbindFromTree. // 2.b nsIContent nodes in a shadow tree - Are never in the document, // ignore mSubtreeRoot and return the containing shadow root. // 4. Attr nodes - Are never in the document, and mSubtreeRoot // is always 'this' (as set in nsINode's ctor).
nsINode* node; if (IsInUncomposedDoc()) {
node = OwnerDocAsNode();
} elseif (IsContent()) {
ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
node = containingShadow ? containingShadow : mSubtreeRoot; if (!node) {
NS_WARNING("Using SubtreeRoot() on unlinked element?");
node = RootOfNode(this);
}
} else {
node = mSubtreeRoot;
}
MOZ_ASSERT(node, "Should always have a node here!"); #ifdef DEBUG
{ const nsINode* slowNode = RootOfNode(this);
MOZ_ASSERT(slowNode == node, "These should always be in sync!");
} #endif return node;
}
// Special case for ShadowRoot because the ShadowRoot itself is // the root. This is necessary to prevent selection from crossing // the ShadowRoot boundary. // // FIXME(emilio): The NAC check should probably be done before this? We can // have NAC inside shadow DOM. if (ShadowRoot* containingShadow = aContent->GetContainingShadow()) { return containingShadow;
} if (nsIContent* nativeAnonRoot =
aContent->GetClosestNativeAnonymousSubtreeRoot()) { return nativeAnonRoot;
} if (Document* doc = aContent->GetUncomposedDoc()) { return doc->GetRootElement();
} return nsIContent::FromNode(aContent->SubtreeRoot());
}
if (!isContent && !IsDocument()) { return nullptr;
}
if (isContent) { if (GetComposedDoc() != aPresShell->GetDocument()) { return nullptr;
}
if (AsContent()->HasIndependentSelection() ||
IsInNativeAnonymousSubtree()) { // This node should be an inclusive descendant of input/textarea editor. // In that case, the anonymous <div> for TextEditor should be always the // selection root. // FIXME: If Selection for the document is collapsed in <input> or // <textarea>, returning anonymous <div> may make the callers confused. // Perhaps, we should do this only when this is in the native anonymous // subtree unless the callers explicitly want to retrieve the anonymous // <div> from a text control element. if (Element* anonymousDivElement =
GetAnonymousRootElementOfTextEditor()) { return anonymousDivElement;
}
}
}
if (nsPresContext* presContext = aPresShell->GetPresContext()) { if (nsContentUtils::GetHTMLEditor(presContext)) { // When there is an HTMLEditor, selection root should be one of focused // editing host, <body> or root of the (sub)tree which this node belong.
// If this node is in design mode or this node is not editable, selection // root should be the <body> if this node is not in any subtrees and there // is a <body> or the root of the shadow DOM if this node is in a shadow // or the document element. // XXX If this node is not connected, it seems that this should return // nullptr because this node is not selectable. if (!IsInComposedDoc() || IsInDesignMode() ||
!HasFlag(NODE_IS_EDITABLE)) {
Element* const bodyOrDocumentElement = [&]() -> Element* { if (Element* const bodyElement = OwnerDoc()->GetBodyElement()) { return bodyElement;
} return OwnerDoc()->GetDocumentElement();
}();
NS_ENSURE_TRUE(bodyOrDocumentElement, nullptr); return nsContentUtils::IsInSameAnonymousTree(this,
bodyOrDocumentElement)
? bodyOrDocumentElement
: GetRootForContentSubtree(AsContent());
} // If this node is editable but not in the design mode, this is always an // editable node in an editing host of contenteditable. In this case, // let's use the editing host element as selection root.
MOZ_ASSERT(IsEditable());
MOZ_ASSERT(!IsInDesignMode());
MOZ_ASSERT(IsContent()); returnstatic_cast<nsIContent*>(this)->GetEditingHost();
}
}
// This node might be in another subtree, if so, we should find this subtree's // root. Otherwise, we can return the content simply.
NS_ENSURE_TRUE(content, nullptr); if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
content = GetRootForContentSubtree(AsContent()); // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame. // Use the host as the root. if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) {
content = shadowRoot->GetHost(); if (content && aAllowCrossShadowBoundary) {
content = content->GetSelectionRootContent(aPresShell,
aAllowCrossShadowBoundary);
}
}
}
return content;
}
nsINodeList* nsINode::ChildNodes() {
nsSlots* slots = Slots(); if (!slots->mChildNodes) {
slots->mChildNodes = IsAttr() ? new nsAttrChildContentList(this)
: new nsParentNodeChildContentList(this);
}
void nsINode::LastRelease() { if (nsSlots* slots = GetExistingSlots()) { if (!slots->mMutationObservers.isEmpty()) { for (auto iter = slots->mMutationObservers.begin();
iter != slots->mMutationObservers.end(); ++iter) {
iter->NodeWillBeDestroyed(this);
}
}
ClearBoundObjects(*slots, *this); if (IsContent()) {
nsIContent* content = AsContent(); if (HTMLSlotElement* slot = content->GetManualSlotAssignment()) {
content->SetManualSlotAssignment(nullptr);
slot->RemoveManuallyAssignedNode(*content);
}
}
if (Element* element = Element::FromNode(this)) { if (CustomElementData* data = element->GetCustomElementData()) {
data->Unlink();
}
}
delete slots;
mSlots = nullptr;
}
// Kill properties first since that may run external code, so we want to // be in as complete state as possible at that time. if (IsDocument()) { // Delete all properties before tearing down the document. Some of the // properties are bound to nsINode objects and the destructor functions of // the properties may want to use the owner document of the nsINode.
AsDocument()->RemoveAllProperties();
AsDocument()->DropStyleSet();
} else { if (HasProperties()) { // Strong reference to the document so that deleting properties can't // delete the document.
nsCOMPtr<Document> document = OwnerDoc();
document->RemoveAllPropertiesFor(this);
}
if (HasFlag(ADDED_TO_FORM)) { if (auto* formControl = nsGenericHTMLFormControlElement::FromNode(this)) { // Tell the form (if any) this node is going away. Don't // notify, since we're being destroyed in any case.
formControl->ClearForm(true, true);
} elseif (auto* imageElem = HTMLImageElement::FromNode(this)) {
imageElem->ClearForm(true);
}
} if (HasFlag(NODE_HAS_LISTENERMANAGER)) { #ifdef DEBUG if (nsContentUtils::IsInitialized()) {
EventListenerManager* manager =
nsContentUtils::GetExistingListenerManagerForNode(this); if (!manager) {
NS_ERROR( "Huh, our bit says we have a listener manager list, " "but there's nothing in the hash!?!!");
}
} #endif
staticconstchar* NodeTypeAsString(nsINode* aNode) { staticconstchar* NodeTypeStrings[] = { "", // No nodes of type 0 "an Element", "an Attribute", "a Text", "a CDATASection", "an EntityReference", "an Entity", "a ProcessingInstruction", "a Comment", "a Document", "a DocumentType", "a DocumentFragment", "a Notation",
};
static_assert(std::size(NodeTypeStrings) == nsINode::MAX_NODE_TYPE + 1, "Max node type out of range for our array");
nsINode* nsINode::RemoveChild(nsINode& aOldChild, ErrorResult& aError) { if (!aOldChild.IsContent()) { // aOldChild can't be one of our children.
aError.ThrowNotFoundError( "The node to be removed is not a child of this node"); return nullptr;
}
if (aOldChild.GetParentNode() == this) {
nsContentUtils::MaybeFireNodeRemoved(&aOldChild, this);
}
// Check again, we may not be the child's parent anymore. // Can be triggered by dom/base/crashtests/293388-1.html if (aOldChild.IsRootOfNativeAnonymousSubtree() ||
aOldChild.GetParentNode() != this) { // aOldChild isn't one of our children.
aError.ThrowNotFoundError( "The node to be removed is not a child of this node"); return nullptr;
}
if (canMerge || node->TextLength() == 0) { // No need to touch canMerge. That way we can merge across empty // textnodes if and only if the node before is a textnode
nodes.AppendElement(node);
} else {
canMerge = true;
}
// If there's no following sibling, then we need to ensure that we don't // collect following siblings of our (grand)parent as to-be-removed
canMerge = canMerge && !!node->GetNextSibling();
}
if (nodes.IsEmpty()) { return;
}
// We're relying on mozAutoSubtreeModified to keep the doc alive here.
RefPtr<Document> doc = OwnerDoc();
// Batch possible DOMSubtreeModified events.
mozAutoSubtreeModified subtree(doc, nullptr);
// Fire all DOMNodeRemoved events. Optimize the common case of there being // no listeners bool hasRemoveListeners = nsContentUtils::HasMutationListeners(
doc, NS_EVENT_BITS_MUTATION_NODEREMOVED); if (hasRemoveListeners) { for (nsCOMPtr<nsIContent>& node : nodes) { // Node may have already been removed. if (nsCOMPtr<nsINode> parentNode = node->GetParentNode()) { // TODO: Bug 1622253
nsContentUtils::MaybeFireNodeRemoved(MOZ_KnownLive(node), parentNode);
}
}
}
mozAutoDocUpdate batch(doc, true);
// Merge and remove all nodes
nsAutoString tmpStr; for (uint32_t i = 0; i < nodes.Length(); ++i) {
nsIContent* node = nodes[i]; // Merge with previous node unless empty const nsTextFragment* text = node->GetText(); if (text->GetLength()) {
nsIContent* target = node->GetPreviousSibling();
NS_ASSERTION(
(target && target->NodeType() == TEXT_NODE) || hasRemoveListeners, "Should always have a previous text sibling unless " "mutation events messed us up"); if (!hasRemoveListeners || (target && target->NodeType() == TEXT_NODE)) {
nsTextNode* t = static_cast<nsTextNode*>(target); if (text->Is2b()) {
t->AppendTextForNormalize(text->Get2b(), text->GetLength(), true,
node);
} else {
tmpStr.Truncate();
text->AppendTo(tmpStr);
t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node);
}
}
}
// Remove node
nsCOMPtr<nsINode> parent = node->GetParentNode();
NS_ASSERTION(parent || hasRemoveListeners, "Should always have a parent unless " "mutation events messed us up"); if (parent) {
parent->RemoveChildNode(node, true);
}
}
}
void nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix) { if (Element* nsElement = GetNameSpaceElement()) { // XXX Waiting for DOM spec to list error codes.
// Trace up the content parent chain looking for the namespace // declaration that defines the aNamespaceURI namespace. Once found, // return the prefix (i.e. the attribute localName). for (Element* element : nsElement->InclusiveAncestorsOfType<Element>()) {
uint32_t attrCount = element->GetAttrCount();
for (uint32_t i = 0; i < attrCount; ++i) { const nsAttrName* name = element->GetAttrNameAt(i);
if (name->NamespaceEquals(kNameSpaceID_XMLNS) &&
element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(),
aNamespaceURI, eCaseMatters)) { // If the localName is "xmlns", the prefix we output should be // null.
nsAtom* localName = name->LocalName();
// Check if either node is an attribute const Attr* attr1 = Attr::FromNode(node1); if (attr1) { const Element* elem = attr1->GetElement(); // If there is an owner element add the attribute // to the chain and walk up to the element if (elem) {
node1 = elem;
parents1.AppendElement(attr1);
}
} if (auto* attr2 = Attr::FromNode(node2)) { const Element* elem = attr2->GetElement(); if (elem == node1 && attr1) { // Both nodes are attributes on the same element. // Compare position between the attributes.
uint32_t i; const nsAttrName* attrName; for (i = 0; (attrName = elem->GetAttrNameAt(i)); ++i) { if (attrName->Equals(attr1->NodeInfo())) {
NS_ASSERTION(!attrName->Equals(attr2->NodeInfo()), "Different attrs at same position"); return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
Node_Binding::DOCUMENT_POSITION_PRECEDING;
} if (attrName->Equals(attr2->NodeInfo())) { return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
Node_Binding::DOCUMENT_POSITION_FOLLOWING;
}
}
MOZ_ASSERT_UNREACHABLE("neither attribute in the element"); return Node_Binding::DOCUMENT_POSITION_DISCONNECTED;
}
if (elem) {
node2 = elem;
parents2.AppendElement(attr2);
}
}
// We now know that both nodes are either nsIContents or Documents. // If either node started out as an attribute, that attribute will have // the same relative position as its ownerElement, except if the // ownerElement ends up being the container for the other node
// Build the chain of parents do {
parents1.AppendElement(node1);
node1 = node1->GetParentNode();
} while (node1); do {
parents2.AppendElement(node2);
node2 = node2->GetParentNode();
} while (node2);
// Find where the parent chain differs and check indices in the parent. const nsINode* parent = top1;
uint32_t len; for (len = std::min(pos1, pos2); len > 0; --len) { const nsINode* child1 = parents1.ElementAt(--pos1); const nsINode* child2 = parents2.ElementAt(--pos2); if (child1 != child2) { // child1 or child2 can be an attribute here. This will work fine since // ComputeIndexOf will return Nothing for the attribute making the // attribute be considered before any child.
Maybe<uint32_t> child1Index; bool cachedChild1Index = false; if (&aOtherNode == child1 && aOtherIndex) {
cachedChild1Index = true;
child1Index = aOtherIndex->isSome() ? *aOtherIndex
: parent->ComputeIndexOf(child1);
} else {
child1Index = parent->ComputeIndexOf(child1);
}
if (cachedChild1Index) {
*aOtherIndex = child1Index;
} if (cachedChild2Index) {
*aThisIndex = child2Index;
}
return retVal;
}
parent = child1;
}
// We hit the end of one of the parent chains without finding a difference // between the chains. That must mean that one node is an ancestor of the // other. The one with the shortest chain must be the ancestor. return pos1 < pos2 ? (Node_Binding::DOCUMENT_POSITION_PRECEDING |
Node_Binding::DOCUMENT_POSITION_CONTAINS)
: (Node_Binding::DOCUMENT_POSITION_FOLLOWING |
Node_Binding::DOCUMENT_POSITION_CONTAINED_BY);
}
bool nsINode::IsSameNode(nsINode* other) { return other == this; }
bool nsINode::IsEqualNode(nsINode* aOther) { if (!aOther) { returnfalse;
}
// Might as well do a quick check to avoid walking our kids if we're // obviously the same. if (aOther == this) { returntrue;
}
switch (nodeType) { case ELEMENT_NODE: { // Both are elements (we checked that their nodeinfos are equal). Do the // check on attributes.
Element* element1 = node1->AsElement();
Element* element2 = node2->AsElement();
uint32_t attrCount = element1->GetAttrCount(); if (attrCount != element2->GetAttrCount()) { returnfalse;
}
// Iterate over attributes. for (uint32_t i = 0; i < attrCount; ++i) { const nsAttrName* attrName = element1->GetAttrNameAt(i); #ifdef DEBUG bool hasAttr = #endif
element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(),
string1);
NS_ASSERTION(hasAttr, "Why don't we have an attr?");
if (!element2->AttrValueIs(attrName->NamespaceID(),
attrName->LocalName(), string1,
eCaseMatters)) { returnfalse;
}
} break;
} case TEXT_NODE: case COMMENT_NODE: case CDATA_SECTION_NODE: case PROCESSING_INSTRUCTION_NODE: {
MOZ_ASSERT(node1->IsCharacterData());
MOZ_ASSERT(node2->IsCharacterData()); auto* data1 = static_cast<CharacterData*>(node1); auto* data2 = static_cast<CharacterData*>(node2);
if (!data1->TextEquals(data2)) { returnfalse;
}
break;
} case DOCUMENT_NODE: case DOCUMENT_FRAGMENT_NODE: break; case ATTRIBUTE_NODE: {
NS_ASSERTION(node1 == this && node2 == aOther, "Did we come upon an attribute node while walking a " "subtree?");
node1->GetNodeValue(string1);
node2->GetNodeValue(string2);
// Returning here as to not bother walking subtree. And there is no // risk that we're half way through walking some other subtree since // attribute nodes doesn't appear in subtrees. return string1.Equals(string2);
} case DOCUMENT_TYPE_NODE: {
DocumentType* docType1 = static_cast<DocumentType*>(node1);
DocumentType* docType2 = static_cast<DocumentType*>(node2);
// Public ID
docType1->GetPublicId(string1);
docType2->GetPublicId(string2); if (!string1.Equals(string2)) { returnfalse;
}
// System ID
docType1->GetSystemId(string1);
docType2->GetSystemId(string2); if (!string1.Equals(string2)) { returnfalse;
}
nsINode* nextNode = node1->GetFirstChild(); if (nextNode) {
node1 = nextNode;
node2 = node2->GetFirstChild();
} else { if (node2->GetFirstChild()) { // node2 has a firstChild, but node1 doesn't returnfalse;
}
// Find next sibling, possibly walking parent chain. while (1) { if (node1 == this) {
NS_ASSERTION(node2 == aOther, "Should have reached the start node " "for both trees at the same time"); returntrue;
}
bool nsINode::DispatchEvent(Event& aEvent, CallerType aCallerType,
ErrorResult& aRv) { // XXX sXBL/XBL2 issue -- do we really want the owner here? What // if that's the XBL document? Would we want its presshell? Or what?
nsCOMPtr<Document> document = OwnerDoc();
// Do nothing if the element does not belong to a document if (!document) { returntrue;
}
// Obtain a presentation shell
RefPtr<nsPresContext> context = document->GetPresContext();
if (nsCCUncollectableMarker::sGeneration) { // If we're black no need to traverse. if (tmp->HasKnownLiveWrapper() || tmp->InCCBlackTree()) { returnfalse;
}
if (!tmp->UnoptimizableCCNode()) { // If we're in a black document, return early. if ((currentDoc && currentDoc->HasKnownLiveWrapper())) { returnfalse;
} // If we're not in anonymous content and we have a black parent, // return early.
nsIContent* parent = tmp->GetParent(); if (parent && !parent->UnoptimizableCCNode() &&
parent->HasKnownLiveWrapper()) {
MOZ_ASSERT(parent->ComputeIndexOf(tmp).isSome(), "Parent doesn't own us?"); returnfalse;
}
}
}
}
#ifdef DEBUG if (!aError.Failed()) {
MOZ_ASSERT(aParent->OwnerDoc() == doc, "ownerDoc chainged while adopting");
MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?");
MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(), "ownerDocument changed again after adopting!");
} #endif// DEBUG
}
static nsresult UpdateGlobalsInSubtree(nsIContent* aRoot) {
MOZ_ASSERT(ShouldUseNACScope(aRoot)); // Start off with no global so we don't fire any error events on failure.
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
ErrorResult rv;
JS::Rooted<JSObject*> reflector(cx); for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) { if ((reflector = cur->GetWrapper())) {
JSAutoRealm ar(cx, reflector);
UpdateReflectorGlobal(cx, reflector, rv);
rv.WouldReportJSException(); if (rv.Failed()) { // We _could_ consider BlastSubtreeToPieces here, but it's not really // needed. Having some nodes in here accessible to content while others // are not is probably OK. We just need to fail out of the actual // insertion, so they're not in the DOM. Returning a failure here will // do that. return rv.StealNSResult();
}
}
}
return NS_OK;
}
void nsINode::InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis, bool aNotify, ErrorResult& aRv) { if (!IsContainerNode()) {
aRv.ThrowHierarchyRequestError( "Parent is not a Document, DocumentFragment, or Element node."); return;
}
MOZ_ASSERT(!aKid->GetParentNode(), "Inserting node that already has parent");
MOZ_ASSERT(!IsAttr());
// The id-handling code, and in the future possibly other code, need to // react to unexpected attribute changes.
nsMutationGuard::DidMutate();
// Do this before checking the child-count since this could cause mutations
mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify);
if (OwnerDoc() != aKid->OwnerDoc()) {
AdoptNodeIntoOwnerDoc(this, aKid, aRv); if (NS_WARN_IF(aRv.Failed())) { return;
}
}
if (!aBeforeThis) {
AppendChildToChildList(aKid);
} else {
InsertChildToChildList(aKid, aBeforeThis);
}
// XXXbz Do we even need this code anymore? bool wasInNACScope = ShouldUseNACScope(aKid);
BindContext context(*this);
aRv = aKid->BindToTree(context, *this); if (!aRv.Failed() && !wasInNACScope && ShouldUseNACScope(aKid)) {
MOZ_ASSERT(ShouldUseNACScope(this), "Why does the kid need to use an the anonymous content scope?");
aRv = UpdateGlobalsInSubtree(aKid);
} if (aRv.Failed()) {
DisconnectChild(aKid);
aKid->UnbindFromTree(); return;
}
// Invalidate cached array of child nodes
InvalidateChildNodes();
NS_ASSERTION(aKid->GetParentNode() == this, "Did we run script inappropriately?");
if (aNotify) { // Note that we always want to call ContentInserted when things are added // as kids to documents if (parent && !aBeforeThis) {
MutationObservers::NotifyContentAppended(parent, aKid);
} else {
MutationObservers::NotifyContentInserted(this, aKid);
}
nsIContent* nsINode::GetPreviousSibling() const { // Do not expose circular linked list if (mPreviousOrLastSibling && !mPreviousOrLastSibling->mNextSibling) { return nullptr;
} return mPreviousOrLastSibling;
}
// CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer. // It should be small enough to not cause collisions between adjecent objects, // and large enough to make sure that all indexes are used. #define CACHE_POINTER_SHIFT 6 #define CACHE_NUM_SLOTS 128 #define CACHE_CHILD_LIMIT 10
if (aKid->mNextSibling) {
aKid->mNextSibling->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
} else { // aKid is the last child in the list
mFirstChild->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
}
aKid->mPreviousOrLastSibling = nullptr;
if (previousSibling) {
previousSibling->mNextSibling = std::move(aKid->mNextSibling);
} else { // aKid is the first child in the list
mFirstChild = std::move(aKid->mNextSibling);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.