Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsHtml5TreeOperation.h"
8 #include "nsContentUtils.h"
9 #include "nsDocElementCreatedNotificationRunner.h"
10 #include "nsNodeUtils.h"
11 #include "nsAttrName.h"
12 #include "nsHtml5TreeBuilder.h"
13 #include "nsIDOMMutationEvent.h"
14 #include "mozAutoDocUpdate.h"
15 #include "nsBindingManager.h"
16 #include "nsXBLBinding.h"
17 #include "nsHtml5DocumentMode.h"
18 #include "nsHtml5HtmlAttributes.h"
19 #include "nsContentCreatorFunctions.h"
20 #include "nsIScriptElement.h"
21 #include "nsIDTD.h"
22 #include "nsISupportsImpl.h"
23 #include "nsIDOMHTMLFormElement.h"
24 #include "nsIFormControl.h"
25 #include "nsIStyleSheetLinkingElement.h"
26 #include "nsIDOMDocumentType.h"
27 #include "nsIObserverService.h"
28 #include "mozilla/Services.h"
29 #include "nsIMutationObserver.h"
30 #include "nsIFormProcessor.h"
31 #include "nsIServiceManager.h"
32 #include "nsEscape.h"
33 #include "mozilla/dom/Comment.h"
34 #include "mozilla/dom/Element.h"
35 #include "mozilla/dom/HTMLImageElement.h"
36 #include "mozilla/dom/HTMLTemplateElement.h"
37 #include "nsHtml5SVGLoadDispatcher.h"
38 #include "nsIURI.h"
39 #include "nsIProtocolHandler.h"
40 #include "nsNetUtil.h"
41 #include "nsIHTMLDocument.h"
42 #include "mozilla/Likely.h"
43 #include "nsTextNode.h"
45 using namespace mozilla;
47 static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
49 /**
50 * Helper class that opens a notification batch if the current doc
51 * is different from the executor doc.
52 */
53 class MOZ_STACK_CLASS nsHtml5OtherDocUpdate {
54 public:
55 nsHtml5OtherDocUpdate(nsIDocument* aCurrentDoc, nsIDocument* aExecutorDoc)
56 {
57 NS_PRECONDITION(aCurrentDoc, "Node has no doc?");
58 NS_PRECONDITION(aExecutorDoc, "Executor has no doc?");
59 if (MOZ_LIKELY(aCurrentDoc == aExecutorDoc)) {
60 mDocument = nullptr;
61 } else {
62 mDocument = aCurrentDoc;
63 aCurrentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
64 }
65 }
67 ~nsHtml5OtherDocUpdate()
68 {
69 if (MOZ_UNLIKELY(mDocument)) {
70 mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
71 }
72 }
73 private:
74 nsIDocument* mDocument;
75 };
77 nsHtml5TreeOperation::nsHtml5TreeOperation()
78 #ifdef DEBUG
79 : mOpCode(eTreeOpUninitialized)
80 #endif
81 {
82 MOZ_COUNT_CTOR(nsHtml5TreeOperation);
83 }
85 nsHtml5TreeOperation::~nsHtml5TreeOperation()
86 {
87 MOZ_COUNT_DTOR(nsHtml5TreeOperation);
88 NS_ASSERTION(mOpCode != eTreeOpUninitialized, "Uninitialized tree op.");
89 switch(mOpCode) {
90 case eTreeOpAddAttributes:
91 delete mTwo.attributes;
92 break;
93 case eTreeOpCreateElementNetwork:
94 case eTreeOpCreateElementNotNetwork:
95 delete mThree.attributes;
96 break;
97 case eTreeOpAppendDoctypeToDocument:
98 delete mTwo.stringPair;
99 break;
100 case eTreeOpFosterParentText:
101 case eTreeOpAppendText:
102 case eTreeOpAppendComment:
103 case eTreeOpAppendCommentToDocument:
104 case eTreeOpAddViewSourceHref:
105 delete[] mTwo.unicharPtr;
106 break;
107 case eTreeOpSetDocumentCharset:
108 case eTreeOpNeedsCharsetSwitchTo:
109 delete[] mOne.charPtr;
110 break;
111 case eTreeOpProcessOfflineManifest:
112 nsMemory::Free(mOne.unicharPtr);
113 break;
114 default: // keep the compiler happy
115 break;
116 }
117 }
119 nsresult
120 nsHtml5TreeOperation::AppendTextToTextNode(const char16_t* aBuffer,
121 uint32_t aLength,
122 nsIContent* aTextNode,
123 nsHtml5DocumentBuilder* aBuilder)
124 {
125 NS_PRECONDITION(aTextNode, "Got null text node.");
127 if (aBuilder->HaveNotified(aTextNode)) {
128 // This text node has already been notified on, so it's necessary to
129 // notify on the append
130 nsresult rv = NS_OK;
131 uint32_t oldLength = aTextNode->TextLength();
132 CharacterDataChangeInfo info = {
133 true,
134 oldLength,
135 oldLength,
136 aLength
137 };
138 nsNodeUtils::CharacterDataWillChange(aTextNode, &info);
140 rv = aTextNode->AppendText(aBuffer, aLength, false);
141 NS_ENSURE_SUCCESS(rv, rv);
143 nsNodeUtils::CharacterDataChanged(aTextNode, &info);
144 return rv;
145 }
147 return aTextNode->AppendText(aBuffer, aLength, false);
148 }
151 nsresult
152 nsHtml5TreeOperation::AppendText(const char16_t* aBuffer,
153 uint32_t aLength,
154 nsIContent* aParent,
155 nsHtml5DocumentBuilder* aBuilder)
156 {
157 nsresult rv = NS_OK;
158 nsIContent* lastChild = aParent->GetLastChild();
159 if (lastChild && lastChild->IsNodeOfType(nsINode::eTEXT)) {
160 nsHtml5OtherDocUpdate update(aParent->OwnerDoc(),
161 aBuilder->GetDocument());
162 return AppendTextToTextNode(aBuffer,
163 aLength,
164 lastChild,
165 aBuilder);
166 }
168 nsRefPtr<nsTextNode> text = new nsTextNode(aBuilder->GetNodeInfoManager());
169 NS_ASSERTION(text, "Infallible malloc failed?");
170 rv = text->SetText(aBuffer, aLength, false);
171 NS_ENSURE_SUCCESS(rv, rv);
173 return Append(text, aParent, aBuilder);
174 }
176 nsresult
177 nsHtml5TreeOperation::Append(nsIContent* aNode,
178 nsIContent* aParent,
179 nsHtml5DocumentBuilder* aBuilder)
180 {
181 nsresult rv = NS_OK;
182 nsIDocument* executorDoc = aBuilder->GetDocument();
183 NS_ASSERTION(executorDoc, "Null doc on executor");
184 nsIDocument* parentDoc = aParent->OwnerDoc();
185 NS_ASSERTION(parentDoc, "Null owner doc on old node.");
187 if (MOZ_LIKELY(executorDoc == parentDoc)) {
188 // the usual case. the parent is in the parser's doc
189 rv = aParent->AppendChildTo(aNode, false);
190 if (NS_SUCCEEDED(rv)) {
191 aBuilder->PostPendingAppendNotification(aParent, aNode);
192 }
193 return rv;
194 }
196 // The parent has been moved to another doc
197 parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
199 uint32_t childCount = aParent->GetChildCount();
200 rv = aParent->AppendChildTo(aNode, false);
201 if (NS_SUCCEEDED(rv)) {
202 nsNodeUtils::ContentAppended(aParent, aNode, childCount);
203 }
204 parentDoc->EndUpdate(UPDATE_CONTENT_MODEL);
205 return rv;
206 }
208 nsresult
209 nsHtml5TreeOperation::AppendToDocument(nsIContent* aNode,
210 nsHtml5DocumentBuilder* aBuilder)
211 {
212 nsresult rv = NS_OK;
213 aBuilder->FlushPendingAppendNotifications();
214 nsIDocument* doc = aBuilder->GetDocument();
215 uint32_t childCount = doc->GetChildCount();
216 rv = doc->AppendChildTo(aNode, false);
217 if (rv == NS_ERROR_DOM_HIERARCHY_REQUEST_ERR) {
218 return NS_OK;
219 }
220 NS_ENSURE_SUCCESS(rv, rv);
221 nsNodeUtils::ContentInserted(doc, aNode, childCount);
223 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
224 "Someone forgot to block scripts");
225 if (aNode->IsElement()) {
226 nsContentUtils::AddScriptRunner(
227 new nsDocElementCreatedNotificationRunner(doc));
228 }
229 return rv;
230 }
232 static bool
233 IsElementOrTemplateContent(nsINode* aNode) {
234 if (aNode) {
235 if (aNode->IsElement()) {
236 return true;
237 } else if (aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
238 // Check if the node is a template content.
239 mozilla::dom::DocumentFragment* frag =
240 static_cast<mozilla::dom::DocumentFragment*>(aNode);
241 nsIContent* fragHost = frag->GetHost();
242 if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
243 return true;
244 }
245 }
246 }
247 return false;
248 }
250 void
251 nsHtml5TreeOperation::Detach(nsIContent* aNode, nsHtml5DocumentBuilder* aBuilder)
252 {
253 aBuilder->FlushPendingAppendNotifications();
254 nsCOMPtr<nsINode> parent = aNode->GetParentNode();
255 if (parent) {
256 nsHtml5OtherDocUpdate update(parent->OwnerDoc(),
257 aBuilder->GetDocument());
258 int32_t pos = parent->IndexOf(aNode);
259 NS_ASSERTION((pos >= 0), "Element not found as child of its parent");
260 parent->RemoveChildAt(pos, true);
261 }
262 }
264 nsresult
265 nsHtml5TreeOperation::AppendChildrenToNewParent(nsIContent* aNode,
266 nsIContent* aParent,
267 nsHtml5DocumentBuilder* aBuilder)
268 {
269 aBuilder->FlushPendingAppendNotifications();
271 nsHtml5OtherDocUpdate update(aParent->OwnerDoc(),
272 aBuilder->GetDocument());
274 uint32_t childCount = aParent->GetChildCount();
275 bool didAppend = false;
276 while (aNode->HasChildren()) {
277 nsCOMPtr<nsIContent> child = aNode->GetFirstChild();
278 aNode->RemoveChildAt(0, true);
279 nsresult rv = aParent->AppendChildTo(child, false);
280 NS_ENSURE_SUCCESS(rv, rv);
281 didAppend = true;
282 }
283 if (didAppend) {
284 nsNodeUtils::ContentAppended(aParent, aParent->GetChildAt(childCount),
285 childCount);
286 }
287 return NS_OK;
288 }
290 nsresult
291 nsHtml5TreeOperation::FosterParent(nsIContent* aNode,
292 nsIContent* aParent,
293 nsIContent* aTable,
294 nsHtml5DocumentBuilder* aBuilder)
295 {
296 nsIContent* foster = aTable->GetParent();
298 if (IsElementOrTemplateContent(foster)) {
299 aBuilder->FlushPendingAppendNotifications();
301 nsHtml5OtherDocUpdate update(foster->OwnerDoc(),
302 aBuilder->GetDocument());
304 uint32_t pos = foster->IndexOf(aTable);
305 nsresult rv = foster->InsertChildAt(aNode, pos, false);
306 NS_ENSURE_SUCCESS(rv, rv);
307 nsNodeUtils::ContentInserted(foster, aNode, pos);
308 return rv;
309 }
311 return Append(aNode, aParent, aBuilder);
312 }
314 nsresult
315 nsHtml5TreeOperation::AddAttributes(nsIContent* aNode,
316 nsHtml5HtmlAttributes* aAttributes,
317 nsHtml5DocumentBuilder* aBuilder)
318 {
319 dom::Element* node = aNode->AsElement();
320 nsHtml5OtherDocUpdate update(node->OwnerDoc(),
321 aBuilder->GetDocument());
323 int32_t len = aAttributes->getLength();
324 for (int32_t i = len; i > 0;) {
325 --i;
326 // prefix doesn't need regetting. it is always null or a static atom
327 // local name is never null
328 nsCOMPtr<nsIAtom> localName =
329 Reget(aAttributes->getLocalNameNoBoundsCheck(i));
330 int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
331 if (!node->HasAttr(nsuri, localName)) {
332 // prefix doesn't need regetting. it is always null or a static atom
333 // local name is never null
334 node->SetAttr(nsuri,
335 localName,
336 aAttributes->getPrefixNoBoundsCheck(i),
337 *(aAttributes->getValueNoBoundsCheck(i)),
338 true);
339 // XXX what to do with nsresult?
340 }
341 }
342 return NS_OK;
343 }
346 nsIContent*
347 nsHtml5TreeOperation::CreateElement(int32_t aNs,
348 nsIAtom* aName,
349 nsHtml5HtmlAttributes* aAttributes,
350 mozilla::dom::FromParser aFromParser,
351 nsHtml5DocumentBuilder* aBuilder)
352 {
353 bool isKeygen = (aName == nsHtml5Atoms::keygen && aNs == kNameSpaceID_XHTML);
354 if (MOZ_UNLIKELY(isKeygen)) {
355 aName = nsHtml5Atoms::select;
356 }
358 nsCOMPtr<dom::Element> newContent;
359 nsCOMPtr<nsINodeInfo> nodeInfo = aBuilder->GetNodeInfoManager()->
360 GetNodeInfo(aName, nullptr, aNs, nsIDOMNode::ELEMENT_NODE);
361 NS_ASSERTION(nodeInfo, "Got null nodeinfo.");
362 NS_NewElement(getter_AddRefs(newContent),
363 nodeInfo.forget(),
364 aFromParser);
365 NS_ASSERTION(newContent, "Element creation created null pointer.");
367 aBuilder->HoldElement(newContent);
369 if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
370 nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
371 if (ssle) {
372 ssle->InitStyleLinkElement(false);
373 ssle->SetEnableUpdates(false);
374 }
375 } else if (MOZ_UNLIKELY(isKeygen)) {
376 // Adapted from CNavDTD
377 nsresult rv;
378 nsCOMPtr<nsIFormProcessor> theFormProcessor =
379 do_GetService(kFormProcessorCID, &rv);
380 if (NS_FAILED(rv)) {
381 return newContent;
382 }
384 nsTArray<nsString> theContent;
385 nsAutoString theAttribute;
387 (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
388 theContent,
389 theAttribute);
391 newContent->SetAttr(kNameSpaceID_None,
392 nsGkAtoms::moztype,
393 nullptr,
394 theAttribute,
395 false);
397 nsCOMPtr<nsINodeInfo> optionNodeInfo =
398 aBuilder->GetNodeInfoManager()->GetNodeInfo(nsHtml5Atoms::option,
399 nullptr,
400 kNameSpaceID_XHTML,
401 nsIDOMNode::ELEMENT_NODE);
403 for (uint32_t i = 0; i < theContent.Length(); ++i) {
404 nsCOMPtr<dom::Element> optionElt;
405 nsCOMPtr<nsINodeInfo> ni = optionNodeInfo;
406 NS_NewElement(getter_AddRefs(optionElt),
407 ni.forget(),
408 aFromParser);
409 nsRefPtr<nsTextNode> optionText =
410 new nsTextNode(aBuilder->GetNodeInfoManager());
411 (void) optionText->SetText(theContent[i], false);
412 optionElt->AppendChildTo(optionText, false);
413 newContent->AppendChildTo(optionElt, false);
414 newContent->DoneAddingChildren(false);
415 }
416 }
418 if (!aAttributes) {
419 return newContent;
420 }
422 int32_t len = aAttributes->getLength();
423 for (int32_t i = len; i > 0;) {
424 --i;
425 // prefix doesn't need regetting. it is always null or a static atom
426 // local name is never null
427 nsCOMPtr<nsIAtom> localName =
428 Reget(aAttributes->getLocalNameNoBoundsCheck(i));
429 nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
430 int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
432 if (aNs == kNameSpaceID_XHTML &&
433 nsHtml5Atoms::a == aName &&
434 nsHtml5Atoms::name == localName) {
435 // This is an HTML5-incompliant Geckoism.
436 // Remove when fixing bug 582361
437 NS_ConvertUTF16toUTF8 cname(*(aAttributes->getValueNoBoundsCheck(i)));
438 NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
439 newContent->SetAttr(nsuri,
440 localName,
441 prefix,
442 uv,
443 false);
444 } else {
445 nsString& value = *(aAttributes->getValueNoBoundsCheck(i));
446 newContent->SetAttr(nsuri,
447 localName,
448 prefix,
449 value,
450 false);
452 // Custom element prototype swizzling may be needed if there is an
453 // "is" attribute.
454 if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) {
455 ErrorResult errorResult;
456 newContent->OwnerDoc()->SwizzleCustomElement(newContent,
457 value,
458 newContent->GetNameSpaceID(),
459 errorResult);
460 }
461 }
462 }
463 return newContent;
464 }
466 void
467 nsHtml5TreeOperation::SetFormElement(nsIContent* aNode, nsIContent* aParent)
468 {
469 nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aNode));
470 nsCOMPtr<nsIDOMHTMLImageElement> domImageElement = do_QueryInterface(aNode);
471 // NS_ASSERTION(formControl, "Form-associated element did not implement nsIFormControl.");
472 // TODO: uncomment the above line when <keygen> (bug 101019) is supported by Gecko
473 nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(aParent));
474 NS_ASSERTION(formElement, "The form element doesn't implement nsIDOMHTMLFormElement.");
475 // avoid crashing on <keygen>
476 if (formControl &&
477 !aNode->HasAttr(kNameSpaceID_None, nsGkAtoms::form)) {
478 formControl->SetForm(formElement);
479 } else if (domImageElement) {
480 nsRefPtr<dom::HTMLImageElement> imageElement =
481 static_cast<dom::HTMLImageElement*>(domImageElement.get());
482 MOZ_ASSERT(imageElement);
483 imageElement->SetForm(formElement);
484 }
485 }
487 nsresult
488 nsHtml5TreeOperation::AppendIsindexPrompt(nsIContent* parent, nsHtml5DocumentBuilder* aBuilder)
489 {
490 nsXPIDLString prompt;
491 nsresult rv =
492 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
493 "IsIndexPromptWithSpace", prompt);
494 uint32_t len = prompt.Length();
495 if (NS_FAILED(rv)) {
496 return rv;
497 }
498 if (!len) {
499 // Don't bother appending a zero-length text node.
500 return NS_OK;
501 }
502 return AppendText(prompt.BeginReading(), len, parent, aBuilder);
503 }
505 nsresult
506 nsHtml5TreeOperation::FosterParentText(nsIContent* aStackParent,
507 char16_t* aBuffer,
508 uint32_t aLength,
509 nsIContent* aTable,
510 nsHtml5DocumentBuilder* aBuilder)
511 {
512 nsresult rv = NS_OK;
513 nsIContent* foster = aTable->GetParent();
515 if (IsElementOrTemplateContent(foster)) {
516 aBuilder->FlushPendingAppendNotifications();
518 nsHtml5OtherDocUpdate update(foster->OwnerDoc(),
519 aBuilder->GetDocument());
521 uint32_t pos = foster->IndexOf(aTable);
523 nsIContent* previousSibling = aTable->GetPreviousSibling();
524 if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) {
525 return AppendTextToTextNode(aBuffer,
526 aLength,
527 previousSibling,
528 aBuilder);
529 }
531 nsRefPtr<nsTextNode> text =
532 new nsTextNode(aBuilder->GetNodeInfoManager());
533 NS_ASSERTION(text, "Infallible malloc failed?");
534 rv = text->SetText(aBuffer, aLength, false);
535 NS_ENSURE_SUCCESS(rv, rv);
537 rv = foster->InsertChildAt(text, pos, false);
538 NS_ENSURE_SUCCESS(rv, rv);
539 nsNodeUtils::ContentInserted(foster, text, pos);
540 return rv;
541 }
543 return AppendText(aBuffer, aLength, aStackParent, aBuilder);
544 }
546 nsresult
547 nsHtml5TreeOperation::AppendComment(nsIContent* aParent,
548 char16_t* aBuffer,
549 int32_t aLength,
550 nsHtml5DocumentBuilder* aBuilder)
551 {
552 nsRefPtr<dom::Comment> comment =
553 new dom::Comment(aBuilder->GetNodeInfoManager());
554 NS_ASSERTION(comment, "Infallible malloc failed?");
555 nsresult rv = comment->SetText(aBuffer, aLength, false);
556 NS_ENSURE_SUCCESS(rv, rv);
558 return Append(comment, aParent, aBuilder);
559 }
561 nsresult
562 nsHtml5TreeOperation::AppendCommentToDocument(char16_t* aBuffer,
563 int32_t aLength,
564 nsHtml5DocumentBuilder* aBuilder)
565 {
566 nsRefPtr<dom::Comment> comment =
567 new dom::Comment(aBuilder->GetNodeInfoManager());
568 NS_ASSERTION(comment, "Infallible malloc failed?");
569 nsresult rv = comment->SetText(aBuffer, aLength, false);
570 NS_ENSURE_SUCCESS(rv, rv);
572 return AppendToDocument(comment, aBuilder);
573 }
575 nsresult
576 nsHtml5TreeOperation::AppendDoctypeToDocument(nsIAtom* aName,
577 const nsAString& aPublicId,
578 const nsAString& aSystemId,
579 nsHtml5DocumentBuilder* aBuilder)
580 {
581 // Adapted from nsXMLContentSink
582 // Create a new doctype node
583 nsCOMPtr<nsIDOMDocumentType> docType;
584 nsAutoString voidString;
585 voidString.SetIsVoid(true);
586 NS_NewDOMDocumentType(getter_AddRefs(docType),
587 aBuilder->GetNodeInfoManager(),
588 aName,
589 aPublicId,
590 aSystemId,
591 voidString);
592 NS_ASSERTION(docType, "Doctype creation failed.");
593 nsCOMPtr<nsIContent> asContent = do_QueryInterface(docType);
594 return AppendToDocument(asContent, aBuilder);
595 }
597 nsIContent*
598 nsHtml5TreeOperation::GetDocumentFragmentForTemplate(nsIContent* aNode)
599 {
600 dom::HTMLTemplateElement* tempElem =
601 static_cast<dom::HTMLTemplateElement*>(aNode);
602 nsRefPtr<dom::DocumentFragment> frag = tempElem->Content();
603 return frag;
604 }
606 void
607 nsHtml5TreeOperation::PreventScriptExecution(nsIContent* aNode)
608 {
609 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aNode);
610 MOZ_ASSERT(sele);
611 sele->PreventExecution();
612 }
614 void
615 nsHtml5TreeOperation::DoneAddingChildren(nsIContent* aNode,
616 nsHtml5DocumentBuilder* aBuilder)
617 {
618 aNode->DoneAddingChildren(aBuilder->HaveNotified(aNode));
619 }
621 void
622 nsHtml5TreeOperation::DoneCreatingElement(nsIContent* aNode)
623 {
624 aNode->DoneCreatingElement();
625 }
627 void
628 nsHtml5TreeOperation::SvgLoad(nsIContent* aNode)
629 {
630 nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(aNode);
631 if (NS_FAILED(NS_DispatchToMainThread(event))) {
632 NS_WARNING("failed to dispatch svg load dispatcher");
633 }
634 }
636 void
637 nsHtml5TreeOperation::MarkMalformedIfScript(nsIContent* aNode)
638 {
639 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aNode);
640 if (sele) {
641 // Make sure to serialize this script correctly, for nice round tripping.
642 sele->SetIsMalformed();
643 }
644 }
646 nsresult
647 nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
648 nsIContent** aScriptElement)
649 {
650 switch(mOpCode) {
651 case eTreeOpAppend: {
652 nsIContent* node = *(mOne.node);
653 nsIContent* parent = *(mTwo.node);
654 return Append(node, parent, aBuilder);
655 }
656 case eTreeOpDetach: {
657 nsIContent* node = *(mOne.node);
658 Detach(node, aBuilder);
659 return NS_OK;
660 }
661 case eTreeOpAppendChildrenToNewParent: {
662 nsCOMPtr<nsIContent> node = *(mOne.node);
663 nsIContent* parent = *(mTwo.node);
664 return AppendChildrenToNewParent(node, parent, aBuilder);
665 }
666 case eTreeOpFosterParent: {
667 nsIContent* node = *(mOne.node);
668 nsIContent* parent = *(mTwo.node);
669 nsIContent* table = *(mThree.node);
670 return FosterParent(node, parent, table, aBuilder);
671 }
672 case eTreeOpAppendToDocument: {
673 nsIContent* node = *(mOne.node);
674 return AppendToDocument(node, aBuilder);
675 }
676 case eTreeOpAddAttributes: {
677 nsIContent* node = *(mOne.node);
678 nsHtml5HtmlAttributes* attributes = mTwo.attributes;
679 return AddAttributes(node, attributes, aBuilder);
680 }
681 case eTreeOpCreateElementNetwork:
682 case eTreeOpCreateElementNotNetwork: {
683 nsIContent** target = mOne.node;
684 int32_t ns = mFour.integer;
685 nsCOMPtr<nsIAtom> name = Reget(mTwo.atom);
686 nsHtml5HtmlAttributes* attributes = mThree.attributes;
688 *target = CreateElement(ns,
689 name,
690 attributes,
691 mOpCode == eTreeOpCreateElementNetwork ?
692 dom::FROM_PARSER_NETWORK :
693 dom::FROM_PARSER_DOCUMENT_WRITE,
694 aBuilder);
695 return NS_OK;
696 }
697 case eTreeOpSetFormElement: {
698 nsIContent* node = *(mOne.node);
699 nsIContent* parent = *(mTwo.node);
700 SetFormElement(node, parent);
701 return NS_OK;
702 }
703 case eTreeOpAppendText: {
704 nsIContent* parent = *mOne.node;
705 char16_t* buffer = mTwo.unicharPtr;
706 uint32_t length = mFour.integer;
707 return AppendText(buffer, length, parent, aBuilder);
708 }
709 case eTreeOpAppendIsindexPrompt: {
710 nsIContent* parent = *mOne.node;
711 return AppendIsindexPrompt(parent, aBuilder);
712 }
713 case eTreeOpFosterParentText: {
714 nsIContent* stackParent = *mOne.node;
715 char16_t* buffer = mTwo.unicharPtr;
716 uint32_t length = mFour.integer;
717 nsIContent* table = *mThree.node;
718 return FosterParentText(stackParent, buffer, length, table, aBuilder);
719 }
720 case eTreeOpAppendComment: {
721 nsIContent* parent = *mOne.node;
722 char16_t* buffer = mTwo.unicharPtr;
723 int32_t length = mFour.integer;
724 return AppendComment(parent, buffer, length, aBuilder);
725 }
726 case eTreeOpAppendCommentToDocument: {
727 char16_t* buffer = mTwo.unicharPtr;
728 int32_t length = mFour.integer;
729 return AppendCommentToDocument(buffer, length, aBuilder);
730 }
731 case eTreeOpAppendDoctypeToDocument: {
732 nsCOMPtr<nsIAtom> name = Reget(mOne.atom);
733 nsHtml5TreeOperationStringPair* pair = mTwo.stringPair;
734 nsString publicId;
735 nsString systemId;
736 pair->Get(publicId, systemId);
737 return AppendDoctypeToDocument(name, publicId, systemId, aBuilder);
738 }
739 case eTreeOpGetDocumentFragmentForTemplate: {
740 nsIContent* node = *(mOne.node);
741 *mTwo.node = GetDocumentFragmentForTemplate(node);
742 return NS_OK;
743 }
744 case eTreeOpMarkAsBroken: {
745 return mOne.result;
746 }
747 case eTreeOpRunScript: {
748 nsIContent* node = *(mOne.node);
749 nsAHtml5TreeBuilderState* snapshot = mTwo.state;
750 if (snapshot) {
751 aBuilder->InitializeDocWriteParserState(snapshot, mFour.integer);
752 }
753 *aScriptElement = node;
754 return NS_OK;
755 }
756 case eTreeOpRunScriptAsyncDefer: {
757 nsIContent* node = *(mOne.node);
758 aBuilder->RunScript(node);
759 return NS_OK;
760 }
761 case eTreeOpPreventScriptExecution: {
762 nsIContent* node = *(mOne.node);
763 PreventScriptExecution(node);
764 return NS_OK;
765 }
766 case eTreeOpDoneAddingChildren: {
767 nsIContent* node = *(mOne.node);
768 node->DoneAddingChildren(aBuilder->HaveNotified(node));
769 return NS_OK;
770 }
771 case eTreeOpDoneCreatingElement: {
772 nsIContent* node = *(mOne.node);
773 DoneCreatingElement(node);
774 return NS_OK;
775 }
776 case eTreeOpFlushPendingAppendNotifications: {
777 aBuilder->FlushPendingAppendNotifications();
778 return NS_OK;
779 }
780 case eTreeOpSetDocumentCharset: {
781 char* str = mOne.charPtr;
782 int32_t charsetSource = mFour.integer;
783 nsDependentCString dependentString(str);
784 aBuilder->SetDocumentCharsetAndSource(dependentString, charsetSource);
785 return NS_OK;
786 }
787 case eTreeOpNeedsCharsetSwitchTo: {
788 char* str = mOne.charPtr;
789 int32_t charsetSource = mFour.integer;
790 int32_t lineNumber = mTwo.integer;
791 aBuilder->NeedsCharsetSwitchTo(str, charsetSource, (uint32_t)lineNumber);
792 return NS_OK;
793 }
794 case eTreeOpUpdateStyleSheet: {
795 nsIContent* node = *(mOne.node);
796 aBuilder->FlushPendingAppendNotifications();
797 aBuilder->UpdateStyleSheet(node);
798 return NS_OK;
799 }
800 case eTreeOpProcessMeta: {
801 nsIContent* node = *(mOne.node);
802 return aBuilder->ProcessMETATag(node);
803 }
804 case eTreeOpProcessOfflineManifest: {
805 char16_t* str = mOne.unicharPtr;
806 nsDependentString dependentString(str);
807 aBuilder->ProcessOfflineManifest(dependentString);
808 return NS_OK;
809 }
810 case eTreeOpMarkMalformedIfScript: {
811 nsIContent* node = *(mOne.node);
812 MarkMalformedIfScript(node);
813 return NS_OK;
814 }
815 case eTreeOpStreamEnded: {
816 aBuilder->DidBuildModel(false); // this causes a notifications flush anyway
817 return NS_OK;
818 }
819 case eTreeOpStartLayout: {
820 aBuilder->StartLayout(); // this causes a notification flush anyway
821 return NS_OK;
822 }
823 case eTreeOpDocumentMode: {
824 aBuilder->SetDocumentMode(mOne.mode);
825 return NS_OK;
826 }
827 case eTreeOpSetStyleLineNumber: {
828 nsIContent* node = *(mOne.node);
829 nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(node);
830 NS_ASSERTION(ssle, "Node didn't QI to style.");
831 ssle->SetLineNumber(mFour.integer);
832 return NS_OK;
833 }
834 case eTreeOpSetScriptLineNumberAndFreeze: {
835 nsIContent* node = *(mOne.node);
836 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node);
837 NS_ASSERTION(sele, "Node didn't QI to script.");
838 sele->SetScriptLineNumber(mFour.integer);
839 sele->FreezeUriAsyncDefer();
840 return NS_OK;
841 }
842 case eTreeOpSvgLoad: {
843 nsIContent* node = *(mOne.node);
844 SvgLoad(node);
845 return NS_OK;
846 }
847 case eTreeOpMaybeComplainAboutCharset: {
848 char* msgId = mOne.charPtr;
849 bool error = mTwo.integer;
850 int32_t lineNumber = mThree.integer;
851 aBuilder->MaybeComplainAboutCharset(msgId, error, (uint32_t)lineNumber);
852 return NS_OK;
853 }
854 case eTreeOpAddClass: {
855 nsIContent* node = *(mOne.node);
856 char16_t* str = mTwo.unicharPtr;
857 nsDependentString depStr(str);
858 // See viewsource.css for the possible classes
859 nsAutoString klass;
860 node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass);
861 if (!klass.IsEmpty()) {
862 klass.Append(' ');
863 klass.Append(depStr);
864 node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true);
865 } else {
866 node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true);
867 }
868 return NS_OK;
869 }
870 case eTreeOpAddLineNumberId: {
871 nsIContent* node = *(mOne.node);
872 int32_t lineNumber = mFour.integer;
873 nsAutoString val(NS_LITERAL_STRING("line"));
874 val.AppendInt(lineNumber);
875 node->SetAttr(kNameSpaceID_None, nsGkAtoms::id, val, true);
876 return NS_OK;
877 }
878 case eTreeOpAddViewSourceHref: {
879 nsIContent* node = *mOne.node;
880 char16_t* buffer = mTwo.unicharPtr;
881 int32_t length = mFour.integer;
883 nsDependentString relative(buffer, length);
885 nsIDocument* doc = aBuilder->GetDocument();
887 const nsCString& charset = doc->GetDocumentCharacterSet();
888 nsCOMPtr<nsIURI> uri;
889 nsresult rv = NS_NewURI(getter_AddRefs(uri),
890 relative,
891 charset.get(),
892 aBuilder->GetViewSourceBaseURI());
893 NS_ENSURE_SUCCESS(rv, NS_OK);
895 // Reuse the fix for bug 467852
896 // URLs that execute script (e.g. "javascript:" URLs) should just be
897 // ignored. There's nothing reasonable we can do with them, and allowing
898 // them to execute in the context of the view-source window presents a
899 // security risk. Just return the empty string in this case.
900 bool openingExecutesScript = false;
901 rv = NS_URIChainHasFlags(uri,
902 nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT,
903 &openingExecutesScript);
904 if (NS_FAILED(rv) || openingExecutesScript) {
905 return NS_OK;
906 }
908 nsAutoCString viewSourceUrl;
910 // URLs that return data (e.g. "http:" URLs) should be prefixed with
911 // "view-source:". URLs that don't return data should just be returned
912 // undecorated.
913 bool doesNotReturnData = false;
914 rv = NS_URIChainHasFlags(uri,
915 nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
916 &doesNotReturnData);
917 NS_ENSURE_SUCCESS(rv, NS_OK);
918 if (!doesNotReturnData) {
919 viewSourceUrl.AssignLiteral("view-source:");
920 }
922 nsAutoCString spec;
923 uri->GetSpec(spec);
925 viewSourceUrl.Append(spec);
927 nsAutoString utf16;
928 CopyUTF8toUTF16(viewSourceUrl, utf16);
930 node->SetAttr(kNameSpaceID_None, nsGkAtoms::href, utf16, true);
931 return rv;
932 }
933 case eTreeOpAddError: {
934 nsIContent* node = *(mOne.node);
935 char* msgId = mTwo.charPtr;
936 nsCOMPtr<nsIAtom> atom = Reget(mThree.atom);
937 nsCOMPtr<nsIAtom> otherAtom = Reget(mFour.atom);
938 // See viewsource.css for the possible classes in addition to "error".
939 nsAutoString klass;
940 node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass);
941 if (!klass.IsEmpty()) {
942 klass.Append(NS_LITERAL_STRING(" error"));
943 node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true);
944 } else {
945 node->SetAttr(kNameSpaceID_None,
946 nsGkAtoms::_class,
947 NS_LITERAL_STRING("error"),
948 true);
949 }
951 nsresult rv;
952 nsXPIDLString message;
953 if (otherAtom) {
954 const char16_t* params[] = { atom->GetUTF16String(),
955 otherAtom->GetUTF16String() };
956 rv = nsContentUtils::FormatLocalizedString(
957 nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message);
958 NS_ENSURE_SUCCESS(rv, NS_OK);
959 } else if (atom) {
960 const char16_t* params[] = { atom->GetUTF16String() };
961 rv = nsContentUtils::FormatLocalizedString(
962 nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message);
963 NS_ENSURE_SUCCESS(rv, NS_OK);
964 } else {
965 rv = nsContentUtils::GetLocalizedString(
966 nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, message);
967 NS_ENSURE_SUCCESS(rv, NS_OK);
968 }
970 nsAutoString title;
971 node->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
972 if (!title.IsEmpty()) {
973 title.Append('\n');
974 title.Append(message);
975 node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, title, true);
976 } else {
977 node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, message, true);
978 }
979 return rv;
980 }
981 default: {
982 NS_NOTREACHED("Bogus tree op");
983 }
984 }
985 return NS_OK; // keep compiler happy
986 }