michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=78: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsHtml5TreeOperation.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsDocElementCreatedNotificationRunner.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsAttrName.h" michael@0: #include "nsHtml5TreeBuilder.h" michael@0: #include "nsIDOMMutationEvent.h" michael@0: #include "mozAutoDocUpdate.h" michael@0: #include "nsBindingManager.h" michael@0: #include "nsXBLBinding.h" michael@0: #include "nsHtml5DocumentMode.h" michael@0: #include "nsHtml5HtmlAttributes.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsIScriptElement.h" michael@0: #include "nsIDTD.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include "nsIDOMHTMLFormElement.h" michael@0: #include "nsIFormControl.h" michael@0: #include "nsIStyleSheetLinkingElement.h" michael@0: #include "nsIDOMDocumentType.h" michael@0: #include "nsIObserverService.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsIMutationObserver.h" michael@0: #include "nsIFormProcessor.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsEscape.h" michael@0: #include "mozilla/dom/Comment.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/HTMLImageElement.h" michael@0: #include "mozilla/dom/HTMLTemplateElement.h" michael@0: #include "nsHtml5SVGLoadDispatcher.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIProtocolHandler.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIHTMLDocument.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "nsTextNode.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID); michael@0: michael@0: /** michael@0: * Helper class that opens a notification batch if the current doc michael@0: * is different from the executor doc. michael@0: */ michael@0: class MOZ_STACK_CLASS nsHtml5OtherDocUpdate { michael@0: public: michael@0: nsHtml5OtherDocUpdate(nsIDocument* aCurrentDoc, nsIDocument* aExecutorDoc) michael@0: { michael@0: NS_PRECONDITION(aCurrentDoc, "Node has no doc?"); michael@0: NS_PRECONDITION(aExecutorDoc, "Executor has no doc?"); michael@0: if (MOZ_LIKELY(aCurrentDoc == aExecutorDoc)) { michael@0: mDocument = nullptr; michael@0: } else { michael@0: mDocument = aCurrentDoc; michael@0: aCurrentDoc->BeginUpdate(UPDATE_CONTENT_MODEL); michael@0: } michael@0: } michael@0: michael@0: ~nsHtml5OtherDocUpdate() michael@0: { michael@0: if (MOZ_UNLIKELY(mDocument)) { michael@0: mDocument->EndUpdate(UPDATE_CONTENT_MODEL); michael@0: } michael@0: } michael@0: private: michael@0: nsIDocument* mDocument; michael@0: }; michael@0: michael@0: nsHtml5TreeOperation::nsHtml5TreeOperation() michael@0: #ifdef DEBUG michael@0: : mOpCode(eTreeOpUninitialized) michael@0: #endif michael@0: { michael@0: MOZ_COUNT_CTOR(nsHtml5TreeOperation); michael@0: } michael@0: michael@0: nsHtml5TreeOperation::~nsHtml5TreeOperation() michael@0: { michael@0: MOZ_COUNT_DTOR(nsHtml5TreeOperation); michael@0: NS_ASSERTION(mOpCode != eTreeOpUninitialized, "Uninitialized tree op."); michael@0: switch(mOpCode) { michael@0: case eTreeOpAddAttributes: michael@0: delete mTwo.attributes; michael@0: break; michael@0: case eTreeOpCreateElementNetwork: michael@0: case eTreeOpCreateElementNotNetwork: michael@0: delete mThree.attributes; michael@0: break; michael@0: case eTreeOpAppendDoctypeToDocument: michael@0: delete mTwo.stringPair; michael@0: break; michael@0: case eTreeOpFosterParentText: michael@0: case eTreeOpAppendText: michael@0: case eTreeOpAppendComment: michael@0: case eTreeOpAppendCommentToDocument: michael@0: case eTreeOpAddViewSourceHref: michael@0: delete[] mTwo.unicharPtr; michael@0: break; michael@0: case eTreeOpSetDocumentCharset: michael@0: case eTreeOpNeedsCharsetSwitchTo: michael@0: delete[] mOne.charPtr; michael@0: break; michael@0: case eTreeOpProcessOfflineManifest: michael@0: nsMemory::Free(mOne.unicharPtr); michael@0: break; michael@0: default: // keep the compiler happy michael@0: break; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendTextToTextNode(const char16_t* aBuffer, michael@0: uint32_t aLength, michael@0: nsIContent* aTextNode, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: NS_PRECONDITION(aTextNode, "Got null text node."); michael@0: michael@0: if (aBuilder->HaveNotified(aTextNode)) { michael@0: // This text node has already been notified on, so it's necessary to michael@0: // notify on the append michael@0: nsresult rv = NS_OK; michael@0: uint32_t oldLength = aTextNode->TextLength(); michael@0: CharacterDataChangeInfo info = { michael@0: true, michael@0: oldLength, michael@0: oldLength, michael@0: aLength michael@0: }; michael@0: nsNodeUtils::CharacterDataWillChange(aTextNode, &info); michael@0: michael@0: rv = aTextNode->AppendText(aBuffer, aLength, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsNodeUtils::CharacterDataChanged(aTextNode, &info); michael@0: return rv; michael@0: } michael@0: michael@0: return aTextNode->AppendText(aBuffer, aLength, false); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendText(const char16_t* aBuffer, michael@0: uint32_t aLength, michael@0: nsIContent* aParent, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: nsIContent* lastChild = aParent->GetLastChild(); michael@0: if (lastChild && lastChild->IsNodeOfType(nsINode::eTEXT)) { michael@0: nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), michael@0: aBuilder->GetDocument()); michael@0: return AppendTextToTextNode(aBuffer, michael@0: aLength, michael@0: lastChild, michael@0: aBuilder); michael@0: } michael@0: michael@0: nsRefPtr text = new nsTextNode(aBuilder->GetNodeInfoManager()); michael@0: NS_ASSERTION(text, "Infallible malloc failed?"); michael@0: rv = text->SetText(aBuffer, aLength, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return Append(text, aParent, aBuilder); michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::Append(nsIContent* aNode, michael@0: nsIContent* aParent, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: nsIDocument* executorDoc = aBuilder->GetDocument(); michael@0: NS_ASSERTION(executorDoc, "Null doc on executor"); michael@0: nsIDocument* parentDoc = aParent->OwnerDoc(); michael@0: NS_ASSERTION(parentDoc, "Null owner doc on old node."); michael@0: michael@0: if (MOZ_LIKELY(executorDoc == parentDoc)) { michael@0: // the usual case. the parent is in the parser's doc michael@0: rv = aParent->AppendChildTo(aNode, false); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: aBuilder->PostPendingAppendNotification(aParent, aNode); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: // The parent has been moved to another doc michael@0: parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL); michael@0: michael@0: uint32_t childCount = aParent->GetChildCount(); michael@0: rv = aParent->AppendChildTo(aNode, false); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsNodeUtils::ContentAppended(aParent, aNode, childCount); michael@0: } michael@0: parentDoc->EndUpdate(UPDATE_CONTENT_MODEL); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendToDocument(nsIContent* aNode, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: nsIDocument* doc = aBuilder->GetDocument(); michael@0: uint32_t childCount = doc->GetChildCount(); michael@0: rv = doc->AppendChildTo(aNode, false); michael@0: if (rv == NS_ERROR_DOM_HIERARCHY_REQUEST_ERR) { michael@0: return NS_OK; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsNodeUtils::ContentInserted(doc, aNode, childCount); michael@0: michael@0: NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), michael@0: "Someone forgot to block scripts"); michael@0: if (aNode->IsElement()) { michael@0: nsContentUtils::AddScriptRunner( michael@0: new nsDocElementCreatedNotificationRunner(doc)); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static bool michael@0: IsElementOrTemplateContent(nsINode* aNode) { michael@0: if (aNode) { michael@0: if (aNode->IsElement()) { michael@0: return true; michael@0: } else if (aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { michael@0: // Check if the node is a template content. michael@0: mozilla::dom::DocumentFragment* frag = michael@0: static_cast(aNode); michael@0: nsIContent* fragHost = frag->GetHost(); michael@0: if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::Detach(nsIContent* aNode, nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: nsCOMPtr parent = aNode->GetParentNode(); michael@0: if (parent) { michael@0: nsHtml5OtherDocUpdate update(parent->OwnerDoc(), michael@0: aBuilder->GetDocument()); michael@0: int32_t pos = parent->IndexOf(aNode); michael@0: NS_ASSERTION((pos >= 0), "Element not found as child of its parent"); michael@0: parent->RemoveChildAt(pos, true); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendChildrenToNewParent(nsIContent* aNode, michael@0: nsIContent* aParent, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: michael@0: nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), michael@0: aBuilder->GetDocument()); michael@0: michael@0: uint32_t childCount = aParent->GetChildCount(); michael@0: bool didAppend = false; michael@0: while (aNode->HasChildren()) { michael@0: nsCOMPtr child = aNode->GetFirstChild(); michael@0: aNode->RemoveChildAt(0, true); michael@0: nsresult rv = aParent->AppendChildTo(child, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: didAppend = true; michael@0: } michael@0: if (didAppend) { michael@0: nsNodeUtils::ContentAppended(aParent, aParent->GetChildAt(childCount), michael@0: childCount); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::FosterParent(nsIContent* aNode, michael@0: nsIContent* aParent, michael@0: nsIContent* aTable, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsIContent* foster = aTable->GetParent(); michael@0: michael@0: if (IsElementOrTemplateContent(foster)) { michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: michael@0: nsHtml5OtherDocUpdate update(foster->OwnerDoc(), michael@0: aBuilder->GetDocument()); michael@0: michael@0: uint32_t pos = foster->IndexOf(aTable); michael@0: nsresult rv = foster->InsertChildAt(aNode, pos, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsNodeUtils::ContentInserted(foster, aNode, pos); michael@0: return rv; michael@0: } michael@0: michael@0: return Append(aNode, aParent, aBuilder); michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AddAttributes(nsIContent* aNode, michael@0: nsHtml5HtmlAttributes* aAttributes, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: dom::Element* node = aNode->AsElement(); michael@0: nsHtml5OtherDocUpdate update(node->OwnerDoc(), michael@0: aBuilder->GetDocument()); michael@0: michael@0: int32_t len = aAttributes->getLength(); michael@0: for (int32_t i = len; i > 0;) { michael@0: --i; michael@0: // prefix doesn't need regetting. it is always null or a static atom michael@0: // local name is never null michael@0: nsCOMPtr localName = michael@0: Reget(aAttributes->getLocalNameNoBoundsCheck(i)); michael@0: int32_t nsuri = aAttributes->getURINoBoundsCheck(i); michael@0: if (!node->HasAttr(nsuri, localName)) { michael@0: // prefix doesn't need regetting. it is always null or a static atom michael@0: // local name is never null michael@0: node->SetAttr(nsuri, michael@0: localName, michael@0: aAttributes->getPrefixNoBoundsCheck(i), michael@0: *(aAttributes->getValueNoBoundsCheck(i)), michael@0: true); michael@0: // XXX what to do with nsresult? michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsIContent* michael@0: nsHtml5TreeOperation::CreateElement(int32_t aNs, michael@0: nsIAtom* aName, michael@0: nsHtml5HtmlAttributes* aAttributes, michael@0: mozilla::dom::FromParser aFromParser, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: bool isKeygen = (aName == nsHtml5Atoms::keygen && aNs == kNameSpaceID_XHTML); michael@0: if (MOZ_UNLIKELY(isKeygen)) { michael@0: aName = nsHtml5Atoms::select; michael@0: } michael@0: michael@0: nsCOMPtr newContent; michael@0: nsCOMPtr nodeInfo = aBuilder->GetNodeInfoManager()-> michael@0: GetNodeInfo(aName, nullptr, aNs, nsIDOMNode::ELEMENT_NODE); michael@0: NS_ASSERTION(nodeInfo, "Got null nodeinfo."); michael@0: NS_NewElement(getter_AddRefs(newContent), michael@0: nodeInfo.forget(), michael@0: aFromParser); michael@0: NS_ASSERTION(newContent, "Element creation created null pointer."); michael@0: michael@0: aBuilder->HoldElement(newContent); michael@0: michael@0: if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) { michael@0: nsCOMPtr ssle(do_QueryInterface(newContent)); michael@0: if (ssle) { michael@0: ssle->InitStyleLinkElement(false); michael@0: ssle->SetEnableUpdates(false); michael@0: } michael@0: } else if (MOZ_UNLIKELY(isKeygen)) { michael@0: // Adapted from CNavDTD michael@0: nsresult rv; michael@0: nsCOMPtr theFormProcessor = michael@0: do_GetService(kFormProcessorCID, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return newContent; michael@0: } michael@0: michael@0: nsTArray theContent; michael@0: nsAutoString theAttribute; michael@0: michael@0: (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"), michael@0: theContent, michael@0: theAttribute); michael@0: michael@0: newContent->SetAttr(kNameSpaceID_None, michael@0: nsGkAtoms::moztype, michael@0: nullptr, michael@0: theAttribute, michael@0: false); michael@0: michael@0: nsCOMPtr optionNodeInfo = michael@0: aBuilder->GetNodeInfoManager()->GetNodeInfo(nsHtml5Atoms::option, michael@0: nullptr, michael@0: kNameSpaceID_XHTML, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: michael@0: for (uint32_t i = 0; i < theContent.Length(); ++i) { michael@0: nsCOMPtr optionElt; michael@0: nsCOMPtr ni = optionNodeInfo; michael@0: NS_NewElement(getter_AddRefs(optionElt), michael@0: ni.forget(), michael@0: aFromParser); michael@0: nsRefPtr optionText = michael@0: new nsTextNode(aBuilder->GetNodeInfoManager()); michael@0: (void) optionText->SetText(theContent[i], false); michael@0: optionElt->AppendChildTo(optionText, false); michael@0: newContent->AppendChildTo(optionElt, false); michael@0: newContent->DoneAddingChildren(false); michael@0: } michael@0: } michael@0: michael@0: if (!aAttributes) { michael@0: return newContent; michael@0: } michael@0: michael@0: int32_t len = aAttributes->getLength(); michael@0: for (int32_t i = len; i > 0;) { michael@0: --i; michael@0: // prefix doesn't need regetting. it is always null or a static atom michael@0: // local name is never null michael@0: nsCOMPtr localName = michael@0: Reget(aAttributes->getLocalNameNoBoundsCheck(i)); michael@0: nsCOMPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); michael@0: int32_t nsuri = aAttributes->getURINoBoundsCheck(i); michael@0: michael@0: if (aNs == kNameSpaceID_XHTML && michael@0: nsHtml5Atoms::a == aName && michael@0: nsHtml5Atoms::name == localName) { michael@0: // This is an HTML5-incompliant Geckoism. michael@0: // Remove when fixing bug 582361 michael@0: NS_ConvertUTF16toUTF8 cname(*(aAttributes->getValueNoBoundsCheck(i))); michael@0: NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting())); michael@0: newContent->SetAttr(nsuri, michael@0: localName, michael@0: prefix, michael@0: uv, michael@0: false); michael@0: } else { michael@0: nsString& value = *(aAttributes->getValueNoBoundsCheck(i)); michael@0: newContent->SetAttr(nsuri, michael@0: localName, michael@0: prefix, michael@0: value, michael@0: false); michael@0: michael@0: // Custom element prototype swizzling may be needed if there is an michael@0: // "is" attribute. michael@0: if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) { michael@0: ErrorResult errorResult; michael@0: newContent->OwnerDoc()->SwizzleCustomElement(newContent, michael@0: value, michael@0: newContent->GetNameSpaceID(), michael@0: errorResult); michael@0: } michael@0: } michael@0: } michael@0: return newContent; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::SetFormElement(nsIContent* aNode, nsIContent* aParent) michael@0: { michael@0: nsCOMPtr formControl(do_QueryInterface(aNode)); michael@0: nsCOMPtr domImageElement = do_QueryInterface(aNode); michael@0: // NS_ASSERTION(formControl, "Form-associated element did not implement nsIFormControl."); michael@0: // TODO: uncomment the above line when (bug 101019) is supported by Gecko michael@0: nsCOMPtr formElement(do_QueryInterface(aParent)); michael@0: NS_ASSERTION(formElement, "The form element doesn't implement nsIDOMHTMLFormElement."); michael@0: // avoid crashing on michael@0: if (formControl && michael@0: !aNode->HasAttr(kNameSpaceID_None, nsGkAtoms::form)) { michael@0: formControl->SetForm(formElement); michael@0: } else if (domImageElement) { michael@0: nsRefPtr imageElement = michael@0: static_cast(domImageElement.get()); michael@0: MOZ_ASSERT(imageElement); michael@0: imageElement->SetForm(formElement); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendIsindexPrompt(nsIContent* parent, nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsXPIDLString prompt; michael@0: nsresult rv = michael@0: nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, michael@0: "IsIndexPromptWithSpace", prompt); michael@0: uint32_t len = prompt.Length(); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: if (!len) { michael@0: // Don't bother appending a zero-length text node. michael@0: return NS_OK; michael@0: } michael@0: return AppendText(prompt.BeginReading(), len, parent, aBuilder); michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::FosterParentText(nsIContent* aStackParent, michael@0: char16_t* aBuffer, michael@0: uint32_t aLength, michael@0: nsIContent* aTable, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: nsIContent* foster = aTable->GetParent(); michael@0: michael@0: if (IsElementOrTemplateContent(foster)) { michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: michael@0: nsHtml5OtherDocUpdate update(foster->OwnerDoc(), michael@0: aBuilder->GetDocument()); michael@0: michael@0: uint32_t pos = foster->IndexOf(aTable); michael@0: michael@0: nsIContent* previousSibling = aTable->GetPreviousSibling(); michael@0: if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) { michael@0: return AppendTextToTextNode(aBuffer, michael@0: aLength, michael@0: previousSibling, michael@0: aBuilder); michael@0: } michael@0: michael@0: nsRefPtr text = michael@0: new nsTextNode(aBuilder->GetNodeInfoManager()); michael@0: NS_ASSERTION(text, "Infallible malloc failed?"); michael@0: rv = text->SetText(aBuffer, aLength, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = foster->InsertChildAt(text, pos, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsNodeUtils::ContentInserted(foster, text, pos); michael@0: return rv; michael@0: } michael@0: michael@0: return AppendText(aBuffer, aLength, aStackParent, aBuilder); michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendComment(nsIContent* aParent, michael@0: char16_t* aBuffer, michael@0: int32_t aLength, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsRefPtr comment = michael@0: new dom::Comment(aBuilder->GetNodeInfoManager()); michael@0: NS_ASSERTION(comment, "Infallible malloc failed?"); michael@0: nsresult rv = comment->SetText(aBuffer, aLength, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return Append(comment, aParent, aBuilder); michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendCommentToDocument(char16_t* aBuffer, michael@0: int32_t aLength, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: nsRefPtr comment = michael@0: new dom::Comment(aBuilder->GetNodeInfoManager()); michael@0: NS_ASSERTION(comment, "Infallible malloc failed?"); michael@0: nsresult rv = comment->SetText(aBuffer, aLength, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return AppendToDocument(comment, aBuilder); michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::AppendDoctypeToDocument(nsIAtom* aName, michael@0: const nsAString& aPublicId, michael@0: const nsAString& aSystemId, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: // Adapted from nsXMLContentSink michael@0: // Create a new doctype node michael@0: nsCOMPtr docType; michael@0: nsAutoString voidString; michael@0: voidString.SetIsVoid(true); michael@0: NS_NewDOMDocumentType(getter_AddRefs(docType), michael@0: aBuilder->GetNodeInfoManager(), michael@0: aName, michael@0: aPublicId, michael@0: aSystemId, michael@0: voidString); michael@0: NS_ASSERTION(docType, "Doctype creation failed."); michael@0: nsCOMPtr asContent = do_QueryInterface(docType); michael@0: return AppendToDocument(asContent, aBuilder); michael@0: } michael@0: michael@0: nsIContent* michael@0: nsHtml5TreeOperation::GetDocumentFragmentForTemplate(nsIContent* aNode) michael@0: { michael@0: dom::HTMLTemplateElement* tempElem = michael@0: static_cast(aNode); michael@0: nsRefPtr frag = tempElem->Content(); michael@0: return frag; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::PreventScriptExecution(nsIContent* aNode) michael@0: { michael@0: nsCOMPtr sele = do_QueryInterface(aNode); michael@0: MOZ_ASSERT(sele); michael@0: sele->PreventExecution(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::DoneAddingChildren(nsIContent* aNode, michael@0: nsHtml5DocumentBuilder* aBuilder) michael@0: { michael@0: aNode->DoneAddingChildren(aBuilder->HaveNotified(aNode)); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::DoneCreatingElement(nsIContent* aNode) michael@0: { michael@0: aNode->DoneCreatingElement(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::SvgLoad(nsIContent* aNode) michael@0: { michael@0: nsCOMPtr event = new nsHtml5SVGLoadDispatcher(aNode); michael@0: if (NS_FAILED(NS_DispatchToMainThread(event))) { michael@0: NS_WARNING("failed to dispatch svg load dispatcher"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeOperation::MarkMalformedIfScript(nsIContent* aNode) michael@0: { michael@0: nsCOMPtr sele = do_QueryInterface(aNode); michael@0: if (sele) { michael@0: // Make sure to serialize this script correctly, for nice round tripping. michael@0: sele->SetIsMalformed(); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, michael@0: nsIContent** aScriptElement) michael@0: { michael@0: switch(mOpCode) { michael@0: case eTreeOpAppend: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsIContent* parent = *(mTwo.node); michael@0: return Append(node, parent, aBuilder); michael@0: } michael@0: case eTreeOpDetach: { michael@0: nsIContent* node = *(mOne.node); michael@0: Detach(node, aBuilder); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpAppendChildrenToNewParent: { michael@0: nsCOMPtr node = *(mOne.node); michael@0: nsIContent* parent = *(mTwo.node); michael@0: return AppendChildrenToNewParent(node, parent, aBuilder); michael@0: } michael@0: case eTreeOpFosterParent: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsIContent* parent = *(mTwo.node); michael@0: nsIContent* table = *(mThree.node); michael@0: return FosterParent(node, parent, table, aBuilder); michael@0: } michael@0: case eTreeOpAppendToDocument: { michael@0: nsIContent* node = *(mOne.node); michael@0: return AppendToDocument(node, aBuilder); michael@0: } michael@0: case eTreeOpAddAttributes: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsHtml5HtmlAttributes* attributes = mTwo.attributes; michael@0: return AddAttributes(node, attributes, aBuilder); michael@0: } michael@0: case eTreeOpCreateElementNetwork: michael@0: case eTreeOpCreateElementNotNetwork: { michael@0: nsIContent** target = mOne.node; michael@0: int32_t ns = mFour.integer; michael@0: nsCOMPtr name = Reget(mTwo.atom); michael@0: nsHtml5HtmlAttributes* attributes = mThree.attributes; michael@0: michael@0: *target = CreateElement(ns, michael@0: name, michael@0: attributes, michael@0: mOpCode == eTreeOpCreateElementNetwork ? michael@0: dom::FROM_PARSER_NETWORK : michael@0: dom::FROM_PARSER_DOCUMENT_WRITE, michael@0: aBuilder); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpSetFormElement: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsIContent* parent = *(mTwo.node); michael@0: SetFormElement(node, parent); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpAppendText: { michael@0: nsIContent* parent = *mOne.node; michael@0: char16_t* buffer = mTwo.unicharPtr; michael@0: uint32_t length = mFour.integer; michael@0: return AppendText(buffer, length, parent, aBuilder); michael@0: } michael@0: case eTreeOpAppendIsindexPrompt: { michael@0: nsIContent* parent = *mOne.node; michael@0: return AppendIsindexPrompt(parent, aBuilder); michael@0: } michael@0: case eTreeOpFosterParentText: { michael@0: nsIContent* stackParent = *mOne.node; michael@0: char16_t* buffer = mTwo.unicharPtr; michael@0: uint32_t length = mFour.integer; michael@0: nsIContent* table = *mThree.node; michael@0: return FosterParentText(stackParent, buffer, length, table, aBuilder); michael@0: } michael@0: case eTreeOpAppendComment: { michael@0: nsIContent* parent = *mOne.node; michael@0: char16_t* buffer = mTwo.unicharPtr; michael@0: int32_t length = mFour.integer; michael@0: return AppendComment(parent, buffer, length, aBuilder); michael@0: } michael@0: case eTreeOpAppendCommentToDocument: { michael@0: char16_t* buffer = mTwo.unicharPtr; michael@0: int32_t length = mFour.integer; michael@0: return AppendCommentToDocument(buffer, length, aBuilder); michael@0: } michael@0: case eTreeOpAppendDoctypeToDocument: { michael@0: nsCOMPtr name = Reget(mOne.atom); michael@0: nsHtml5TreeOperationStringPair* pair = mTwo.stringPair; michael@0: nsString publicId; michael@0: nsString systemId; michael@0: pair->Get(publicId, systemId); michael@0: return AppendDoctypeToDocument(name, publicId, systemId, aBuilder); michael@0: } michael@0: case eTreeOpGetDocumentFragmentForTemplate: { michael@0: nsIContent* node = *(mOne.node); michael@0: *mTwo.node = GetDocumentFragmentForTemplate(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpMarkAsBroken: { michael@0: return mOne.result; michael@0: } michael@0: case eTreeOpRunScript: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsAHtml5TreeBuilderState* snapshot = mTwo.state; michael@0: if (snapshot) { michael@0: aBuilder->InitializeDocWriteParserState(snapshot, mFour.integer); michael@0: } michael@0: *aScriptElement = node; michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpRunScriptAsyncDefer: { michael@0: nsIContent* node = *(mOne.node); michael@0: aBuilder->RunScript(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpPreventScriptExecution: { michael@0: nsIContent* node = *(mOne.node); michael@0: PreventScriptExecution(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpDoneAddingChildren: { michael@0: nsIContent* node = *(mOne.node); michael@0: node->DoneAddingChildren(aBuilder->HaveNotified(node)); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpDoneCreatingElement: { michael@0: nsIContent* node = *(mOne.node); michael@0: DoneCreatingElement(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpFlushPendingAppendNotifications: { michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpSetDocumentCharset: { michael@0: char* str = mOne.charPtr; michael@0: int32_t charsetSource = mFour.integer; michael@0: nsDependentCString dependentString(str); michael@0: aBuilder->SetDocumentCharsetAndSource(dependentString, charsetSource); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpNeedsCharsetSwitchTo: { michael@0: char* str = mOne.charPtr; michael@0: int32_t charsetSource = mFour.integer; michael@0: int32_t lineNumber = mTwo.integer; michael@0: aBuilder->NeedsCharsetSwitchTo(str, charsetSource, (uint32_t)lineNumber); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpUpdateStyleSheet: { michael@0: nsIContent* node = *(mOne.node); michael@0: aBuilder->FlushPendingAppendNotifications(); michael@0: aBuilder->UpdateStyleSheet(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpProcessMeta: { michael@0: nsIContent* node = *(mOne.node); michael@0: return aBuilder->ProcessMETATag(node); michael@0: } michael@0: case eTreeOpProcessOfflineManifest: { michael@0: char16_t* str = mOne.unicharPtr; michael@0: nsDependentString dependentString(str); michael@0: aBuilder->ProcessOfflineManifest(dependentString); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpMarkMalformedIfScript: { michael@0: nsIContent* node = *(mOne.node); michael@0: MarkMalformedIfScript(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpStreamEnded: { michael@0: aBuilder->DidBuildModel(false); // this causes a notifications flush anyway michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpStartLayout: { michael@0: aBuilder->StartLayout(); // this causes a notification flush anyway michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpDocumentMode: { michael@0: aBuilder->SetDocumentMode(mOne.mode); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpSetStyleLineNumber: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsCOMPtr ssle = do_QueryInterface(node); michael@0: NS_ASSERTION(ssle, "Node didn't QI to style."); michael@0: ssle->SetLineNumber(mFour.integer); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpSetScriptLineNumberAndFreeze: { michael@0: nsIContent* node = *(mOne.node); michael@0: nsCOMPtr sele = do_QueryInterface(node); michael@0: NS_ASSERTION(sele, "Node didn't QI to script."); michael@0: sele->SetScriptLineNumber(mFour.integer); michael@0: sele->FreezeUriAsyncDefer(); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpSvgLoad: { michael@0: nsIContent* node = *(mOne.node); michael@0: SvgLoad(node); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpMaybeComplainAboutCharset: { michael@0: char* msgId = mOne.charPtr; michael@0: bool error = mTwo.integer; michael@0: int32_t lineNumber = mThree.integer; michael@0: aBuilder->MaybeComplainAboutCharset(msgId, error, (uint32_t)lineNumber); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpAddClass: { michael@0: nsIContent* node = *(mOne.node); michael@0: char16_t* str = mTwo.unicharPtr; michael@0: nsDependentString depStr(str); michael@0: // See viewsource.css for the possible classes michael@0: nsAutoString klass; michael@0: node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass); michael@0: if (!klass.IsEmpty()) { michael@0: klass.Append(' '); michael@0: klass.Append(depStr); michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true); michael@0: } else { michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpAddLineNumberId: { michael@0: nsIContent* node = *(mOne.node); michael@0: int32_t lineNumber = mFour.integer; michael@0: nsAutoString val(NS_LITERAL_STRING("line")); michael@0: val.AppendInt(lineNumber); michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::id, val, true); michael@0: return NS_OK; michael@0: } michael@0: case eTreeOpAddViewSourceHref: { michael@0: nsIContent* node = *mOne.node; michael@0: char16_t* buffer = mTwo.unicharPtr; michael@0: int32_t length = mFour.integer; michael@0: michael@0: nsDependentString relative(buffer, length); michael@0: michael@0: nsIDocument* doc = aBuilder->GetDocument(); michael@0: michael@0: const nsCString& charset = doc->GetDocumentCharacterSet(); michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), michael@0: relative, michael@0: charset.get(), michael@0: aBuilder->GetViewSourceBaseURI()); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: michael@0: // Reuse the fix for bug 467852 michael@0: // URLs that execute script (e.g. "javascript:" URLs) should just be michael@0: // ignored. There's nothing reasonable we can do with them, and allowing michael@0: // them to execute in the context of the view-source window presents a michael@0: // security risk. Just return the empty string in this case. michael@0: bool openingExecutesScript = false; michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, michael@0: &openingExecutesScript); michael@0: if (NS_FAILED(rv) || openingExecutesScript) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoCString viewSourceUrl; michael@0: michael@0: // URLs that return data (e.g. "http:" URLs) should be prefixed with michael@0: // "view-source:". URLs that don't return data should just be returned michael@0: // undecorated. michael@0: bool doesNotReturnData = false; michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, michael@0: &doesNotReturnData); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: if (!doesNotReturnData) { michael@0: viewSourceUrl.AssignLiteral("view-source:"); michael@0: } michael@0: michael@0: nsAutoCString spec; michael@0: uri->GetSpec(spec); michael@0: michael@0: viewSourceUrl.Append(spec); michael@0: michael@0: nsAutoString utf16; michael@0: CopyUTF8toUTF16(viewSourceUrl, utf16); michael@0: michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::href, utf16, true); michael@0: return rv; michael@0: } michael@0: case eTreeOpAddError: { michael@0: nsIContent* node = *(mOne.node); michael@0: char* msgId = mTwo.charPtr; michael@0: nsCOMPtr atom = Reget(mThree.atom); michael@0: nsCOMPtr otherAtom = Reget(mFour.atom); michael@0: // See viewsource.css for the possible classes in addition to "error". michael@0: nsAutoString klass; michael@0: node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass); michael@0: if (!klass.IsEmpty()) { michael@0: klass.Append(NS_LITERAL_STRING(" error")); michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true); michael@0: } else { michael@0: node->SetAttr(kNameSpaceID_None, michael@0: nsGkAtoms::_class, michael@0: NS_LITERAL_STRING("error"), michael@0: true); michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsXPIDLString message; michael@0: if (otherAtom) { michael@0: const char16_t* params[] = { atom->GetUTF16String(), michael@0: otherAtom->GetUTF16String() }; michael@0: rv = nsContentUtils::FormatLocalizedString( michael@0: nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: } else if (atom) { michael@0: const char16_t* params[] = { atom->GetUTF16String() }; michael@0: rv = nsContentUtils::FormatLocalizedString( michael@0: nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: } else { michael@0: rv = nsContentUtils::GetLocalizedString( michael@0: nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, message); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: } michael@0: michael@0: nsAutoString title; michael@0: node->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title); michael@0: if (!title.IsEmpty()) { michael@0: title.Append('\n'); michael@0: title.Append(message); michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, title, true); michael@0: } else { michael@0: node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, message, true); michael@0: } michael@0: return rv; michael@0: } michael@0: default: { michael@0: NS_NOTREACHED("Bogus tree op"); michael@0: } michael@0: } michael@0: return NS_OK; // keep compiler happy michael@0: }