|
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 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 } |
|
66 |
|
67 ~nsHtml5OtherDocUpdate() |
|
68 { |
|
69 if (MOZ_UNLIKELY(mDocument)) { |
|
70 mDocument->EndUpdate(UPDATE_CONTENT_MODEL); |
|
71 } |
|
72 } |
|
73 private: |
|
74 nsIDocument* mDocument; |
|
75 }; |
|
76 |
|
77 nsHtml5TreeOperation::nsHtml5TreeOperation() |
|
78 #ifdef DEBUG |
|
79 : mOpCode(eTreeOpUninitialized) |
|
80 #endif |
|
81 { |
|
82 MOZ_COUNT_CTOR(nsHtml5TreeOperation); |
|
83 } |
|
84 |
|
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 } |
|
118 |
|
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."); |
|
126 |
|
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); |
|
139 |
|
140 rv = aTextNode->AppendText(aBuffer, aLength, false); |
|
141 NS_ENSURE_SUCCESS(rv, rv); |
|
142 |
|
143 nsNodeUtils::CharacterDataChanged(aTextNode, &info); |
|
144 return rv; |
|
145 } |
|
146 |
|
147 return aTextNode->AppendText(aBuffer, aLength, false); |
|
148 } |
|
149 |
|
150 |
|
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 } |
|
167 |
|
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); |
|
172 |
|
173 return Append(text, aParent, aBuilder); |
|
174 } |
|
175 |
|
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."); |
|
186 |
|
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 } |
|
195 |
|
196 // The parent has been moved to another doc |
|
197 parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL); |
|
198 |
|
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 } |
|
207 |
|
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); |
|
222 |
|
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 } |
|
231 |
|
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 } |
|
249 |
|
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 } |
|
263 |
|
264 nsresult |
|
265 nsHtml5TreeOperation::AppendChildrenToNewParent(nsIContent* aNode, |
|
266 nsIContent* aParent, |
|
267 nsHtml5DocumentBuilder* aBuilder) |
|
268 { |
|
269 aBuilder->FlushPendingAppendNotifications(); |
|
270 |
|
271 nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), |
|
272 aBuilder->GetDocument()); |
|
273 |
|
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 } |
|
289 |
|
290 nsresult |
|
291 nsHtml5TreeOperation::FosterParent(nsIContent* aNode, |
|
292 nsIContent* aParent, |
|
293 nsIContent* aTable, |
|
294 nsHtml5DocumentBuilder* aBuilder) |
|
295 { |
|
296 nsIContent* foster = aTable->GetParent(); |
|
297 |
|
298 if (IsElementOrTemplateContent(foster)) { |
|
299 aBuilder->FlushPendingAppendNotifications(); |
|
300 |
|
301 nsHtml5OtherDocUpdate update(foster->OwnerDoc(), |
|
302 aBuilder->GetDocument()); |
|
303 |
|
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 } |
|
310 |
|
311 return Append(aNode, aParent, aBuilder); |
|
312 } |
|
313 |
|
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()); |
|
322 |
|
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 } |
|
344 |
|
345 |
|
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 } |
|
357 |
|
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."); |
|
366 |
|
367 aBuilder->HoldElement(newContent); |
|
368 |
|
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 } |
|
383 |
|
384 nsTArray<nsString> theContent; |
|
385 nsAutoString theAttribute; |
|
386 |
|
387 (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"), |
|
388 theContent, |
|
389 theAttribute); |
|
390 |
|
391 newContent->SetAttr(kNameSpaceID_None, |
|
392 nsGkAtoms::moztype, |
|
393 nullptr, |
|
394 theAttribute, |
|
395 false); |
|
396 |
|
397 nsCOMPtr<nsINodeInfo> optionNodeInfo = |
|
398 aBuilder->GetNodeInfoManager()->GetNodeInfo(nsHtml5Atoms::option, |
|
399 nullptr, |
|
400 kNameSpaceID_XHTML, |
|
401 nsIDOMNode::ELEMENT_NODE); |
|
402 |
|
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 } |
|
417 |
|
418 if (!aAttributes) { |
|
419 return newContent; |
|
420 } |
|
421 |
|
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); |
|
431 |
|
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); |
|
451 |
|
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 } |
|
465 |
|
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 } |
|
486 |
|
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 } |
|
504 |
|
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(); |
|
514 |
|
515 if (IsElementOrTemplateContent(foster)) { |
|
516 aBuilder->FlushPendingAppendNotifications(); |
|
517 |
|
518 nsHtml5OtherDocUpdate update(foster->OwnerDoc(), |
|
519 aBuilder->GetDocument()); |
|
520 |
|
521 uint32_t pos = foster->IndexOf(aTable); |
|
522 |
|
523 nsIContent* previousSibling = aTable->GetPreviousSibling(); |
|
524 if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) { |
|
525 return AppendTextToTextNode(aBuffer, |
|
526 aLength, |
|
527 previousSibling, |
|
528 aBuilder); |
|
529 } |
|
530 |
|
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); |
|
536 |
|
537 rv = foster->InsertChildAt(text, pos, false); |
|
538 NS_ENSURE_SUCCESS(rv, rv); |
|
539 nsNodeUtils::ContentInserted(foster, text, pos); |
|
540 return rv; |
|
541 } |
|
542 |
|
543 return AppendText(aBuffer, aLength, aStackParent, aBuilder); |
|
544 } |
|
545 |
|
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); |
|
557 |
|
558 return Append(comment, aParent, aBuilder); |
|
559 } |
|
560 |
|
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); |
|
571 |
|
572 return AppendToDocument(comment, aBuilder); |
|
573 } |
|
574 |
|
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 } |
|
596 |
|
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 } |
|
605 |
|
606 void |
|
607 nsHtml5TreeOperation::PreventScriptExecution(nsIContent* aNode) |
|
608 { |
|
609 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aNode); |
|
610 MOZ_ASSERT(sele); |
|
611 sele->PreventExecution(); |
|
612 } |
|
613 |
|
614 void |
|
615 nsHtml5TreeOperation::DoneAddingChildren(nsIContent* aNode, |
|
616 nsHtml5DocumentBuilder* aBuilder) |
|
617 { |
|
618 aNode->DoneAddingChildren(aBuilder->HaveNotified(aNode)); |
|
619 } |
|
620 |
|
621 void |
|
622 nsHtml5TreeOperation::DoneCreatingElement(nsIContent* aNode) |
|
623 { |
|
624 aNode->DoneCreatingElement(); |
|
625 } |
|
626 |
|
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 } |
|
635 |
|
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 } |
|
645 |
|
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; |
|
687 |
|
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; |
|
882 |
|
883 nsDependentString relative(buffer, length); |
|
884 |
|
885 nsIDocument* doc = aBuilder->GetDocument(); |
|
886 |
|
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); |
|
894 |
|
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 } |
|
907 |
|
908 nsAutoCString viewSourceUrl; |
|
909 |
|
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 } |
|
921 |
|
922 nsAutoCString spec; |
|
923 uri->GetSpec(spec); |
|
924 |
|
925 viewSourceUrl.Append(spec); |
|
926 |
|
927 nsAutoString utf16; |
|
928 CopyUTF8toUTF16(viewSourceUrl, utf16); |
|
929 |
|
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 } |
|
950 |
|
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 } |
|
969 |
|
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 } |