diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 4f2dcfe12b51..ea705df3f085 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -721,6 +721,26 @@ nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML) return rv; } +void +nsGenericHTMLElement::FireMutationEventsForDirectParsing(nsIDocument* aDoc, + nsIContent* aDest, + PRInt32 aOldChildCount) +{ + // Fire mutation events. Optimize for the case when there are no listeners + PRInt32 newChildCount = aDest->GetChildCount(); + if (newChildCount && nsContentUtils:: + HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) { + nsAutoTArray, 50> childNodes; + NS_ASSERTION(newChildCount - aOldChildCount >= 0, + "What, some unexpected dom mutation has happened?"); + childNodes.SetCapacity(newChildCount - aOldChildCount); + for (nsINode::ChildIterator iter(aDest); !iter.IsDone(); iter.Next()) { + childNodes.AppendElement(iter); + } + nsGenericElement::FireNodeInserted(aDoc, aDest, childNodes); + } +} + nsresult nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML) { @@ -755,19 +775,7 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML) eCompatibility_NavQuirks, PR_TRUE); // HTML5 parser has notified, but not fired mutation events. - // Fire mutation events. Optimize for the case when there are no listeners - PRInt32 newChildCount = GetChildCount(); - if (newChildCount && nsContentUtils:: - HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) { - nsAutoTArray, 50> childNodes; - NS_ASSERTION(newChildCount - oldChildCount >= 0, - "What, some unexpected dom mutation has happened?"); - childNodes.SetCapacity(newChildCount - oldChildCount); - for (nsINode::ChildIterator iter(this); !iter.IsDone(); iter.Next()) { - childNodes.AppendElement(iter); - } - nsGenericElement::FireNodeInserted(doc, this, childNodes); - } + FireMutationEventsForDirectParsing(doc, this, oldChildCount); } else { rv = nsContentUtils::CreateContextualFragment(this, aInnerHTML, PR_TRUE, @@ -781,6 +789,104 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML) return rv; } +enum nsAdjacentPosition { + eBeforeBegin, + eAfterBegin, + eBeforeEnd, + eAfterEnd +}; + +nsresult +nsGenericHTMLElement::InsertAdjacentHTML(const nsAString& aPosition, + const nsAString& aText) +{ + nsAdjacentPosition position; + if (aPosition.LowerCaseEqualsLiteral("beforebegin")) { + position = eBeforeBegin; + } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) { + position = eAfterBegin; + } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) { + position = eBeforeEnd; + } else if (aPosition.LowerCaseEqualsLiteral("afterend")) { + position = eAfterEnd; + } else { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + nsCOMPtr destination; + if (position == eBeforeBegin || position == eAfterEnd) { + destination = GetParent(); + if (!destination) { + return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; + } + } else { + destination = this; + } + + nsIDocument* doc = GetOwnerDoc(); + NS_ENSURE_STATE(doc); + + // Needed when insertAdjacentHTML is used in combination with contenteditable + mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, PR_TRUE); + + // Batch possible DOMSubtreeModified events. + mozAutoSubtreeModified subtree(doc, nsnull); + + // Parse directly into destination if possible + if (doc->IsHTML() && + (position == eBeforeEnd || + (position == eAfterEnd && !GetNextSibling()) || + (position == eAfterBegin && !GetFirstChild()))) { + PRInt32 oldChildCount = destination->GetChildCount(); + PRInt32 contextNs = destination->GetNameSpaceID(); + nsIAtom* contextLocal = destination->Tag(); + if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) { + // For compat with IE6 through IE9. Willful violation of HTML5 as of + // 2011-04-06. CreateContextualFragment does the same already. + // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434 + contextLocal = nsGkAtoms::body; + } + nsContentUtils::ParseFragmentHTML(aText, + destination, + contextLocal, + contextNs, + doc->GetCompatibilityMode() == + eCompatibility_NavQuirks, + PR_TRUE); + // HTML5 parser has notified, but not fired mutation events. + FireMutationEventsForDirectParsing(doc, destination, oldChildCount); + return NS_OK; + } + + // couldn't parse directly + nsCOMPtr df; + nsresult rv = nsContentUtils::CreateContextualFragment(destination, + aText, + PR_TRUE, + getter_AddRefs(df)); + nsCOMPtr fragment = do_QueryInterface(df); + NS_ENSURE_SUCCESS(rv, rv); + + switch (position) { + case eBeforeBegin: + destination->InsertBefore(fragment, this, &rv); + break; + case eAfterBegin: + static_cast(this)->InsertBefore(fragment, GetFirstChild(), &rv); + break; + case eBeforeEnd: + static_cast(this)->AppendChild(fragment, &rv); + break; + case eAfterEnd: + destination->InsertBefore(fragment, GetNextSibling(), &rv); + break; + default: + NS_NOTREACHED("Bad position."); + break; + } + return rv; +} + nsresult nsGenericHTMLElement::ScrollIntoView(PRBool aTop, PRUint8 optional_argc) { diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index c4c5313a2fbd..af428736e755 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -135,6 +135,8 @@ public: nsresult GetOffsetParent(nsIDOMElement** aOffsetParent); virtual nsresult GetInnerHTML(nsAString& aInnerHTML); virtual nsresult SetInnerHTML(const nsAString& aInnerHTML); + virtual nsresult InsertAdjacentHTML(const nsAString& aPosition, + const nsAString& aText); nsresult ScrollIntoView(PRBool aTop, PRUint8 optional_argc); // Declare Focus(), Blur(), GetTabIndex(), SetTabIndex(), GetHidden(), // SetHidden(), GetSpellcheck(), SetSpellcheck(), and GetDraggable() such that @@ -571,6 +573,18 @@ protected: } private: + /** + * Fire mutation events for changes caused by parsing directly into a + * context node. + * + * @param aDoc the document of the node + * @param aDest the destination node that got stuff appended to it + * @param aOldChildCount the number of children the node had before parsing + */ + void FireMutationEventsForDirectParsing(nsIDocument* aDoc, + nsIContent* aDest, + PRInt32 aOldChildCount); + void RegUnRegAccessKey(PRBool aDoReg); protected: diff --git a/dom/interfaces/html/nsIDOMNSHTMLElement.idl b/dom/interfaces/html/nsIDOMNSHTMLElement.idl index 88dbedb3265a..0000b619d2ec 100644 --- a/dom/interfaces/html/nsIDOMNSHTMLElement.idl +++ b/dom/interfaces/html/nsIDOMNSHTMLElement.idl @@ -40,7 +40,7 @@ interface nsIDOMDOMStringMap; -[scriptable, uuid(cf76761d-2d93-4e88-aaf1-b637878bad65)] +[scriptable, uuid(4012e9a9-f6fb-48b3-9a80-b096c1dcb5ba)] interface nsIDOMNSHTMLElement : nsISupports { readonly attribute long offsetTop; @@ -65,6 +65,10 @@ interface nsIDOMNSHTMLElement : nsISupports // for WHAT-WG drag and drop attribute boolean draggable; + void insertAdjacentHTML(in DOMString position, + in DOMString text) + raises(DOMException); + [optional_argc] void scrollIntoView([optional] in boolean top); attribute boolean spellcheck;