diff --git a/dom/base/Attr.cpp b/dom/base/Attr.cpp index 6c23af968adb..4942216f5143 100644 --- a/dom/base/Attr.cpp +++ b/dom/base/Attr.cpp @@ -331,6 +331,11 @@ Attr::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) { } +void +Attr::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ +} + nsresult Attr::GetEventTargetParent(EventChainPreVisitor& aVisitor) { diff --git a/dom/base/Attr.h b/dom/base/Attr.h index f037c41d1c5b..53a7154a4eda 100644 --- a/dom/base/Attr.h +++ b/dom/base/Attr.h @@ -70,6 +70,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult, bool aPreallocateChildren) const override; virtual already_AddRefed GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override; diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 0d2bdb48358b..8bd7acaec4f6 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1177,6 +1177,12 @@ FragmentOrElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) } } +void +FragmentOrElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + doRemoveChildAt(IndexOf(aKid), aNotify, aKid, mAttrsAndChildren); +} + void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent, OOMReporter& aError) diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index a7d054eeee87..7d99d6d172db 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -123,6 +123,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; virtual void GetTextContentInternal(nsAString& aTextContent, mozilla::OOMReporter& aError) override; virtual void SetTextContentInternal(const nsAString& aTextContent, diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 93e44ec4b866..f8b129b12c86 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -4401,6 +4401,28 @@ nsDocument::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) "(maybe somebody called GetRootElement() too early?)"); } +void +nsDocument::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + if (aKid->IsElement()) { + // Destroy the link map up front before we mess with the child list. + DestroyElementMaps(); + } + + // Preemptively clear mCachedRootElement, since we may be about to remove it + // from our child list, and we don't want to return this maybe-obsolete value + // from any GetRootElement() calls that happen inside of doRemoveChildAt(). + // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any + // GetRootElement() calls until after it's removed the child from mChildren. + // Any call before that point would restore this soon-to-be-obsolete cached + // answer, and our clearing here would be fruitless.) + mCachedRootElement = nullptr; + doRemoveChildAt(IndexOf(aKid), aNotify, aKid, mChildren); + MOZ_ASSERT(mCachedRootElement != aKid, + "Stale pointer in mCachedRootElement, after we tried to clear it " + "(maybe somebody called GetRootElement() too early?)"); +} + void nsDocument::EnsureOnDemandBuiltInUASheet(StyleSheet* aSheet) { diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 12568bab94fc..ee6bad8b997f 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -560,6 +560,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult, bool aPreallocateChildren) const override { diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index 3837bf3035c1..e9e7dbd36590 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -663,6 +663,11 @@ nsGenericDOMDataNode::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) { } +void +nsGenericDOMDataNode::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ +} + nsXBLBinding * nsGenericDOMDataNode::DoGetXBLBinding() const { diff --git a/dom/base/nsGenericDOMDataNode.h b/dom/base/nsGenericDOMDataNode.h index 598945253324..60b0fe139982 100644 --- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -110,6 +110,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; virtual void GetTextContentInternal(nsAString& aTextContent, mozilla::OOMReporter& aError) override { diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index fee9f09b7806..8f4300031b9e 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1927,7 +1927,7 @@ nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify, // nsIDocument::GetRootElement() calls until *after* it has removed aKid from // aChildArray. Any calls before then could potentially restore a stale // value for our cached root element, per note in - // nsDocument::RemoveChildAt_Deprecated(). + // nsDocument::RemoveChildNode(). MOZ_ASSERT(aKid && aKid->GetParentNode() == this && aKid == GetChildAt_Deprecated(aIndex) && IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid"); diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index fcfd303e6971..bd5d3fb4cf90 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -778,6 +778,17 @@ public: */ virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) = 0; + /** + * Remove a child from this node. This method handles calling UnbindFromTree + * on the child appropriately. + * + * @param aKid the content to remove + * @param aNotify whether to notify the document (current document for + * nsIContent, and |this| for nsIDocument) that the remove has + * occurred + */ + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) = 0; + /** * Get a property associated with this node. * diff --git a/dom/html/HTMLFieldSetElement.cpp b/dom/html/HTMLFieldSetElement.cpp index 1821ae177546..51bf95c2daaa 100644 --- a/dom/html/HTMLFieldSetElement.cpp +++ b/dom/html/HTMLFieldSetElement.cpp @@ -195,6 +195,32 @@ HTMLFieldSetElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) } } +void +HTMLFieldSetElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + bool firstLegendHasChanged = false; + + if (mFirstLegend && aKid == mFirstLegend) { + // If we are removing the first legend we have to found another one. + nsIContent* child = mFirstLegend->GetNextSibling(); + mFirstLegend = nullptr; + firstLegendHasChanged = true; + + for (; child; child = child->GetNextSibling()) { + if (child->IsHTMLElement(nsGkAtoms::legend)) { + mFirstLegend = child; + break; + } + } + } + + nsGenericHTMLFormElement::RemoveChildNode(aKid, aNotify); + + if (firstLegendHasChanged) { + NotifyElementsForFirstLegendChange(aNotify); + } +} + void HTMLFieldSetElement::AddElement(nsGenericHTMLFormElement* aElement) { diff --git a/dom/html/HTMLFieldSetElement.h b/dom/html/HTMLFieldSetElement.h index 68b18c501435..8fe1773b3552 100644 --- a/dom/html/HTMLFieldSetElement.h +++ b/dom/html/HTMLFieldSetElement.h @@ -44,6 +44,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aChild, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; // nsIFormControl NS_IMETHOD Reset() override; diff --git a/dom/html/HTMLOptGroupElement.cpp b/dom/html/HTMLOptGroupElement.cpp index d287e7b4f7a9..2347afc03058 100644 --- a/dom/html/HTMLOptGroupElement.cpp +++ b/dom/html/HTMLOptGroupElement.cpp @@ -95,6 +95,14 @@ HTMLOptGroupElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) nsGenericHTMLElement::RemoveChildAt_Deprecated(aIndex, aNotify); } +void +HTMLOptGroupElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + SafeOptionListMutation safeMutation(GetSelect(), this, nullptr, IndexOf(aKid), + aNotify); + nsGenericHTMLElement::RemoveChildNode(aKid, aNotify); +} + nsresult HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue, diff --git a/dom/html/HTMLOptGroupElement.h b/dom/html/HTMLOptGroupElement.h index 0c1c6f414f97..989604cc92c9 100644 --- a/dom/html/HTMLOptGroupElement.h +++ b/dom/html/HTMLOptGroupElement.h @@ -28,6 +28,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; // nsIContent virtual nsresult GetEventTargetParent( diff --git a/dom/html/HTMLPictureElement.cpp b/dom/html/HTMLPictureElement.cpp index 5d5d4cc60f26..1415a7c9ed34 100644 --- a/dom/html/HTMLPictureElement.cpp +++ b/dom/html/HTMLPictureElement.cpp @@ -58,6 +58,30 @@ HTMLPictureElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) nsGenericHTMLElement::RemoveChildAt_Deprecated(aIndex, aNotify); } +void +HTMLPictureElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + if (aKid && aKid->IsHTMLElement(nsGkAtoms::img)) { + HTMLImageElement* img = HTMLImageElement::FromContent(aKid); + if (img) { + img->PictureSourceRemoved(aKid->AsContent()); + } + } else if (aKid && aKid->IsHTMLElement(nsGkAtoms::source)) { + // Find all img siblings after this to notify them of its demise + nsCOMPtr nextSibling = aKid->GetNextSibling(); + if (nextSibling && nextSibling->GetParentNode() == this) { + do { + HTMLImageElement* img = HTMLImageElement::FromContent(nextSibling); + if (img) { + img->PictureSourceRemoved(aKid->AsContent()); + } + } while ( (nextSibling = nextSibling->GetNextSibling()) ); + } + } + + nsGenericHTMLElement::RemoveChildNode(aKid, aNotify); +} + nsresult HTMLPictureElement::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) { diff --git a/dom/html/HTMLPictureElement.h b/dom/html/HTMLPictureElement.h index 361d2ceba8d2..72e5ce15ecfa 100644 --- a/dom/html/HTMLPictureElement.h +++ b/dom/html/HTMLPictureElement.h @@ -24,6 +24,7 @@ public: virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult, bool aPreallocateChildren) const override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; protected: diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index b1549236e69d..8ea93d48abbb 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -224,7 +224,12 @@ HTMLSelectElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) nsGenericHTMLFormElementWithState::RemoveChildAt_Deprecated(aIndex, aNotify); } - +void +HTMLSelectElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + SafeOptionListMutation safeMutation(this, this, nullptr, IndexOf(aKid), aNotify); + nsGenericHTMLFormElementWithState::RemoveChildNode(aKid, aNotify); +} void HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h index 7e54a38fb586..8b4b398dce3b 100644 --- a/dom/html/HTMLSelectElement.h +++ b/dom/html/HTMLSelectElement.h @@ -294,6 +294,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; // Overriden nsIFormControl methods NS_IMETHOD Reset() override; diff --git a/dom/svg/SVGSwitchElement.cpp b/dom/svg/SVGSwitchElement.cpp index a5df9850db77..35f35734ccbc 100644 --- a/dom/svg/SVGSwitchElement.cpp +++ b/dom/svg/SVGSwitchElement.cpp @@ -100,6 +100,13 @@ SVGSwitchElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) MaybeInvalidate(); } +void +SVGSwitchElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + SVGSwitchElementBase::RemoveChildNode(aKid, aNotify); + MaybeInvalidate(); +} + //---------------------------------------------------------------------- // nsIContent methods diff --git a/dom/svg/SVGSwitchElement.h b/dom/svg/SVGSwitchElement.h index 137aea94deb9..ddc0d2256c15 100644 --- a/dom/svg/SVGSwitchElement.h +++ b/dom/svg/SVGSwitchElement.h @@ -43,6 +43,7 @@ public: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; // nsIContent NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override; diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 674b686c9964..edeceb2b388c 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -949,6 +949,101 @@ nsXULElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) } } +void +nsXULElement::RemoveChildNode(nsIContent* aKid, bool aNotify) +{ + // On the removal of a , , or element, + // the possibility exists that some of the items in the removed subtree + // are selected (and therefore need to be deselected). We need to account for this. + nsCOMPtr controlElement; + nsCOMPtr listBox; + bool fireSelectionHandler = false; + + // -1 = do nothing, -2 = null out current item + // anything else = index to re-set as current + int32_t newCurrentIndex = -1; + + if (aKid->NodeInfo()->Equals(nsGkAtoms::listitem, kNameSpaceID_XUL)) { + // This is the nasty case. We have (potentially) a slew of selected items + // and cells going away. + // First, retrieve the tree. + // Check first whether this element IS the tree + controlElement = do_QueryObject(this); + + // If it's not, look at our parent + if (!controlElement) + GetParentTree(getter_AddRefs(controlElement)); + nsCOMPtr controlContent(do_QueryInterface(controlElement)); + RefPtr xulElement = FromContentOrNull(controlContent); + + nsCOMPtr oldKidElem = do_QueryInterface(aKid); + if (xulElement && oldKidElem) { + // Iterate over all of the items and find out if they are contained inside + // the removed subtree. + int32_t length; + controlElement->GetSelectedCount(&length); + for (int32_t i = 0; i < length; i++) { + nsCOMPtr node; + controlElement->MultiGetSelectedItem(i, getter_AddRefs(node)); + // we need to QI here to do an XPCOM-correct pointercompare + nsCOMPtr selElem = do_QueryInterface(node); + if (selElem == oldKidElem && + NS_SUCCEEDED(controlElement->RemoveItemFromSelection(node))) { + length--; + i--; + fireSelectionHandler = true; + } + } + + nsCOMPtr curItem; + controlElement->GetCurrentItem(getter_AddRefs(curItem)); + nsCOMPtr curNode = do_QueryInterface(curItem); + if (curNode && nsContentUtils::ContentIsDescendantOf(curNode, aKid)) { + // Current item going away + IgnoredErrorResult ignored; + nsCOMPtr box = xulElement->GetBoxObject(ignored); + listBox = do_QueryInterface(box); + if (listBox && oldKidElem) { + listBox->GetIndexOfItem(oldKidElem, &newCurrentIndex); + } + + // If any of this fails, we'll just set the current item to null + if (newCurrentIndex == -1) + newCurrentIndex = -2; + } + } + } + + nsStyledElement::RemoveChildNode(aKid, aNotify); + + if (newCurrentIndex == -2) { + controlElement->SetCurrentItem(nullptr); + } else if (newCurrentIndex > -1) { + // Make sure the index is still valid + int32_t treeRows; + listBox->GetRowCount(&treeRows); + if (treeRows > 0) { + newCurrentIndex = std::min((treeRows - 1), newCurrentIndex); + nsCOMPtr newCurrentItem; + listBox->GetItemAtIndex(newCurrentIndex, getter_AddRefs(newCurrentItem)); + nsCOMPtr xulCurItem = do_QueryInterface(newCurrentItem); + if (xulCurItem) + controlElement->SetCurrentItem(xulCurItem); + } else { + controlElement->SetCurrentItem(nullptr); + } + } + + nsIDocument* doc; + if (fireSelectionHandler && (doc = GetComposedDoc())) { + nsContentUtils::DispatchTrustedEvent(doc, + static_cast(this), + NS_LITERAL_STRING("select"), + false, + true); + } +} + void nsXULElement::UnregisterAccessKey(const nsAString& aOldValue) { diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index ce68e94ddecb..bcda3485d8d9 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -367,6 +367,7 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; virtual void RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) override; + virtual void RemoveChildNode(nsIContent* aKid, bool aNotify) override; virtual void DestroyContent() override; #ifdef DEBUG