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