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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : * Henri Sivonen <hsivonen@iki.fi>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsHtml5TreeOperation.h"
42 : #include "nsContentUtils.h"
43 : #include "nsNodeUtils.h"
44 : #include "nsAttrName.h"
45 : #include "nsHtml5TreeBuilder.h"
46 : #include "nsIDOMMutationEvent.h"
47 : #include "mozAutoDocUpdate.h"
48 : #include "nsBindingManager.h"
49 : #include "nsXBLBinding.h"
50 : #include "nsHtml5DocumentMode.h"
51 : #include "nsHtml5HtmlAttributes.h"
52 : #include "nsContentCreatorFunctions.h"
53 : #include "nsIScriptElement.h"
54 : #include "nsIDTD.h"
55 : #include "nsTraceRefcnt.h"
56 : #include "nsIDOMHTMLFormElement.h"
57 : #include "nsIFormControl.h"
58 : #include "nsIStyleSheetLinkingElement.h"
59 : #include "nsIDOMDocumentType.h"
60 : #include "nsIObserverService.h"
61 : #include "mozilla/Services.h"
62 : #include "nsIMutationObserver.h"
63 : #include "nsIFormProcessor.h"
64 : #include "nsIServiceManager.h"
65 : #include "nsEscape.h"
66 : #include "mozilla/dom/Element.h"
67 : #include "nsHtml5SVGLoadDispatcher.h"
68 : #include "nsIURI.h"
69 : #include "nsIProtocolHandler.h"
70 : #include "nsNetUtil.h"
71 :
72 : namespace dom = mozilla::dom;
73 :
74 : static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
75 :
76 : /**
77 : * Helper class that opens a notification batch if the current doc
78 : * is different from the executor doc.
79 : */
80 : class NS_STACK_CLASS nsHtml5OtherDocUpdate {
81 : public:
82 1 : nsHtml5OtherDocUpdate(nsIDocument* aCurrentDoc, nsIDocument* aExecutorDoc)
83 : {
84 1 : NS_PRECONDITION(aCurrentDoc, "Node has no doc?");
85 1 : NS_PRECONDITION(aExecutorDoc, "Executor has no doc?");
86 1 : if (NS_LIKELY(aCurrentDoc == aExecutorDoc)) {
87 1 : mDocument = nsnull;
88 : } else {
89 0 : mDocument = aCurrentDoc;
90 0 : aCurrentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
91 : }
92 1 : }
93 :
94 1 : ~nsHtml5OtherDocUpdate()
95 : {
96 1 : if (NS_UNLIKELY(mDocument)) {
97 0 : mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
98 : }
99 1 : }
100 : private:
101 : nsIDocument* mDocument;
102 : };
103 :
104 3251 : nsHtml5TreeOperation::nsHtml5TreeOperation()
105 : #ifdef DEBUG
106 3251 : : mOpCode(eTreeOpUninitialized)
107 : #endif
108 : {
109 3251 : MOZ_COUNT_CTOR(nsHtml5TreeOperation);
110 3251 : }
111 :
112 3251 : nsHtml5TreeOperation::~nsHtml5TreeOperation()
113 : {
114 3251 : MOZ_COUNT_DTOR(nsHtml5TreeOperation);
115 3251 : NS_ASSERTION(mOpCode != eTreeOpUninitialized, "Uninitialized tree op.");
116 3251 : switch(mOpCode) {
117 : case eTreeOpAddAttributes:
118 0 : delete mTwo.attributes;
119 0 : break;
120 : case eTreeOpCreateElementNetwork:
121 : case eTreeOpCreateElementNotNetwork:
122 780 : delete mThree.attributes;
123 780 : break;
124 : case eTreeOpAppendDoctypeToDocument:
125 0 : delete mTwo.stringPair;
126 0 : break;
127 : case eTreeOpFosterParentText:
128 : case eTreeOpAppendText:
129 : case eTreeOpAppendComment:
130 : case eTreeOpAppendCommentToDocument:
131 : case eTreeOpAddViewSourceHref:
132 287 : delete[] mTwo.unicharPtr;
133 287 : break;
134 : case eTreeOpSetDocumentCharset:
135 : case eTreeOpNeedsCharsetSwitchTo:
136 0 : delete[] mOne.charPtr;
137 0 : break;
138 : case eTreeOpProcessOfflineManifest:
139 234 : nsMemory::Free(mOne.unicharPtr);
140 234 : break;
141 : default: // keep the compiler happy
142 1950 : break;
143 : }
144 3251 : }
145 :
146 : nsresult
147 1 : nsHtml5TreeOperation::AppendTextToTextNode(const PRUnichar* aBuffer,
148 : PRUint32 aLength,
149 : nsIContent* aTextNode,
150 : nsHtml5TreeOpExecutor* aBuilder)
151 : {
152 1 : NS_PRECONDITION(aTextNode, "Got null text node.");
153 :
154 1 : if (aBuilder->HaveNotified(aTextNode)) {
155 : // This text node has already been notified on, so it's necessary to
156 : // notify on the append
157 0 : nsresult rv = NS_OK;
158 0 : PRUint32 oldLength = aTextNode->TextLength();
159 : CharacterDataChangeInfo info = {
160 : true,
161 : oldLength,
162 : oldLength,
163 : aLength
164 0 : };
165 0 : nsNodeUtils::CharacterDataWillChange(aTextNode, &info);
166 :
167 0 : rv = aTextNode->AppendText(aBuffer, aLength, false);
168 0 : NS_ENSURE_SUCCESS(rv, rv);
169 :
170 0 : nsNodeUtils::CharacterDataChanged(aTextNode, &info);
171 0 : return rv;
172 : }
173 :
174 1 : return aTextNode->AppendText(aBuffer, aLength, false);
175 : }
176 :
177 :
178 : nsresult
179 287 : nsHtml5TreeOperation::AppendText(const PRUnichar* aBuffer,
180 : PRUint32 aLength,
181 : nsIContent* aParent,
182 : nsHtml5TreeOpExecutor* aBuilder)
183 : {
184 287 : nsresult rv = NS_OK;
185 287 : nsIContent* lastChild = aParent->GetLastChild();
186 287 : if (lastChild && lastChild->IsNodeOfType(nsINode::eTEXT)) {
187 : nsHtml5OtherDocUpdate update(aParent->OwnerDoc(),
188 2 : aBuilder->GetDocument());
189 : return AppendTextToTextNode(aBuffer,
190 : aLength,
191 : lastChild,
192 1 : aBuilder);
193 : }
194 :
195 572 : nsCOMPtr<nsIContent> text;
196 286 : NS_NewTextNode(getter_AddRefs(text), aBuilder->GetNodeInfoManager());
197 286 : NS_ASSERTION(text, "Infallible malloc failed?");
198 286 : rv = text->SetText(aBuffer, aLength, false);
199 286 : NS_ENSURE_SUCCESS(rv, rv);
200 :
201 286 : return Append(text, aParent, aBuilder);
202 : }
203 :
204 : nsresult
205 832 : nsHtml5TreeOperation::Append(nsIContent* aNode,
206 : nsIContent* aParent,
207 : nsHtml5TreeOpExecutor* aBuilder)
208 : {
209 832 : nsresult rv = NS_OK;
210 832 : nsIDocument* executorDoc = aBuilder->GetDocument();
211 832 : NS_ASSERTION(executorDoc, "Null doc on executor");
212 832 : nsIDocument* parentDoc = aParent->OwnerDoc();
213 832 : NS_ASSERTION(parentDoc, "Null owner doc on old node.");
214 :
215 832 : if (NS_LIKELY(executorDoc == parentDoc)) {
216 : // the usual case. the parent is in the parser's doc
217 832 : rv = aParent->AppendChildTo(aNode, false);
218 832 : if (NS_SUCCEEDED(rv)) {
219 832 : aBuilder->PostPendingAppendNotification(aParent, aNode);
220 : }
221 832 : return rv;
222 : }
223 :
224 : // The parent has been moved to another doc
225 0 : parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
226 :
227 0 : PRUint32 childCount = aParent->GetChildCount();
228 0 : rv = aParent->AppendChildTo(aNode, false);
229 0 : if (NS_SUCCEEDED(rv)) {
230 0 : nsNodeUtils::ContentAppended(aParent, aNode, childCount);
231 : }
232 0 : parentDoc->EndUpdate(UPDATE_CONTENT_MODEL);
233 0 : return rv;
234 : }
235 :
236 : nsresult
237 234 : nsHtml5TreeOperation::AppendToDocument(nsIContent* aNode,
238 : nsHtml5TreeOpExecutor* aBuilder)
239 : {
240 234 : nsresult rv = NS_OK;
241 234 : aBuilder->FlushPendingAppendNotifications();
242 234 : nsIDocument* doc = aBuilder->GetDocument();
243 234 : PRUint32 childCount = doc->GetChildCount();
244 234 : rv = doc->AppendChildTo(aNode, false);
245 234 : NS_ENSURE_SUCCESS(rv, rv);
246 234 : nsNodeUtils::ContentInserted(doc, aNode, childCount);
247 :
248 234 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
249 : "Someone forgot to block scripts");
250 234 : if (aNode->IsElement()) {
251 : nsContentUtils::AddScriptRunner(
252 234 : new nsDocElementCreatedNotificationRunner(doc));
253 : }
254 234 : return rv;
255 : }
256 :
257 : nsresult
258 3251 : nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
259 : nsIContent** aScriptElement)
260 : {
261 3251 : nsresult rv = NS_OK;
262 3251 : switch(mOpCode) {
263 : case eTreeOpAppend: {
264 546 : nsIContent* node = *(mOne.node);
265 546 : nsIContent* parent = *(mTwo.node);
266 546 : return Append(node, parent, aBuilder);
267 : }
268 : case eTreeOpDetach: {
269 0 : nsIContent* node = *(mOne.node);
270 0 : aBuilder->FlushPendingAppendNotifications();
271 0 : nsCOMPtr<nsIContent> parent = node->GetParent();
272 0 : if (parent) {
273 0 : nsHtml5OtherDocUpdate update(parent->OwnerDoc(),
274 0 : aBuilder->GetDocument());
275 0 : PRUint32 pos = parent->IndexOf(node);
276 : NS_ASSERTION((pos >= 0), "Element not found as child of its parent");
277 0 : rv = parent->RemoveChildAt(pos, true);
278 0 : NS_ENSURE_SUCCESS(rv, rv);
279 : }
280 0 : return rv;
281 : }
282 : case eTreeOpAppendChildrenToNewParent: {
283 0 : nsCOMPtr<nsIContent> node = *(mOne.node);
284 0 : nsIContent* parent = *(mTwo.node);
285 0 : aBuilder->FlushPendingAppendNotifications();
286 :
287 : nsHtml5OtherDocUpdate update(parent->OwnerDoc(),
288 0 : aBuilder->GetDocument());
289 :
290 0 : PRUint32 childCount = parent->GetChildCount();
291 0 : bool didAppend = false;
292 0 : while (node->HasChildren()) {
293 0 : nsCOMPtr<nsIContent> child = node->GetFirstChild();
294 0 : rv = node->RemoveChildAt(0, true);
295 0 : NS_ENSURE_SUCCESS(rv, rv);
296 0 : rv = parent->AppendChildTo(child, false);
297 0 : NS_ENSURE_SUCCESS(rv, rv);
298 0 : didAppend = true;
299 : }
300 0 : if (didAppend) {
301 0 : nsNodeUtils::ContentAppended(parent, parent->GetChildAt(childCount),
302 0 : childCount);
303 : }
304 0 : return rv;
305 : }
306 : case eTreeOpFosterParent: {
307 0 : nsIContent* node = *(mOne.node);
308 0 : nsIContent* parent = *(mTwo.node);
309 0 : nsIContent* table = *(mThree.node);
310 0 : nsIContent* foster = table->GetParent();
311 :
312 0 : if (foster && foster->IsElement()) {
313 0 : aBuilder->FlushPendingAppendNotifications();
314 :
315 : nsHtml5OtherDocUpdate update(foster->OwnerDoc(),
316 0 : aBuilder->GetDocument());
317 :
318 0 : PRUint32 pos = foster->IndexOf(table);
319 0 : rv = foster->InsertChildAt(node, pos, false);
320 0 : NS_ENSURE_SUCCESS(rv, rv);
321 0 : nsNodeUtils::ContentInserted(foster, node, pos);
322 0 : return rv;
323 : }
324 :
325 0 : return Append(node, parent, aBuilder);
326 : }
327 : case eTreeOpAppendToDocument: {
328 234 : nsIContent* node = *(mOne.node);
329 234 : return AppendToDocument(node, aBuilder);
330 : }
331 : case eTreeOpAddAttributes: {
332 0 : dom::Element* node = (*(mOne.node))->AsElement();
333 0 : nsHtml5HtmlAttributes* attributes = mTwo.attributes;
334 :
335 : nsHtml5OtherDocUpdate update(node->OwnerDoc(),
336 0 : aBuilder->GetDocument());
337 :
338 0 : PRInt32 len = attributes->getLength();
339 0 : for (PRInt32 i = len; i > 0;) {
340 0 : --i;
341 : // prefix doesn't need regetting. it is always null or a static atom
342 : // local name is never null
343 0 : nsCOMPtr<nsIAtom> localName = Reget(attributes->getLocalName(i));
344 0 : PRInt32 nsuri = attributes->getURI(i);
345 0 : if (!node->HasAttr(nsuri, localName)) {
346 : // prefix doesn't need regetting. it is always null or a static atom
347 : // local name is never null
348 0 : node->SetAttr(nsuri, localName, attributes->getPrefix(i), *(attributes->getValue(i)), true);
349 : // XXX what to do with nsresult?
350 : }
351 : }
352 :
353 0 : return rv;
354 : }
355 : case eTreeOpCreateElementNetwork:
356 : case eTreeOpCreateElementNotNetwork: {
357 780 : nsIContent** target = mOne.node;
358 780 : PRInt32 ns = mFour.integer;
359 1560 : nsCOMPtr<nsIAtom> name = Reget(mTwo.atom);
360 780 : nsHtml5HtmlAttributes* attributes = mThree.attributes;
361 :
362 780 : bool isKeygen = (name == nsHtml5Atoms::keygen && ns == kNameSpaceID_XHTML);
363 780 : if (NS_UNLIKELY(isKeygen)) {
364 0 : name = nsHtml5Atoms::select;
365 : }
366 :
367 1560 : nsCOMPtr<nsIContent> newContent;
368 : nsCOMPtr<nsINodeInfo> nodeInfo = aBuilder->GetNodeInfoManager()->
369 1560 : GetNodeInfo(name, nsnull, ns, nsIDOMNode::ELEMENT_NODE);
370 780 : NS_ASSERTION(nodeInfo, "Got null nodeinfo.");
371 780 : NS_NewElement(getter_AddRefs(newContent),
372 : nodeInfo.forget(),
373 : (mOpCode == eTreeOpCreateElementNetwork ?
374 : dom::FROM_PARSER_NETWORK
375 780 : : (aBuilder->BelongsToStringParser() ?
376 : dom::FROM_PARSER_FRAGMENT :
377 1560 : dom::FROM_PARSER_DOCUMENT_WRITE)));
378 780 : NS_ASSERTION(newContent, "Element creation created null pointer.");
379 :
380 780 : aBuilder->HoldElement(*target = newContent);
381 :
382 780 : if (NS_UNLIKELY(name == nsHtml5Atoms::style || name == nsHtml5Atoms::link)) {
383 0 : nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
384 0 : if (ssle) {
385 0 : ssle->InitStyleLinkElement(false);
386 0 : ssle->SetEnableUpdates(false);
387 : }
388 780 : } else if (NS_UNLIKELY(isKeygen)) {
389 : // Adapted from CNavDTD
390 : nsCOMPtr<nsIFormProcessor> theFormProcessor =
391 0 : do_GetService(kFormProcessorCID, &rv);
392 0 : NS_ENSURE_SUCCESS(rv, rv);
393 :
394 0 : nsTArray<nsString> theContent;
395 0 : nsAutoString theAttribute;
396 :
397 0 : (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
398 : theContent,
399 0 : theAttribute);
400 :
401 0 : newContent->SetAttr(kNameSpaceID_None,
402 : nsGkAtoms::moztype,
403 : nsnull,
404 : theAttribute,
405 0 : false);
406 :
407 : nsCOMPtr<nsINodeInfo> optionNodeInfo =
408 : aBuilder->GetNodeInfoManager()->GetNodeInfo(nsHtml5Atoms::option,
409 : nsnull,
410 : kNameSpaceID_XHTML,
411 0 : nsIDOMNode::ELEMENT_NODE);
412 :
413 0 : for (PRUint32 i = 0; i < theContent.Length(); ++i) {
414 0 : nsCOMPtr<nsIContent> optionElt;
415 0 : nsCOMPtr<nsINodeInfo> ni = optionNodeInfo;
416 0 : NS_NewElement(getter_AddRefs(optionElt),
417 : ni.forget(),
418 : (mOpCode == eTreeOpCreateElementNetwork ?
419 : dom::FROM_PARSER_NETWORK
420 0 : : (aBuilder->BelongsToStringParser() ?
421 : dom::FROM_PARSER_FRAGMENT :
422 0 : dom::FROM_PARSER_DOCUMENT_WRITE)));
423 0 : nsCOMPtr<nsIContent> optionText;
424 0 : NS_NewTextNode(getter_AddRefs(optionText),
425 0 : aBuilder->GetNodeInfoManager());
426 0 : (void) optionText->SetText(theContent[i], false);
427 0 : optionElt->AppendChildTo(optionText, false);
428 0 : newContent->AppendChildTo(optionElt, false);
429 0 : newContent->DoneAddingChildren(false);
430 : }
431 780 : } else if (name == nsHtml5Atoms::frameset && ns == kNameSpaceID_XHTML) {
432 0 : nsIDocument* doc = aBuilder->GetDocument();
433 0 : nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(doc);
434 0 : if (htmlDocument) {
435 : // It seems harmless to call this multiple times, since this
436 : // is a simple field setter
437 0 : htmlDocument->SetIsFrameset(true);
438 : }
439 : }
440 :
441 780 : if (!attributes) {
442 780 : return rv;
443 : }
444 :
445 0 : PRInt32 len = attributes->getLength();
446 0 : for (PRInt32 i = len; i > 0;) {
447 0 : --i;
448 : // prefix doesn't need regetting. it is always null or a static atom
449 : // local name is never null
450 0 : nsCOMPtr<nsIAtom> localName = Reget(attributes->getLocalName(i));
451 0 : if (ns == kNameSpaceID_XHTML &&
452 0 : nsHtml5Atoms::a == name &&
453 0 : nsHtml5Atoms::name == localName) {
454 : // This is an HTML5-incompliant Geckoism.
455 : // Remove when fixing bug 582361
456 0 : NS_ConvertUTF16toUTF8 cname(*(attributes->getValue(i)));
457 0 : NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
458 0 : newContent->SetAttr(attributes->getURI(i), localName,
459 0 : attributes->getPrefix(i), uv, false);
460 : } else {
461 0 : newContent->SetAttr(attributes->getURI(i), localName,
462 0 : attributes->getPrefix(i), *(attributes->getValue(i)), false);
463 : }
464 : }
465 :
466 0 : return rv;
467 : }
468 : case eTreeOpSetFormElement: {
469 0 : nsIContent* node = *(mOne.node);
470 0 : nsIContent* parent = *(mTwo.node);
471 0 : nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node));
472 : // NS_ASSERTION(formControl, "Form-associated element did not implement nsIFormControl.");
473 : // TODO: uncomment the above line when <keygen> (bug 101019) is supported by Gecko
474 0 : nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(parent));
475 0 : NS_ASSERTION(formElement, "The form element doesn't implement nsIDOMHTMLFormElement.");
476 : // avoid crashing on <keygen>
477 0 : if (formControl &&
478 0 : !node->HasAttr(kNameSpaceID_None, nsGkAtoms::form)) {
479 0 : formControl->SetForm(formElement);
480 : }
481 0 : return rv;
482 : }
483 : case eTreeOpAppendText: {
484 287 : nsIContent* parent = *mOne.node;
485 287 : PRUnichar* buffer = mTwo.unicharPtr;
486 287 : PRUint32 length = mFour.integer;
487 287 : return AppendText(buffer, length, parent, aBuilder);
488 : }
489 : case eTreeOpAppendIsindexPrompt: {
490 0 : nsIContent* parent = *mOne.node;
491 0 : nsXPIDLString prompt;
492 : nsresult rv =
493 : nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
494 0 : "IsIndexPromptWithSpace", prompt);
495 0 : PRUint32 len = prompt.Length();
496 0 : if (NS_FAILED(rv)) {
497 0 : return rv;
498 : }
499 0 : if (!len) {
500 : // Don't bother appending a zero-length text node.
501 0 : return NS_OK;
502 : }
503 0 : return AppendText(prompt.BeginReading(), len, parent, aBuilder);
504 : }
505 : case eTreeOpFosterParentText: {
506 0 : nsIContent* stackParent = *mOne.node;
507 0 : PRUnichar* buffer = mTwo.unicharPtr;
508 0 : PRUint32 length = mFour.integer;
509 0 : nsIContent* table = *mThree.node;
510 :
511 0 : nsIContent* foster = table->GetParent();
512 :
513 0 : if (foster && foster->IsElement()) {
514 0 : aBuilder->FlushPendingAppendNotifications();
515 :
516 : nsHtml5OtherDocUpdate update(foster->OwnerDoc(),
517 0 : aBuilder->GetDocument());
518 :
519 0 : PRUint32 pos = foster->IndexOf(table);
520 :
521 0 : nsIContent* previousSibling = table->GetPreviousSibling();
522 0 : if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) {
523 : return AppendTextToTextNode(buffer,
524 : length,
525 : previousSibling,
526 0 : aBuilder);
527 : }
528 :
529 0 : nsCOMPtr<nsIContent> text;
530 0 : NS_NewTextNode(getter_AddRefs(text), aBuilder->GetNodeInfoManager());
531 0 : NS_ASSERTION(text, "Infallible malloc failed?");
532 0 : rv = text->SetText(buffer, length, false);
533 0 : NS_ENSURE_SUCCESS(rv, rv);
534 :
535 0 : rv = foster->InsertChildAt(text, pos, false);
536 0 : NS_ENSURE_SUCCESS(rv, rv);
537 0 : nsNodeUtils::ContentInserted(foster, text, pos);
538 0 : return rv;
539 : }
540 :
541 0 : return AppendText(buffer, length, stackParent, aBuilder);
542 : }
543 : case eTreeOpAppendComment: {
544 0 : nsIContent* parent = *mOne.node;
545 0 : PRUnichar* buffer = mTwo.unicharPtr;
546 0 : PRInt32 length = mFour.integer;
547 :
548 0 : nsCOMPtr<nsIContent> comment;
549 0 : NS_NewCommentNode(getter_AddRefs(comment), aBuilder->GetNodeInfoManager());
550 0 : NS_ASSERTION(comment, "Infallible malloc failed?");
551 0 : rv = comment->SetText(buffer, length, false);
552 0 : NS_ENSURE_SUCCESS(rv, rv);
553 :
554 0 : return Append(comment, parent, aBuilder);
555 : }
556 : case eTreeOpAppendCommentToDocument: {
557 0 : PRUnichar* buffer = mTwo.unicharPtr;
558 0 : PRInt32 length = mFour.integer;
559 :
560 0 : nsCOMPtr<nsIContent> comment;
561 0 : NS_NewCommentNode(getter_AddRefs(comment), aBuilder->GetNodeInfoManager());
562 0 : NS_ASSERTION(comment, "Infallible malloc failed?");
563 0 : rv = comment->SetText(buffer, length, false);
564 0 : NS_ENSURE_SUCCESS(rv, rv);
565 :
566 0 : return AppendToDocument(comment, aBuilder);
567 : }
568 : case eTreeOpAppendDoctypeToDocument: {
569 0 : nsCOMPtr<nsIAtom> name = Reget(mOne.atom);
570 0 : nsHtml5TreeOperationStringPair* pair = mTwo.stringPair;
571 0 : nsString publicId;
572 0 : nsString systemId;
573 0 : pair->Get(publicId, systemId);
574 :
575 : // Adapted from nsXMLContentSink
576 : // Create a new doctype node
577 0 : nsCOMPtr<nsIDOMDocumentType> docType;
578 0 : nsAutoString voidString;
579 0 : voidString.SetIsVoid(true);
580 0 : NS_NewDOMDocumentType(getter_AddRefs(docType),
581 : aBuilder->GetNodeInfoManager(),
582 : name,
583 : publicId,
584 : systemId,
585 0 : voidString);
586 0 : NS_ASSERTION(docType, "Doctype creation failed.");
587 0 : nsCOMPtr<nsIContent> asContent = do_QueryInterface(docType);
588 0 : return AppendToDocument(asContent, aBuilder);
589 : }
590 : case eTreeOpMarkAsBroken: {
591 0 : aBuilder->MarkAsBroken();
592 0 : return rv;
593 : }
594 : case eTreeOpRunScript: {
595 0 : nsIContent* node = *(mOne.node);
596 0 : nsAHtml5TreeBuilderState* snapshot = mTwo.state;
597 0 : if (snapshot) {
598 0 : aBuilder->InitializeDocWriteParserState(snapshot, mFour.integer);
599 : }
600 0 : *aScriptElement = node;
601 0 : return rv;
602 : }
603 : case eTreeOpRunScriptAsyncDefer: {
604 0 : nsIContent* node = *(mOne.node);
605 0 : aBuilder->RunScript(node);
606 0 : return rv;
607 : }
608 : case eTreeOpDoneAddingChildren: {
609 0 : nsIContent* node = *(mOne.node);
610 0 : node->DoneAddingChildren(aBuilder->HaveNotified(node));
611 0 : return rv;
612 : }
613 : case eTreeOpDoneCreatingElement: {
614 0 : nsIContent* node = *(mOne.node);
615 0 : node->DoneCreatingElement();
616 0 : return rv;
617 : }
618 : case eTreeOpFlushPendingAppendNotifications: {
619 0 : aBuilder->FlushPendingAppendNotifications();
620 0 : return rv;
621 : }
622 : case eTreeOpSetDocumentCharset: {
623 0 : char* str = mOne.charPtr;
624 0 : PRInt32 charsetSource = mFour.integer;
625 0 : nsDependentCString dependentString(str);
626 0 : aBuilder->SetDocumentCharsetAndSource(dependentString, charsetSource);
627 0 : return rv;
628 : }
629 : case eTreeOpNeedsCharsetSwitchTo: {
630 0 : char* str = mOne.charPtr;
631 0 : PRInt32 charsetSource = mFour.integer;
632 0 : aBuilder->NeedsCharsetSwitchTo(str, charsetSource);
633 0 : return rv;
634 : }
635 : case eTreeOpUpdateStyleSheet: {
636 0 : nsIContent* node = *(mOne.node);
637 0 : aBuilder->FlushPendingAppendNotifications();
638 0 : aBuilder->UpdateStyleSheet(node);
639 0 : return rv;
640 : }
641 : case eTreeOpProcessMeta: {
642 0 : nsIContent* node = *(mOne.node);
643 0 : rv = aBuilder->ProcessMETATag(node);
644 0 : return rv;
645 : }
646 : case eTreeOpProcessOfflineManifest: {
647 234 : PRUnichar* str = mOne.unicharPtr;
648 468 : nsDependentString dependentString(str);
649 234 : aBuilder->ProcessOfflineManifest(dependentString);
650 234 : return rv;
651 : }
652 : case eTreeOpMarkMalformedIfScript: {
653 468 : nsIContent* node = *(mOne.node);
654 936 : nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node);
655 468 : if (sele) {
656 : // Make sure to serialize this script correctly, for nice round tripping.
657 0 : sele->SetIsMalformed();
658 : }
659 468 : return rv;
660 : }
661 : case eTreeOpStreamEnded: {
662 234 : aBuilder->DidBuildModel(false); // this causes a notifications flush anyway
663 234 : return rv;
664 : }
665 : case eTreeOpStartLayout: {
666 234 : aBuilder->StartLayout(); // this causes a notification flush anyway
667 234 : return rv;
668 : }
669 : case eTreeOpDocumentMode: {
670 234 : aBuilder->SetDocumentMode(mOne.mode);
671 234 : return rv;
672 : }
673 : case eTreeOpSetStyleLineNumber: {
674 0 : nsIContent* node = *(mOne.node);
675 0 : nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(node);
676 0 : NS_ASSERTION(ssle, "Node didn't QI to style.");
677 0 : ssle->SetLineNumber(mFour.integer);
678 0 : return rv;
679 : }
680 : case eTreeOpSetScriptLineNumberAndFreeze: {
681 0 : nsIContent* node = *(mOne.node);
682 0 : nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node);
683 0 : NS_ASSERTION(sele, "Node didn't QI to script.");
684 0 : sele->SetScriptLineNumber(mFour.integer);
685 0 : sele->FreezeUriAsyncDefer();
686 0 : return rv;
687 : }
688 : case eTreeOpSvgLoad: {
689 0 : nsIContent* node = *(mOne.node);
690 0 : nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(node);
691 0 : if (NS_FAILED(NS_DispatchToMainThread(event))) {
692 0 : NS_WARNING("failed to dispatch svg load dispatcher");
693 : }
694 0 : return rv;
695 : }
696 : case eTreeOpAddClass: {
697 0 : nsIContent* node = *(mOne.node);
698 0 : PRUnichar* str = mTwo.unicharPtr;
699 0 : nsDependentString depStr(str);
700 : // See viewsource.css for the possible classes
701 0 : nsAutoString klass;
702 0 : node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass);
703 0 : if (!klass.IsEmpty()) {
704 0 : klass.Append(' ');
705 0 : klass.Append(depStr);
706 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true);
707 : } else {
708 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true);
709 : }
710 0 : return rv;
711 : }
712 : case eTreeOpAddLineNumberId: {
713 0 : nsIContent* node = *(mOne.node);
714 0 : PRInt32 lineNumber = mFour.integer;
715 0 : nsAutoString val(NS_LITERAL_STRING("line"));
716 0 : val.AppendInt(lineNumber);
717 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::id, val, true);
718 0 : return rv;
719 : }
720 : case eTreeOpAddViewSourceHref: {
721 0 : nsIContent* node = *mOne.node;
722 0 : PRUnichar* buffer = mTwo.unicharPtr;
723 0 : PRInt32 length = mFour.integer;
724 :
725 0 : nsDependentString relative(buffer, length);
726 :
727 0 : nsIDocument* doc = aBuilder->GetDocument();
728 :
729 0 : const nsCString& charset = doc->GetDocumentCharacterSet();
730 0 : nsCOMPtr<nsIURI> uri;
731 0 : rv = NS_NewURI(getter_AddRefs(uri),
732 : relative,
733 : charset.get(),
734 0 : aBuilder->GetViewSourceBaseURI());
735 0 : NS_ENSURE_SUCCESS(rv, rv);
736 :
737 : // Reuse the fix for bug 467852
738 : // URLs that execute script (e.g. "javascript:" URLs) should just be
739 : // ignored. There's nothing reasonable we can do with them, and allowing
740 : // them to execute in the context of the view-source window presents a
741 : // security risk. Just return the empty string in this case.
742 0 : bool openingExecutesScript = false;
743 : rv = NS_URIChainHasFlags(uri,
744 : nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT,
745 0 : &openingExecutesScript);
746 0 : if (NS_FAILED(rv) || openingExecutesScript) {
747 0 : return NS_OK;
748 : }
749 :
750 0 : nsCAutoString viewSourceUrl;
751 :
752 : // URLs that return data (e.g. "http:" URLs) should be prefixed with
753 : // "view-source:". URLs that don't return data should just be returned
754 : // undecorated.
755 0 : bool doesNotReturnData = false;
756 : rv = NS_URIChainHasFlags(uri,
757 : nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
758 0 : &doesNotReturnData);
759 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
760 0 : if (!doesNotReturnData) {
761 0 : viewSourceUrl.AssignLiteral("view-source:");
762 : }
763 :
764 0 : nsCAutoString spec;
765 0 : uri->GetSpec(spec);
766 :
767 0 : viewSourceUrl.Append(spec);
768 :
769 0 : nsAutoString utf16;
770 0 : CopyUTF8toUTF16(viewSourceUrl, utf16);
771 :
772 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::href, utf16, true);
773 0 : return rv;
774 : }
775 : case eTreeOpAddError: {
776 0 : nsIContent* node = *(mOne.node);
777 0 : char* msgId = mTwo.charPtr;
778 0 : nsCOMPtr<nsIAtom> atom = Reget(mThree.atom);
779 0 : nsCOMPtr<nsIAtom> otherAtom = Reget(mFour.atom);
780 : // See viewsource.css for the possible classes in addition to "error".
781 0 : nsAutoString klass;
782 0 : node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass);
783 0 : if (!klass.IsEmpty()) {
784 0 : klass.Append(NS_LITERAL_STRING(" error"));
785 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true);
786 : } else {
787 : node->SetAttr(kNameSpaceID_None,
788 : nsGkAtoms::_class,
789 0 : NS_LITERAL_STRING("error"),
790 0 : true);
791 : }
792 :
793 0 : nsXPIDLString message;
794 0 : if (otherAtom) {
795 0 : const PRUnichar* params[] = { atom->GetUTF16String(),
796 0 : otherAtom->GetUTF16String() };
797 : rv = nsContentUtils::FormatLocalizedString(
798 0 : nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message);
799 0 : NS_ENSURE_SUCCESS(rv, rv);
800 0 : } else if (atom) {
801 0 : const PRUnichar* params[] = { atom->GetUTF16String() };
802 : rv = nsContentUtils::FormatLocalizedString(
803 0 : nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message);
804 0 : NS_ENSURE_SUCCESS(rv, rv);
805 : } else {
806 : rv = nsContentUtils::GetLocalizedString(
807 0 : nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, message);
808 0 : NS_ENSURE_SUCCESS(rv, rv);
809 : }
810 :
811 0 : nsAutoString title;
812 0 : node->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
813 0 : if (!title.IsEmpty()) {
814 0 : title.Append('\n');
815 0 : title.Append(message);
816 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, title, true);
817 : } else {
818 0 : node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, message, true);
819 : }
820 0 : return rv;
821 : }
822 : default: {
823 0 : NS_NOTREACHED("Bogus tree op");
824 : }
825 : }
826 0 : return rv; // keep compiler happy
827 : }
|