/src/mozilla-central/parser/html/nsHtml5TreeOperation.cpp
Line | Count | Source (jump to first uncovered line) |
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 "mozAutoDocUpdate.h" |
9 | | #include "mozilla/Likely.h" |
10 | | #include "mozilla/Services.h" |
11 | | #include "mozilla/dom/Comment.h" |
12 | | #include "mozilla/dom/DocumentType.h" |
13 | | #include "mozilla/dom/Element.h" |
14 | | #include "mozilla/dom/HTMLFormElement.h" |
15 | | #include "mozilla/dom/HTMLImageElement.h" |
16 | | #include "mozilla/dom/HTMLTemplateElement.h" |
17 | | #include "mozilla/dom/Text.h" |
18 | | #include "nsAttrName.h" |
19 | | #include "nsBindingManager.h" |
20 | | #include "nsContentCreatorFunctions.h" |
21 | | #include "nsContentUtils.h" |
22 | | #include "nsDocElementCreatedNotificationRunner.h" |
23 | | #include "nsEscape.h" |
24 | | #include "nsHtml5AutoPauseUpdate.h" |
25 | | #include "nsHtml5DocumentMode.h" |
26 | | #include "nsHtml5HtmlAttributes.h" |
27 | | #include "nsHtml5SVGLoadDispatcher.h" |
28 | | #include "nsHtml5TreeBuilder.h" |
29 | | #include "nsIDTD.h" |
30 | | #include "nsIFormControl.h" |
31 | | #include "nsIFormProcessor.h" |
32 | | #include "nsIHTMLDocument.h" |
33 | | #include "nsIMutationObserver.h" |
34 | | #include "nsINode.h" |
35 | | #include "nsIObserverService.h" |
36 | | #include "nsIProtocolHandler.h" |
37 | | #include "nsIScriptElement.h" |
38 | | #include "nsIServiceManager.h" |
39 | | #include "nsIStyleSheetLinkingElement.h" |
40 | | #include "nsISupportsImpl.h" |
41 | | #include "nsIURI.h" |
42 | | #include "nsNetUtil.h" |
43 | | #include "nsNodeUtils.h" |
44 | | #include "nsTextNode.h" |
45 | | #include "nsXBLBinding.h" |
46 | | |
47 | | using namespace mozilla; |
48 | | |
49 | | static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID); |
50 | | |
51 | | /** |
52 | | * Helper class that opens a notification batch if the current doc |
53 | | * is different from the executor doc. |
54 | | */ |
55 | | class MOZ_STACK_CLASS nsHtml5OtherDocUpdate |
56 | | { |
57 | | public: |
58 | | nsHtml5OtherDocUpdate(nsIDocument* aCurrentDoc, nsIDocument* aExecutorDoc) |
59 | 0 | { |
60 | 0 | MOZ_ASSERT(aCurrentDoc, "Node has no doc?"); |
61 | 0 | MOZ_ASSERT(aExecutorDoc, "Executor has no doc?"); |
62 | 0 | if (MOZ_LIKELY(aCurrentDoc == aExecutorDoc)) { |
63 | 0 | mDocument = nullptr; |
64 | 0 | } else { |
65 | 0 | mDocument = aCurrentDoc; |
66 | 0 | aCurrentDoc->BeginUpdate(); |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | | ~nsHtml5OtherDocUpdate() |
71 | 0 | { |
72 | 0 | if (MOZ_UNLIKELY(mDocument)) { |
73 | 0 | mDocument->EndUpdate(); |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | private: |
78 | | nsCOMPtr<nsIDocument> mDocument; |
79 | | }; |
80 | | |
81 | | nsHtml5TreeOperation::nsHtml5TreeOperation() |
82 | | : mOpCode(eTreeOpUninitialized) |
83 | | , mOne{} |
84 | | , mTwo{} |
85 | | , mThree{} |
86 | | , mFour{} |
87 | | , mFive{} |
88 | 0 | { |
89 | 0 | MOZ_COUNT_CTOR(nsHtml5TreeOperation); |
90 | 0 | } |
91 | | |
92 | | nsHtml5TreeOperation::~nsHtml5TreeOperation() |
93 | 0 | { |
94 | 0 | MOZ_COUNT_DTOR(nsHtml5TreeOperation); |
95 | 0 | NS_ASSERTION(mOpCode != eTreeOpUninitialized, "Uninitialized tree op."); |
96 | 0 | switch (mOpCode) { |
97 | 0 | case eTreeOpAddAttributes: |
98 | 0 | delete mTwo.attributes; |
99 | 0 | break; |
100 | 0 | case eTreeOpCreateHTMLElementNetwork: |
101 | 0 | case eTreeOpCreateHTMLElementNotNetwork: |
102 | 0 | case eTreeOpCreateSVGElementNetwork: |
103 | 0 | case eTreeOpCreateSVGElementNotNetwork: |
104 | 0 | case eTreeOpCreateMathMLElement: |
105 | 0 | delete mThree.attributes; |
106 | 0 | break; |
107 | 0 | case eTreeOpAppendDoctypeToDocument: |
108 | 0 | delete mTwo.stringPair; |
109 | 0 | break; |
110 | 0 | case eTreeOpFosterParentText: |
111 | 0 | case eTreeOpAppendText: |
112 | 0 | case eTreeOpAppendComment: |
113 | 0 | case eTreeOpAppendCommentToDocument: |
114 | 0 | case eTreeOpAddViewSourceHref: |
115 | 0 | case eTreeOpAddViewSourceBase: |
116 | 0 | delete[] mTwo.unicharPtr; |
117 | 0 | break; |
118 | 0 | case eTreeOpSetDocumentCharset: |
119 | 0 | case eTreeOpNeedsCharsetSwitchTo: |
120 | 0 | break; |
121 | 0 | case eTreeOpProcessOfflineManifest: |
122 | 0 | free(mOne.unicharPtr); |
123 | 0 | break; |
124 | 0 | default: // keep the compiler happy |
125 | 0 | break; |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | | nsresult |
130 | | nsHtml5TreeOperation::AppendTextToTextNode(const char16_t* aBuffer, |
131 | | uint32_t aLength, |
132 | | dom::Text* aTextNode, |
133 | | nsHtml5DocumentBuilder* aBuilder) |
134 | 0 | { |
135 | 0 | MOZ_ASSERT(aTextNode, "Got null text node."); |
136 | 0 | MOZ_ASSERT(aBuilder); |
137 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
138 | 0 | uint32_t oldLength = aTextNode->TextLength(); |
139 | 0 | CharacterDataChangeInfo info = { true, oldLength, oldLength, aLength }; |
140 | 0 | nsNodeUtils::CharacterDataWillChange(aTextNode, info); |
141 | 0 |
|
142 | 0 | nsresult rv = aTextNode->AppendText(aBuffer, aLength, false); |
143 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
144 | 0 |
|
145 | 0 | nsNodeUtils::CharacterDataChanged(aTextNode, info); |
146 | 0 | return rv; |
147 | 0 | } |
148 | | |
149 | | nsresult |
150 | | nsHtml5TreeOperation::AppendText(const char16_t* aBuffer, |
151 | | uint32_t aLength, |
152 | | nsIContent* aParent, |
153 | | nsHtml5DocumentBuilder* aBuilder) |
154 | 0 | { |
155 | 0 | nsresult rv = NS_OK; |
156 | 0 | nsIContent* lastChild = aParent->GetLastChild(); |
157 | 0 | if (lastChild && lastChild->IsText()) { |
158 | 0 | nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), aBuilder->GetDocument()); |
159 | 0 | return AppendTextToTextNode(aBuffer, aLength, lastChild->GetAsText(), |
160 | 0 | aBuilder); |
161 | 0 | } |
162 | 0 | |
163 | 0 | nsNodeInfoManager* nodeInfoManager = aParent->OwnerDoc()->NodeInfoManager(); |
164 | 0 | RefPtr<nsTextNode> text = new nsTextNode(nodeInfoManager); |
165 | 0 | NS_ASSERTION(text, "Infallible malloc failed?"); |
166 | 0 | rv = text->SetText(aBuffer, aLength, false); |
167 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
168 | 0 |
|
169 | 0 | return Append(text, aParent, aBuilder); |
170 | 0 | } |
171 | | |
172 | | nsresult |
173 | | nsHtml5TreeOperation::Append(nsIContent* aNode, |
174 | | nsIContent* aParent, |
175 | | nsHtml5DocumentBuilder* aBuilder) |
176 | 0 | { |
177 | 0 | MOZ_ASSERT(aBuilder); |
178 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
179 | 0 | nsresult rv = NS_OK; |
180 | 0 | nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), aBuilder->GetDocument()); |
181 | 0 | rv = aParent->AppendChildTo(aNode, false); |
182 | 0 | if (NS_SUCCEEDED(rv)) { |
183 | 0 | aNode->SetParserHasNotified(); |
184 | 0 | nsNodeUtils::ContentAppended(aParent, aNode); |
185 | 0 | } |
186 | 0 | return rv; |
187 | 0 | } |
188 | | |
189 | | nsresult |
190 | | nsHtml5TreeOperation::AppendToDocument(nsIContent* aNode, |
191 | | nsHtml5DocumentBuilder* aBuilder) |
192 | 0 | { |
193 | 0 | MOZ_ASSERT(aBuilder); |
194 | 0 | MOZ_ASSERT(aBuilder->GetDocument() == aNode->OwnerDoc()); |
195 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
196 | 0 | nsresult rv = NS_OK; |
197 | 0 |
|
198 | 0 | nsIDocument* doc = aBuilder->GetDocument(); |
199 | 0 | rv = doc->AppendChildTo(aNode, false); |
200 | 0 | if (rv == NS_ERROR_DOM_HIERARCHY_REQUEST_ERR) { |
201 | 0 | aNode->SetParserHasNotified(); |
202 | 0 | return NS_OK; |
203 | 0 | } |
204 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
205 | 0 | aNode->SetParserHasNotified(); |
206 | 0 | nsNodeUtils::ContentInserted(doc, aNode); |
207 | 0 |
|
208 | 0 | NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), |
209 | 0 | "Someone forgot to block scripts"); |
210 | 0 | if (aNode->IsElement()) { |
211 | 0 | nsContentUtils::AddScriptRunner( |
212 | 0 | new nsDocElementCreatedNotificationRunner(doc)); |
213 | 0 | } |
214 | 0 | return rv; |
215 | 0 | } |
216 | | |
217 | | static bool |
218 | | IsElementOrTemplateContent(nsINode* aNode) |
219 | 0 | { |
220 | 0 | if (aNode) { |
221 | 0 | if (aNode->IsElement()) { |
222 | 0 | return true; |
223 | 0 | } |
224 | 0 | if (aNode->IsDocumentFragment()) { |
225 | 0 | // Check if the node is a template content. |
226 | 0 | nsIContent* fragHost = aNode->AsDocumentFragment()->GetHost(); |
227 | 0 | if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) { |
228 | 0 | return true; |
229 | 0 | } |
230 | 0 | } |
231 | 0 | } |
232 | 0 | return false; |
233 | 0 | } |
234 | | |
235 | | void |
236 | | nsHtml5TreeOperation::Detach(nsIContent* aNode, |
237 | | nsHtml5DocumentBuilder* aBuilder) |
238 | 0 | { |
239 | 0 | MOZ_ASSERT(aBuilder); |
240 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
241 | 0 | nsCOMPtr<nsINode> parent = aNode->GetParentNode(); |
242 | 0 | if (parent) { |
243 | 0 | nsHtml5OtherDocUpdate update(parent->OwnerDoc(), aBuilder->GetDocument()); |
244 | 0 | parent->RemoveChildNode(aNode, true); |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | nsresult |
249 | | nsHtml5TreeOperation::AppendChildrenToNewParent( |
250 | | nsIContent* aNode, |
251 | | nsIContent* aParent, |
252 | | nsHtml5DocumentBuilder* aBuilder) |
253 | 0 | { |
254 | 0 | MOZ_ASSERT(aBuilder); |
255 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
256 | 0 | nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), aBuilder->GetDocument()); |
257 | 0 |
|
258 | 0 | bool didAppend = false; |
259 | 0 | while (aNode->HasChildren()) { |
260 | 0 | nsCOMPtr<nsIContent> child = aNode->GetFirstChild(); |
261 | 0 | aNode->RemoveChildNode(child, true); |
262 | 0 | nsresult rv = aParent->AppendChildTo(child, false); |
263 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
264 | 0 | didAppend = true; |
265 | 0 | } |
266 | 0 | if (didAppend) { |
267 | 0 | nsNodeUtils::ContentAppended(aParent, aParent->GetLastChild()); |
268 | 0 | } |
269 | 0 | return NS_OK; |
270 | 0 | } |
271 | | |
272 | | nsresult |
273 | | nsHtml5TreeOperation::FosterParent(nsIContent* aNode, |
274 | | nsIContent* aParent, |
275 | | nsIContent* aTable, |
276 | | nsHtml5DocumentBuilder* aBuilder) |
277 | 0 | { |
278 | 0 | MOZ_ASSERT(aBuilder); |
279 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
280 | 0 | nsIContent* foster = aTable->GetParent(); |
281 | 0 |
|
282 | 0 | if (IsElementOrTemplateContent(foster)) { |
283 | 0 |
|
284 | 0 | nsHtml5OtherDocUpdate update(foster->OwnerDoc(), aBuilder->GetDocument()); |
285 | 0 |
|
286 | 0 | nsresult rv = foster->InsertChildBefore(aNode, aTable, false); |
287 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
288 | 0 | nsNodeUtils::ContentInserted(foster, aNode); |
289 | 0 | return rv; |
290 | 0 | } |
291 | 0 | |
292 | 0 | return Append(aNode, aParent, aBuilder); |
293 | 0 | } |
294 | | |
295 | | nsresult |
296 | | nsHtml5TreeOperation::AddAttributes(nsIContent* aNode, |
297 | | nsHtml5HtmlAttributes* aAttributes, |
298 | | nsHtml5DocumentBuilder* aBuilder) |
299 | 0 | { |
300 | 0 | dom::Element* node = aNode->AsElement(); |
301 | 0 | nsHtml5OtherDocUpdate update(node->OwnerDoc(), aBuilder->GetDocument()); |
302 | 0 |
|
303 | 0 | int32_t len = aAttributes->getLength(); |
304 | 0 | for (int32_t i = len; i > 0;) { |
305 | 0 | --i; |
306 | 0 | // prefix doesn't need regetting. it is always null or a static atom |
307 | 0 | // local name is never null |
308 | 0 | RefPtr<nsAtom> localName = Reget(aAttributes->getLocalNameNoBoundsCheck(i)); |
309 | 0 | int32_t nsuri = aAttributes->getURINoBoundsCheck(i); |
310 | 0 | if (!node->HasAttr(nsuri, localName)) { |
311 | 0 | // prefix doesn't need regetting. it is always null or a static atom |
312 | 0 | // local name is never null |
313 | 0 | nsString value; // Not Auto, because using it to hold nsStringBuffer* |
314 | 0 | aAttributes->getValueNoBoundsCheck(i).ToString(value); |
315 | 0 | node->SetAttr( |
316 | 0 | nsuri, localName, aAttributes->getPrefixNoBoundsCheck(i), value, true); |
317 | 0 | // XXX what to do with nsresult? |
318 | 0 | } |
319 | 0 | } |
320 | 0 | return NS_OK; |
321 | 0 | } |
322 | | |
323 | | void |
324 | | nsHtml5TreeOperation::SetHTMLElementAttributes( |
325 | | dom::Element* aElement, |
326 | | nsAtom* aName, |
327 | | nsHtml5HtmlAttributes* aAttributes) |
328 | 0 | { |
329 | 0 | int32_t len = aAttributes->getLength(); |
330 | 0 | for (int32_t i = 0; i < len; i++) { |
331 | 0 | nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); |
332 | 0 | nsAtom* klass = val.MaybeAsAtom(); |
333 | 0 | if (klass) { |
334 | 0 | aElement->SetSingleClassFromParser(klass); |
335 | 0 | } else { |
336 | 0 | // prefix doesn't need regetting. it is always null or a static atom |
337 | 0 | // local name is never null |
338 | 0 | RefPtr<nsAtom> localName = |
339 | 0 | Reget(aAttributes->getLocalNameNoBoundsCheck(i)); |
340 | 0 | RefPtr<nsAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); |
341 | 0 | int32_t nsuri = aAttributes->getURINoBoundsCheck(i); |
342 | 0 |
|
343 | 0 | nsString value; // Not Auto, because using it to hold nsStringBuffer* |
344 | 0 | val.ToString(value); |
345 | 0 | if (nsGkAtoms::a == aName && nsGkAtoms::name == localName) { |
346 | 0 | // This is an HTML5-incompliant Geckoism. |
347 | 0 | // Remove when fixing bug 582361 |
348 | 0 | NS_ConvertUTF16toUTF8 cname(value); |
349 | 0 | NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting())); |
350 | 0 | aElement->SetAttr(nsuri, localName, prefix, uv, false); |
351 | 0 | } else { |
352 | 0 | aElement->SetAttr(nsuri, localName, prefix, value, false); |
353 | 0 | } |
354 | 0 | } |
355 | 0 | } |
356 | 0 | } |
357 | | |
358 | | nsIContent* |
359 | | nsHtml5TreeOperation::CreateHTMLElement( |
360 | | nsAtom* aName, |
361 | | nsHtml5HtmlAttributes* aAttributes, |
362 | | mozilla::dom::FromParser aFromParser, |
363 | | nsNodeInfoManager* aNodeInfoManager, |
364 | | nsHtml5DocumentBuilder* aBuilder, |
365 | | mozilla::dom::HTMLContentCreatorFunction aCreator) |
366 | 0 | { |
367 | 0 | bool isKeygen = (aName == nsGkAtoms::keygen); |
368 | 0 | if (MOZ_UNLIKELY(isKeygen)) { |
369 | 0 | aName = nsGkAtoms::select; |
370 | 0 | aCreator = NS_NewHTMLSelectElement; |
371 | 0 | } |
372 | 0 |
|
373 | 0 | RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( |
374 | 0 | aName, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE); |
375 | 0 | NS_ASSERTION(nodeInfo, "Got null nodeinfo."); |
376 | 0 |
|
377 | 0 | dom::Element* newContent = nullptr; |
378 | 0 | nsIDocument* document = nodeInfo->GetDocument(); |
379 | 0 | bool willExecuteScript = false; |
380 | 0 | bool isCustomElement = false; |
381 | 0 | RefPtr<nsAtom> isAtom; |
382 | 0 | dom::CustomElementDefinition* definition = nullptr; |
383 | 0 |
|
384 | 0 | // Avoid overhead by checking if custom elements pref is enabled or not. |
385 | 0 | if (dom::CustomElementRegistry::IsCustomElementEnabled(document)) { |
386 | 0 | if (aAttributes) { |
387 | 0 | nsHtml5String is = aAttributes->getValue(nsHtml5AttributeName::ATTR_IS); |
388 | 0 | if (is) { |
389 | 0 | nsAutoString isValue; |
390 | 0 | is.ToString(isValue); |
391 | 0 | isAtom = NS_Atomize(isValue); |
392 | 0 | } |
393 | 0 | } |
394 | 0 |
|
395 | 0 | isCustomElement = (aCreator == NS_NewCustomElement || isAtom); |
396 | 0 | if (isCustomElement && aFromParser != dom::FROM_PARSER_FRAGMENT) { |
397 | 0 | RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom(); |
398 | 0 | RefPtr<nsAtom> typeAtom = |
399 | 0 | (aCreator == NS_NewCustomElement) ? tagAtom : isAtom; |
400 | 0 |
|
401 | 0 | MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName())); |
402 | 0 | definition = nsContentUtils::LookupCustomElementDefinition( |
403 | 0 | document, nodeInfo->NameAtom(), nodeInfo->NamespaceID(), typeAtom); |
404 | 0 |
|
405 | 0 | if (definition) { |
406 | 0 | willExecuteScript = true; |
407 | 0 | } |
408 | 0 | } |
409 | 0 | } |
410 | 0 |
|
411 | 0 | if (willExecuteScript) { // This will cause custom element constructors to run |
412 | 0 | AutoSetThrowOnDynamicMarkupInsertionCounter |
413 | 0 | throwOnDynamicMarkupInsertionCounter(document); |
414 | 0 | nsHtml5AutoPauseUpdate autoPauseContentUpdate(aBuilder); |
415 | 0 | { |
416 | 0 | nsAutoMicroTask mt; |
417 | 0 | } |
418 | 0 | dom::AutoCEReaction autoCEReaction( |
419 | 0 | document->GetDocGroup()->CustomElementReactionsStack(), nullptr); |
420 | 0 |
|
421 | 0 | nsCOMPtr<dom::Element> newElement; |
422 | 0 | NS_NewHTMLElement(getter_AddRefs(newElement), |
423 | 0 | nodeInfo.forget(), |
424 | 0 | aFromParser, |
425 | 0 | isAtom, |
426 | 0 | definition); |
427 | 0 |
|
428 | 0 | MOZ_ASSERT(newElement, "Element creation created null pointer."); |
429 | 0 | newContent = newElement; |
430 | 0 | aBuilder->HoldElement(newElement.forget()); |
431 | 0 |
|
432 | 0 | if (MOZ_UNLIKELY(aName == nsGkAtoms::style || aName == nsGkAtoms::link)) { |
433 | 0 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent)); |
434 | 0 | if (ssle) { |
435 | 0 | ssle->InitStyleLinkElement(false); |
436 | 0 | ssle->SetEnableUpdates(false); |
437 | 0 | } |
438 | 0 | } |
439 | 0 |
|
440 | 0 | if (!aAttributes) { |
441 | 0 | return newContent; |
442 | 0 | } |
443 | 0 | |
444 | 0 | SetHTMLElementAttributes(newContent, aName, aAttributes); |
445 | 0 | } else { |
446 | 0 | nsCOMPtr<dom::Element> newElement; |
447 | 0 |
|
448 | 0 | if (isCustomElement) { |
449 | 0 | NS_NewHTMLElement(getter_AddRefs(newElement), |
450 | 0 | nodeInfo.forget(), |
451 | 0 | aFromParser, |
452 | 0 | isAtom, |
453 | 0 | definition); |
454 | 0 | } else { |
455 | 0 | newElement = aCreator(nodeInfo.forget(), aFromParser); |
456 | 0 | } |
457 | 0 |
|
458 | 0 | MOZ_ASSERT(newElement, "Element creation created null pointer."); |
459 | 0 |
|
460 | 0 | newContent = newElement; |
461 | 0 | aBuilder->HoldElement(newElement.forget()); |
462 | 0 |
|
463 | 0 | if (MOZ_UNLIKELY(aName == nsGkAtoms::style || aName == nsGkAtoms::link)) { |
464 | 0 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent)); |
465 | 0 | if (ssle) { |
466 | 0 | ssle->InitStyleLinkElement(false); |
467 | 0 | ssle->SetEnableUpdates(false); |
468 | 0 | } |
469 | 0 | } else if (MOZ_UNLIKELY(isKeygen)) { |
470 | 0 | // Adapted from CNavDTD |
471 | 0 | nsresult rv; |
472 | 0 | nsCOMPtr<nsIFormProcessor> theFormProcessor = |
473 | 0 | do_GetService(kFormProcessorCID, &rv); |
474 | 0 | if (NS_FAILED(rv)) { |
475 | 0 | return newContent; |
476 | 0 | } |
477 | 0 | |
478 | 0 | nsTArray<nsString> theContent; |
479 | 0 | nsAutoString theAttribute; |
480 | 0 |
|
481 | 0 | (void)theFormProcessor->ProvideContent( |
482 | 0 | NS_LITERAL_STRING("select"), theContent, theAttribute); |
483 | 0 |
|
484 | 0 | newContent->SetAttr( |
485 | 0 | kNameSpaceID_None, nsGkAtoms::moztype, nullptr, theAttribute, false); |
486 | 0 |
|
487 | 0 | RefPtr<dom::NodeInfo> optionNodeInfo = aNodeInfoManager->GetNodeInfo( |
488 | 0 | nsGkAtoms::option, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE); |
489 | 0 |
|
490 | 0 | for (uint32_t i = 0; i < theContent.Length(); ++i) { |
491 | 0 | RefPtr<dom::NodeInfo> ni = optionNodeInfo; |
492 | 0 | nsCOMPtr<dom::Element> optionElt = |
493 | 0 | NS_NewHTMLOptionElement(ni.forget(), aFromParser); |
494 | 0 | RefPtr<nsTextNode> optionText = new nsTextNode(aNodeInfoManager); |
495 | 0 | (void)optionText->SetText(theContent[i], false); |
496 | 0 | optionElt->AppendChildTo(optionText, false); |
497 | 0 | newContent->AppendChildTo(optionElt, false); |
498 | 0 | } |
499 | 0 | newContent->DoneAddingChildren(false); |
500 | 0 | } |
501 | 0 |
|
502 | 0 | if (!aAttributes) { |
503 | 0 | return newContent; |
504 | 0 | } |
505 | 0 | |
506 | 0 | SetHTMLElementAttributes(newContent, aName, aAttributes); |
507 | 0 | } |
508 | 0 |
|
509 | 0 | return newContent; |
510 | 0 | } |
511 | | |
512 | | nsIContent* |
513 | | nsHtml5TreeOperation::CreateSVGElement( |
514 | | nsAtom* aName, |
515 | | nsHtml5HtmlAttributes* aAttributes, |
516 | | mozilla::dom::FromParser aFromParser, |
517 | | nsNodeInfoManager* aNodeInfoManager, |
518 | | nsHtml5DocumentBuilder* aBuilder, |
519 | | mozilla::dom::SVGContentCreatorFunction aCreator) |
520 | 0 | { |
521 | 0 | nsCOMPtr<nsIContent> newElement; |
522 | 0 | if (MOZ_LIKELY(aNodeInfoManager->SVGEnabled())) { |
523 | 0 | RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( |
524 | 0 | aName, nullptr, kNameSpaceID_SVG, nsINode::ELEMENT_NODE); |
525 | 0 | MOZ_ASSERT(nodeInfo, "Got null nodeinfo."); |
526 | 0 |
|
527 | 0 | mozilla::DebugOnly<nsresult> rv = |
528 | 0 | aCreator(getter_AddRefs(newElement), nodeInfo.forget(), aFromParser); |
529 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv) && newElement); |
530 | 0 | } else { |
531 | 0 | RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( |
532 | 0 | aName, nullptr, kNameSpaceID_disabled_SVG, nsINode::ELEMENT_NODE); |
533 | 0 | MOZ_ASSERT(nodeInfo, "Got null nodeinfo."); |
534 | 0 |
|
535 | 0 | // The mismatch between NS_NewXMLElement and SVGContentCreatorFunction |
536 | 0 | // argument types is annoying. |
537 | 0 | nsCOMPtr<dom::Element> xmlElement; |
538 | 0 | mozilla::DebugOnly<nsresult> rv = |
539 | 0 | NS_NewXMLElement(getter_AddRefs(xmlElement), nodeInfo.forget()); |
540 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv) && xmlElement); |
541 | 0 | newElement = xmlElement; |
542 | 0 | } |
543 | 0 |
|
544 | 0 | dom::Element* newContent = newElement->AsElement(); |
545 | 0 | aBuilder->HoldElement(newElement.forget()); |
546 | 0 |
|
547 | 0 | if (MOZ_UNLIKELY(aName == nsGkAtoms::style)) { |
548 | 0 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent)); |
549 | 0 | if (ssle) { |
550 | 0 | ssle->InitStyleLinkElement(false); |
551 | 0 | ssle->SetEnableUpdates(false); |
552 | 0 | } |
553 | 0 | } |
554 | 0 |
|
555 | 0 | if (!aAttributes) { |
556 | 0 | return newContent; |
557 | 0 | } |
558 | 0 | |
559 | 0 | int32_t len = aAttributes->getLength(); |
560 | 0 | for (int32_t i = 0; i < len; i++) { |
561 | 0 | nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); |
562 | 0 | nsAtom* klass = val.MaybeAsAtom(); |
563 | 0 | if (klass) { |
564 | 0 | newContent->SetSingleClassFromParser(klass); |
565 | 0 | } else { |
566 | 0 | // prefix doesn't need regetting. it is always null or a static atom |
567 | 0 | // local name is never null |
568 | 0 | RefPtr<nsAtom> localName = |
569 | 0 | Reget(aAttributes->getLocalNameNoBoundsCheck(i)); |
570 | 0 | RefPtr<nsAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); |
571 | 0 | int32_t nsuri = aAttributes->getURINoBoundsCheck(i); |
572 | 0 |
|
573 | 0 | nsString value; // Not Auto, because using it to hold nsStringBuffer* |
574 | 0 | val.ToString(value); |
575 | 0 | newContent->SetAttr(nsuri, localName, prefix, value, false); |
576 | 0 | } |
577 | 0 | } |
578 | 0 | return newContent; |
579 | 0 | } |
580 | | |
581 | | nsIContent* |
582 | | nsHtml5TreeOperation::CreateMathMLElement(nsAtom* aName, |
583 | | nsHtml5HtmlAttributes* aAttributes, |
584 | | nsNodeInfoManager* aNodeInfoManager, |
585 | | nsHtml5DocumentBuilder* aBuilder) |
586 | 0 | { |
587 | 0 | nsCOMPtr<dom::Element> newElement; |
588 | 0 | if (MOZ_LIKELY(aNodeInfoManager->MathMLEnabled())) { |
589 | 0 | RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( |
590 | 0 | aName, nullptr, kNameSpaceID_MathML, nsINode::ELEMENT_NODE); |
591 | 0 | NS_ASSERTION(nodeInfo, "Got null nodeinfo."); |
592 | 0 |
|
593 | 0 | mozilla::DebugOnly<nsresult> rv = |
594 | 0 | NS_NewMathMLElement(getter_AddRefs(newElement), nodeInfo.forget()); |
595 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv) && newElement); |
596 | 0 | } else { |
597 | 0 | RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( |
598 | 0 | aName, nullptr, kNameSpaceID_disabled_MathML, nsINode::ELEMENT_NODE); |
599 | 0 | NS_ASSERTION(nodeInfo, "Got null nodeinfo."); |
600 | 0 |
|
601 | 0 | mozilla::DebugOnly<nsresult> rv = |
602 | 0 | NS_NewXMLElement(getter_AddRefs(newElement), nodeInfo.forget()); |
603 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv) && newElement); |
604 | 0 | } |
605 | 0 |
|
606 | 0 | dom::Element* newContent = newElement; |
607 | 0 | aBuilder->HoldElement(newElement.forget()); |
608 | 0 |
|
609 | 0 | if (!aAttributes) { |
610 | 0 | return newContent; |
611 | 0 | } |
612 | 0 | |
613 | 0 | int32_t len = aAttributes->getLength(); |
614 | 0 | for (int32_t i = 0; i < len; i++) { |
615 | 0 | nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); |
616 | 0 | nsAtom* klass = val.MaybeAsAtom(); |
617 | 0 | if (klass) { |
618 | 0 | newContent->SetSingleClassFromParser(klass); |
619 | 0 | } else { |
620 | 0 | // prefix doesn't need regetting. it is always null or a static atom |
621 | 0 | // local name is never null |
622 | 0 | RefPtr<nsAtom> localName = |
623 | 0 | Reget(aAttributes->getLocalNameNoBoundsCheck(i)); |
624 | 0 | RefPtr<nsAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); |
625 | 0 | int32_t nsuri = aAttributes->getURINoBoundsCheck(i); |
626 | 0 |
|
627 | 0 | nsString value; // Not Auto, because using it to hold nsStringBuffer* |
628 | 0 | val.ToString(value); |
629 | 0 | newContent->SetAttr(nsuri, localName, prefix, value, false); |
630 | 0 | } |
631 | 0 | } |
632 | 0 | return newContent; |
633 | 0 | } |
634 | | |
635 | | void |
636 | | nsHtml5TreeOperation::SetFormElement(nsIContent* aNode, nsIContent* aParent) |
637 | 0 | { |
638 | 0 | nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aNode)); |
639 | 0 | RefPtr<dom::HTMLImageElement> domImageElement = |
640 | 0 | dom::HTMLImageElement::FromNodeOrNull(aNode); |
641 | 0 | // NS_ASSERTION(formControl, "Form-associated element did not implement |
642 | 0 | // nsIFormControl."); |
643 | 0 | // TODO: uncomment the above line when <keygen> (bug 101019) is supported by |
644 | 0 | // Gecko |
645 | 0 | RefPtr<dom::HTMLFormElement> formElement = |
646 | 0 | dom::HTMLFormElement::FromNodeOrNull(aParent); |
647 | 0 | NS_ASSERTION(formElement, |
648 | 0 | "The form element doesn't implement HTMLFormElement."); |
649 | 0 | // avoid crashing on <keygen> |
650 | 0 | if (formControl && |
651 | 0 | !aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::form)) { |
652 | 0 | formControl->SetForm(formElement); |
653 | 0 | } else if (domImageElement) { |
654 | 0 | domImageElement->SetForm(formElement); |
655 | 0 | } |
656 | 0 | } |
657 | | |
658 | | nsresult |
659 | | nsHtml5TreeOperation::FosterParentText(nsIContent* aStackParent, |
660 | | char16_t* aBuffer, |
661 | | uint32_t aLength, |
662 | | nsIContent* aTable, |
663 | | nsHtml5DocumentBuilder* aBuilder) |
664 | 0 | { |
665 | 0 | MOZ_ASSERT(aBuilder); |
666 | 0 | MOZ_ASSERT(aBuilder->IsInDocUpdate()); |
667 | 0 | nsresult rv = NS_OK; |
668 | 0 | nsIContent* foster = aTable->GetParent(); |
669 | 0 |
|
670 | 0 | if (IsElementOrTemplateContent(foster)) { |
671 | 0 | nsHtml5OtherDocUpdate update(foster->OwnerDoc(), aBuilder->GetDocument()); |
672 | 0 |
|
673 | 0 | nsIContent* previousSibling = aTable->GetPreviousSibling(); |
674 | 0 | if (previousSibling && previousSibling->IsText()) { |
675 | 0 | return AppendTextToTextNode(aBuffer, aLength, |
676 | 0 | previousSibling->GetAsText(), aBuilder); |
677 | 0 | } |
678 | 0 | |
679 | 0 | nsNodeInfoManager* nodeInfoManager = |
680 | 0 | aStackParent->OwnerDoc()->NodeInfoManager(); |
681 | 0 | RefPtr<nsTextNode> text = new nsTextNode(nodeInfoManager); |
682 | 0 | NS_ASSERTION(text, "Infallible malloc failed?"); |
683 | 0 | rv = text->SetText(aBuffer, aLength, false); |
684 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
685 | 0 |
|
686 | 0 | rv = foster->InsertChildBefore(text, aTable, false); |
687 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
688 | 0 | nsNodeUtils::ContentInserted(foster, text); |
689 | 0 | return rv; |
690 | 0 | } |
691 | 0 | |
692 | 0 | return AppendText(aBuffer, aLength, aStackParent, aBuilder); |
693 | 0 | } |
694 | | |
695 | | nsresult |
696 | | nsHtml5TreeOperation::AppendComment(nsIContent* aParent, |
697 | | char16_t* aBuffer, |
698 | | int32_t aLength, |
699 | | nsHtml5DocumentBuilder* aBuilder) |
700 | 0 | { |
701 | 0 | nsNodeInfoManager* nodeInfoManager = aParent->OwnerDoc()->NodeInfoManager(); |
702 | 0 | RefPtr<dom::Comment> comment = new dom::Comment(nodeInfoManager); |
703 | 0 | NS_ASSERTION(comment, "Infallible malloc failed?"); |
704 | 0 | nsresult rv = comment->SetText(aBuffer, aLength, false); |
705 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
706 | 0 |
|
707 | 0 | return Append(comment, aParent, aBuilder); |
708 | 0 | } |
709 | | |
710 | | nsresult |
711 | | nsHtml5TreeOperation::AppendCommentToDocument(char16_t* aBuffer, |
712 | | int32_t aLength, |
713 | | nsHtml5DocumentBuilder* aBuilder) |
714 | 0 | { |
715 | 0 | RefPtr<dom::Comment> comment = |
716 | 0 | new dom::Comment(aBuilder->GetNodeInfoManager()); |
717 | 0 | NS_ASSERTION(comment, "Infallible malloc failed?"); |
718 | 0 | nsresult rv = comment->SetText(aBuffer, aLength, false); |
719 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
720 | 0 |
|
721 | 0 | return AppendToDocument(comment, aBuilder); |
722 | 0 | } |
723 | | |
724 | | nsresult |
725 | | nsHtml5TreeOperation::AppendDoctypeToDocument(nsAtom* aName, |
726 | | const nsAString& aPublicId, |
727 | | const nsAString& aSystemId, |
728 | | nsHtml5DocumentBuilder* aBuilder) |
729 | 0 | { |
730 | 0 | // Adapted from nsXMLContentSink |
731 | 0 | // Create a new doctype node |
732 | 0 | RefPtr<dom::DocumentType> docType = NS_NewDOMDocumentType( |
733 | 0 | aBuilder->GetNodeInfoManager(), aName, aPublicId, aSystemId, VoidString()); |
734 | 0 | return AppendToDocument(docType, aBuilder); |
735 | 0 | } |
736 | | |
737 | | nsIContent* |
738 | | nsHtml5TreeOperation::GetDocumentFragmentForTemplate(nsIContent* aNode) |
739 | 0 | { |
740 | 0 | dom::HTMLTemplateElement* tempElem = |
741 | 0 | static_cast<dom::HTMLTemplateElement*>(aNode); |
742 | 0 | RefPtr<dom::DocumentFragment> frag = tempElem->Content(); |
743 | 0 | return frag; |
744 | 0 | } |
745 | | |
746 | | nsIContent* |
747 | | nsHtml5TreeOperation::GetFosterParent(nsIContent* aTable, |
748 | | nsIContent* aStackParent) |
749 | 0 | { |
750 | 0 | nsIContent* tableParent = aTable->GetParent(); |
751 | 0 | return IsElementOrTemplateContent(tableParent) ? tableParent : aStackParent; |
752 | 0 | } |
753 | | |
754 | | void |
755 | | nsHtml5TreeOperation::PreventScriptExecution(nsIContent* aNode) |
756 | 0 | { |
757 | 0 | nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aNode); |
758 | 0 | if (sele) { |
759 | 0 | sele->PreventExecution(); |
760 | 0 | } else { |
761 | 0 | MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, |
762 | 0 | "Node didn't QI to script, but SVG wasn't disabled."); |
763 | 0 | } |
764 | 0 | } |
765 | | |
766 | | void |
767 | | nsHtml5TreeOperation::DoneAddingChildren(nsIContent* aNode) |
768 | 0 | { |
769 | 0 | aNode->DoneAddingChildren(aNode->HasParserNotified()); |
770 | 0 | } |
771 | | |
772 | | void |
773 | | nsHtml5TreeOperation::DoneCreatingElement(nsIContent* aNode) |
774 | 0 | { |
775 | 0 | aNode->DoneCreatingElement(); |
776 | 0 | } |
777 | | |
778 | | void |
779 | | nsHtml5TreeOperation::SvgLoad(nsIContent* aNode) |
780 | 0 | { |
781 | 0 | nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(aNode); |
782 | 0 | if (NS_FAILED( |
783 | 0 | aNode->OwnerDoc()->Dispatch(TaskCategory::Network, event.forget()))) { |
784 | 0 | NS_WARNING("failed to dispatch svg load dispatcher"); |
785 | 0 | } |
786 | 0 | } |
787 | | |
788 | | void |
789 | | nsHtml5TreeOperation::MarkMalformedIfScript(nsIContent* aNode) |
790 | 0 | { |
791 | 0 | nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aNode); |
792 | 0 | if (sele) { |
793 | 0 | // Make sure to serialize this script correctly, for nice round tripping. |
794 | 0 | sele->SetIsMalformed(); |
795 | 0 | } |
796 | 0 | } |
797 | | |
798 | | nsresult |
799 | | nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, |
800 | | nsIContent** aScriptElement, |
801 | | bool* aInterrupted, |
802 | | bool* aStreamEnded) |
803 | 0 | { |
804 | 0 | switch (mOpCode) { |
805 | 0 | case eTreeOpUninitialized: { |
806 | 0 | MOZ_CRASH("eTreeOpUninitialized"); |
807 | 0 | } |
808 | 0 | case eTreeOpAppend: { |
809 | 0 | nsIContent* node = *(mOne.node); |
810 | 0 | nsIContent* parent = *(mTwo.node); |
811 | 0 | return Append(node, parent, aBuilder); |
812 | 0 | } |
813 | 0 | case eTreeOpDetach: { |
814 | 0 | nsIContent* node = *(mOne.node); |
815 | 0 | Detach(node, aBuilder); |
816 | 0 | return NS_OK; |
817 | 0 | } |
818 | 0 | case eTreeOpAppendChildrenToNewParent: { |
819 | 0 | nsCOMPtr<nsIContent> node = *(mOne.node); |
820 | 0 | nsIContent* parent = *(mTwo.node); |
821 | 0 | return AppendChildrenToNewParent(node, parent, aBuilder); |
822 | 0 | } |
823 | 0 | case eTreeOpFosterParent: { |
824 | 0 | nsIContent* node = *(mOne.node); |
825 | 0 | nsIContent* parent = *(mTwo.node); |
826 | 0 | nsIContent* table = *(mThree.node); |
827 | 0 | return FosterParent(node, parent, table, aBuilder); |
828 | 0 | } |
829 | 0 | case eTreeOpAppendToDocument: { |
830 | 0 | nsIContent* node = *(mOne.node); |
831 | 0 | nsresult rv = AppendToDocument(node, aBuilder); |
832 | 0 |
|
833 | 0 | aBuilder->PauseDocUpdate(aInterrupted); |
834 | 0 | return rv; |
835 | 0 | } |
836 | 0 | case eTreeOpAddAttributes: { |
837 | 0 | nsIContent* node = *(mOne.node); |
838 | 0 | nsHtml5HtmlAttributes* attributes = mTwo.attributes; |
839 | 0 | return AddAttributes(node, attributes, aBuilder); |
840 | 0 | } |
841 | 0 | case eTreeOpDocumentMode: { |
842 | 0 | aBuilder->SetDocumentMode(mOne.mode); |
843 | 0 | return NS_OK; |
844 | 0 | } |
845 | 0 | case eTreeOpCreateHTMLElementNetwork: |
846 | 0 | case eTreeOpCreateHTMLElementNotNetwork: { |
847 | 0 | nsIContent** target = mOne.node; |
848 | 0 | mozilla::dom::HTMLContentCreatorFunction creator = mFour.htmlCreator; |
849 | 0 | RefPtr<nsAtom> name = Reget(mTwo.atom); |
850 | 0 | nsHtml5HtmlAttributes* attributes = mThree.attributes; |
851 | 0 | nsIContent* intendedParent = mFive.node ? *(mFive.node) : nullptr; |
852 | 0 |
|
853 | 0 | // intendedParent == nullptr is a special case where the |
854 | 0 | // intended parent is the document. |
855 | 0 | nsNodeInfoManager* nodeInfoManager = |
856 | 0 | intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager() |
857 | 0 | : aBuilder->GetNodeInfoManager(); |
858 | 0 |
|
859 | 0 | *target = CreateHTMLElement(name, |
860 | 0 | attributes, |
861 | 0 | mOpCode == eTreeOpCreateHTMLElementNetwork |
862 | 0 | ? dom::FROM_PARSER_NETWORK |
863 | 0 | : dom::FROM_PARSER_DOCUMENT_WRITE, |
864 | 0 | nodeInfoManager, |
865 | 0 | aBuilder, |
866 | 0 | creator); |
867 | 0 | return NS_OK; |
868 | 0 | } |
869 | 0 | case eTreeOpCreateSVGElementNetwork: |
870 | 0 | case eTreeOpCreateSVGElementNotNetwork: { |
871 | 0 | nsIContent** target = mOne.node; |
872 | 0 | mozilla::dom::SVGContentCreatorFunction creator = mFour.svgCreator; |
873 | 0 | RefPtr<nsAtom> name = Reget(mTwo.atom); |
874 | 0 | nsHtml5HtmlAttributes* attributes = mThree.attributes; |
875 | 0 | nsIContent* intendedParent = mFive.node ? *(mFive.node) : nullptr; |
876 | 0 |
|
877 | 0 | // intendedParent == nullptr is a special case where the |
878 | 0 | // intended parent is the document. |
879 | 0 | nsNodeInfoManager* nodeInfoManager = |
880 | 0 | intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager() |
881 | 0 | : aBuilder->GetNodeInfoManager(); |
882 | 0 |
|
883 | 0 | *target = CreateSVGElement(name, |
884 | 0 | attributes, |
885 | 0 | mOpCode == eTreeOpCreateSVGElementNetwork |
886 | 0 | ? dom::FROM_PARSER_NETWORK |
887 | 0 | : dom::FROM_PARSER_DOCUMENT_WRITE, |
888 | 0 | nodeInfoManager, |
889 | 0 | aBuilder, |
890 | 0 | creator); |
891 | 0 | return NS_OK; |
892 | 0 | } |
893 | 0 | case eTreeOpCreateMathMLElement: { |
894 | 0 | nsIContent** target = mOne.node; |
895 | 0 | RefPtr<nsAtom> name = Reget(mTwo.atom); |
896 | 0 | nsHtml5HtmlAttributes* attributes = mThree.attributes; |
897 | 0 | nsIContent* intendedParent = mFive.node ? *(mFive.node) : nullptr; |
898 | 0 |
|
899 | 0 | // intendedParent == nullptr is a special case where the |
900 | 0 | // intended parent is the document. |
901 | 0 | nsNodeInfoManager* nodeInfoManager = |
902 | 0 | intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager() |
903 | 0 | : aBuilder->GetNodeInfoManager(); |
904 | 0 |
|
905 | 0 | *target = |
906 | 0 | CreateMathMLElement(name, attributes, nodeInfoManager, aBuilder); |
907 | 0 | return NS_OK; |
908 | 0 | } |
909 | 0 | case eTreeOpSetFormElement: { |
910 | 0 | nsIContent* node = *(mOne.node); |
911 | 0 | nsIContent* parent = *(mTwo.node); |
912 | 0 | SetFormElement(node, parent); |
913 | 0 | return NS_OK; |
914 | 0 | } |
915 | 0 | case eTreeOpAppendText: { |
916 | 0 | nsIContent* parent = *mOne.node; |
917 | 0 | char16_t* buffer = mTwo.unicharPtr; |
918 | 0 | uint32_t length = mFour.integer; |
919 | 0 | return AppendText(buffer, length, parent, aBuilder); |
920 | 0 | } |
921 | 0 | case eTreeOpFosterParentText: { |
922 | 0 | nsIContent* stackParent = *mOne.node; |
923 | 0 | char16_t* buffer = mTwo.unicharPtr; |
924 | 0 | uint32_t length = mFour.integer; |
925 | 0 | nsIContent* table = *mThree.node; |
926 | 0 | return FosterParentText(stackParent, buffer, length, table, aBuilder); |
927 | 0 | } |
928 | 0 | case eTreeOpAppendComment: { |
929 | 0 | nsIContent* parent = *mOne.node; |
930 | 0 | char16_t* buffer = mTwo.unicharPtr; |
931 | 0 | int32_t length = mFour.integer; |
932 | 0 | return AppendComment(parent, buffer, length, aBuilder); |
933 | 0 | } |
934 | 0 | case eTreeOpAppendCommentToDocument: { |
935 | 0 | char16_t* buffer = mTwo.unicharPtr; |
936 | 0 | int32_t length = mFour.integer; |
937 | 0 | return AppendCommentToDocument(buffer, length, aBuilder); |
938 | 0 | } |
939 | 0 | case eTreeOpAppendDoctypeToDocument: { |
940 | 0 | RefPtr<nsAtom> name = Reget(mOne.atom); |
941 | 0 | nsHtml5TreeOperationStringPair* pair = mTwo.stringPair; |
942 | 0 | nsString publicId; |
943 | 0 | nsString systemId; |
944 | 0 | pair->Get(publicId, systemId); |
945 | 0 | return AppendDoctypeToDocument(name, publicId, systemId, aBuilder); |
946 | 0 | } |
947 | 0 | case eTreeOpGetDocumentFragmentForTemplate: { |
948 | 0 | nsIContent* node = *(mOne.node); |
949 | 0 | *mTwo.node = GetDocumentFragmentForTemplate(node); |
950 | 0 | return NS_OK; |
951 | 0 | } |
952 | 0 | case eTreeOpGetFosterParent: { |
953 | 0 | nsIContent* table = *(mOne.node); |
954 | 0 | nsIContent* stackParent = *(mTwo.node); |
955 | 0 | nsIContent* fosterParent = GetFosterParent(table, stackParent); |
956 | 0 | *mThree.node = fosterParent; |
957 | 0 | return NS_OK; |
958 | 0 | } |
959 | 0 | case eTreeOpMarkAsBroken: { |
960 | 0 | return mOne.result; |
961 | 0 | } |
962 | 0 | case eTreeOpRunScript: { |
963 | 0 | nsIContent* node = *(mOne.node); |
964 | 0 | nsAHtml5TreeBuilderState* snapshot = mTwo.state; |
965 | 0 | if (snapshot) { |
966 | 0 | aBuilder->InitializeDocWriteParserState(snapshot, mFour.integer); |
967 | 0 | } |
968 | 0 | *aScriptElement = node; |
969 | 0 | return NS_OK; |
970 | 0 | } |
971 | 0 | case eTreeOpRunScriptAsyncDefer: { |
972 | 0 | nsIContent* node = *(mOne.node); |
973 | 0 | aBuilder->RunScript(node); |
974 | 0 | return NS_OK; |
975 | 0 | } |
976 | 0 | case eTreeOpPreventScriptExecution: { |
977 | 0 | nsIContent* node = *(mOne.node); |
978 | 0 | PreventScriptExecution(node); |
979 | 0 | return NS_OK; |
980 | 0 | } |
981 | 0 | case eTreeOpDoneAddingChildren: { |
982 | 0 | nsIContent* node = *(mOne.node); |
983 | 0 | node->DoneAddingChildren(node->HasParserNotified()); |
984 | 0 | return NS_OK; |
985 | 0 | } |
986 | 0 | case eTreeOpDoneCreatingElement: { |
987 | 0 | nsIContent* node = *(mOne.node); |
988 | 0 | DoneCreatingElement(node); |
989 | 0 | return NS_OK; |
990 | 0 | } |
991 | 0 | case eTreeOpSetDocumentCharset: { |
992 | 0 | auto encoding = WrapNotNull(mOne.encoding); |
993 | 0 | int32_t charsetSource = mFour.integer; |
994 | 0 | aBuilder->SetDocumentCharsetAndSource(encoding, charsetSource); |
995 | 0 | return NS_OK; |
996 | 0 | } |
997 | 0 | case eTreeOpNeedsCharsetSwitchTo: { |
998 | 0 | auto encoding = WrapNotNull(mOne.encoding); |
999 | 0 | int32_t charsetSource = mFour.integer; |
1000 | 0 | int32_t lineNumber = mTwo.integer; |
1001 | 0 | aBuilder->NeedsCharsetSwitchTo( |
1002 | 0 | encoding, charsetSource, (uint32_t)lineNumber); |
1003 | 0 | return NS_OK; |
1004 | 0 | } |
1005 | 0 | case eTreeOpUpdateStyleSheet: { |
1006 | 0 | nsIContent* node = *(mOne.node); |
1007 | 0 | aBuilder->UpdateStyleSheet(node); |
1008 | 0 | return NS_OK; |
1009 | 0 | } |
1010 | 0 | case eTreeOpProcessMeta: { |
1011 | 0 | nsIContent* node = *(mOne.node); |
1012 | 0 | return aBuilder->ProcessMETATag(node); |
1013 | 0 | } |
1014 | 0 | case eTreeOpProcessOfflineManifest: { |
1015 | 0 | char16_t* str = mOne.unicharPtr; |
1016 | 0 | nsDependentString dependentString(str); |
1017 | 0 | aBuilder->ProcessOfflineManifest(dependentString); |
1018 | 0 | return NS_OK; |
1019 | 0 | } |
1020 | 0 | case eTreeOpMarkMalformedIfScript: { |
1021 | 0 | nsIContent* node = *(mOne.node); |
1022 | 0 | MarkMalformedIfScript(node); |
1023 | 0 | return NS_OK; |
1024 | 0 | } |
1025 | 0 | case eTreeOpStreamEnded: { |
1026 | 0 | *aStreamEnded = true; |
1027 | 0 | return NS_OK; |
1028 | 0 | } |
1029 | 0 | case eTreeOpSetStyleLineNumber: { |
1030 | 0 | nsIContent* node = *(mOne.node); |
1031 | 0 | nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(node); |
1032 | 0 | if (ssle) { |
1033 | 0 | ssle->SetLineNumber(mFour.integer); |
1034 | 0 | } else { |
1035 | 0 | MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, |
1036 | 0 | "Node didn't QI to style, but SVG wasn't disabled."); |
1037 | 0 | } |
1038 | 0 | return NS_OK; |
1039 | 0 | } |
1040 | 0 | case eTreeOpSetScriptLineNumberAndFreeze: { |
1041 | 0 | nsIContent* node = *(mOne.node); |
1042 | 0 | nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node); |
1043 | 0 | if (sele) { |
1044 | 0 | sele->SetScriptLineNumber(mFour.integer); |
1045 | 0 | sele->FreezeExecutionAttrs(node->OwnerDoc()); |
1046 | 0 | } else { |
1047 | 0 | MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, |
1048 | 0 | "Node didn't QI to script, but SVG wasn't disabled."); |
1049 | 0 | } |
1050 | 0 | return NS_OK; |
1051 | 0 | } |
1052 | 0 | case eTreeOpSvgLoad: { |
1053 | 0 | nsIContent* node = *(mOne.node); |
1054 | 0 | SvgLoad(node); |
1055 | 0 | return NS_OK; |
1056 | 0 | } |
1057 | 0 | case eTreeOpMaybeComplainAboutCharset: { |
1058 | 0 | char* msgId = mOne.charPtr; |
1059 | 0 | bool error = mTwo.integer; |
1060 | 0 | int32_t lineNumber = mThree.integer; |
1061 | 0 | aBuilder->MaybeComplainAboutCharset(msgId, error, (uint32_t)lineNumber); |
1062 | 0 | return NS_OK; |
1063 | 0 | } |
1064 | 0 | case eTreeOpEnableEncodingMenu: { |
1065 | 0 | nsIDocument* doc = aBuilder->GetDocument(); |
1066 | 0 | doc->EnableEncodingMenu(); |
1067 | 0 | return NS_OK; |
1068 | 0 | } |
1069 | 0 | case eTreeOpAddClass: { |
1070 | 0 | Element* element = (*(mOne.node))->AsElement(); |
1071 | 0 | char16_t* str = mTwo.unicharPtr; |
1072 | 0 | nsDependentString depStr(str); |
1073 | 0 | // See viewsource.css for the possible classes |
1074 | 0 | nsAutoString klass; |
1075 | 0 | element->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass); |
1076 | 0 | if (!klass.IsEmpty()) { |
1077 | 0 | klass.Append(' '); |
1078 | 0 | klass.Append(depStr); |
1079 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true); |
1080 | 0 | } else { |
1081 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true); |
1082 | 0 | } |
1083 | 0 | return NS_OK; |
1084 | 0 | } |
1085 | 0 | case eTreeOpAddViewSourceHref: { |
1086 | 0 | Element* element = (*mOne.node)->AsElement(); |
1087 | 0 | char16_t* buffer = mTwo.unicharPtr; |
1088 | 0 | int32_t length = mFour.integer; |
1089 | 0 |
|
1090 | 0 | nsDependentString relative(buffer, length); |
1091 | 0 |
|
1092 | 0 | nsIDocument* doc = aBuilder->GetDocument(); |
1093 | 0 |
|
1094 | 0 | auto encoding = doc->GetDocumentCharacterSet(); |
1095 | 0 | nsCOMPtr<nsIURI> uri; |
1096 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), |
1097 | 0 | relative, |
1098 | 0 | encoding, |
1099 | 0 | aBuilder->GetViewSourceBaseURI()); |
1100 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
1101 | 0 |
|
1102 | 0 | // Reuse the fix for bug 467852 |
1103 | 0 | // URLs that execute script (e.g. "javascript:" URLs) should just be |
1104 | 0 | // ignored. There's nothing reasonable we can do with them, and allowing |
1105 | 0 | // them to execute in the context of the view-source window presents a |
1106 | 0 | // security risk. Just return the empty string in this case. |
1107 | 0 | bool openingExecutesScript = false; |
1108 | 0 | rv = NS_URIChainHasFlags(uri, |
1109 | 0 | nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, |
1110 | 0 | &openingExecutesScript); |
1111 | 0 | if (NS_FAILED(rv) || openingExecutesScript) { |
1112 | 0 | return NS_OK; |
1113 | 0 | } |
1114 | 0 | |
1115 | 0 | nsAutoCString viewSourceUrl; |
1116 | 0 |
|
1117 | 0 | // URLs that return data (e.g. "http:" URLs) should be prefixed with |
1118 | 0 | // "view-source:". URLs that don't return data should just be returned |
1119 | 0 | // undecorated. |
1120 | 0 | bool doesNotReturnData = false; |
1121 | 0 | rv = NS_URIChainHasFlags( |
1122 | 0 | uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &doesNotReturnData); |
1123 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
1124 | 0 | if (!doesNotReturnData) { |
1125 | 0 | viewSourceUrl.AssignLiteral("view-source:"); |
1126 | 0 | } |
1127 | 0 |
|
1128 | 0 | nsAutoCString spec; |
1129 | 0 | rv = uri->GetSpec(spec); |
1130 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1131 | 0 |
|
1132 | 0 | viewSourceUrl.Append(spec); |
1133 | 0 |
|
1134 | 0 | nsAutoString utf16; |
1135 | 0 | CopyUTF8toUTF16(viewSourceUrl, utf16); |
1136 | 0 |
|
1137 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::href, utf16, true); |
1138 | 0 | return NS_OK; |
1139 | 0 | } |
1140 | 0 | case eTreeOpAddViewSourceBase: { |
1141 | 0 | char16_t* buffer = mTwo.unicharPtr; |
1142 | 0 | int32_t length = mFour.integer; |
1143 | 0 | nsDependentString baseUrl(buffer, length); |
1144 | 0 | aBuilder->AddBase(baseUrl); |
1145 | 0 | return NS_OK; |
1146 | 0 | } |
1147 | 0 | case eTreeOpAddError: { |
1148 | 0 | Element* element = (*(mOne.node))->AsElement(); |
1149 | 0 | char* msgId = mTwo.charPtr; |
1150 | 0 | RefPtr<nsAtom> atom = Reget(mThree.atom); |
1151 | 0 | RefPtr<nsAtom> otherAtom = Reget(mFour.atom); |
1152 | 0 | // See viewsource.css for the possible classes in addition to "error". |
1153 | 0 | nsAutoString klass; |
1154 | 0 | element->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass); |
1155 | 0 | if (!klass.IsEmpty()) { |
1156 | 0 | klass.AppendLiteral(" error"); |
1157 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true); |
1158 | 0 | } else { |
1159 | 0 | element->SetAttr(kNameSpaceID_None, |
1160 | 0 | nsGkAtoms::_class, |
1161 | 0 | NS_LITERAL_STRING("error"), |
1162 | 0 | true); |
1163 | 0 | } |
1164 | 0 |
|
1165 | 0 | nsresult rv; |
1166 | 0 | nsAutoString message; |
1167 | 0 | if (otherAtom) { |
1168 | 0 | const char16_t* params[] = { atom->GetUTF16String(), |
1169 | 0 | otherAtom->GetUTF16String() }; |
1170 | 0 | rv = nsContentUtils::FormatLocalizedString( |
1171 | 0 | nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message); |
1172 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
1173 | 0 | } else if (atom) { |
1174 | 0 | const char16_t* params[] = { atom->GetUTF16String() }; |
1175 | 0 | rv = nsContentUtils::FormatLocalizedString( |
1176 | 0 | nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message); |
1177 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
1178 | 0 | } else { |
1179 | 0 | rv = nsContentUtils::GetLocalizedString( |
1180 | 0 | nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, message); |
1181 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
1182 | 0 | } |
1183 | 0 |
|
1184 | 0 | nsAutoString title; |
1185 | 0 | element->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title); |
1186 | 0 | if (!title.IsEmpty()) { |
1187 | 0 | title.Append('\n'); |
1188 | 0 | title.Append(message); |
1189 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::title, title, true); |
1190 | 0 | } else { |
1191 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::title, message, true); |
1192 | 0 | } |
1193 | 0 | return rv; |
1194 | 0 | } |
1195 | 0 | case eTreeOpAddLineNumberId: { |
1196 | 0 | Element* element = (*(mOne.node))->AsElement(); |
1197 | 0 | int32_t lineNumber = mFour.integer; |
1198 | 0 | nsAutoString val(NS_LITERAL_STRING("line")); |
1199 | 0 | val.AppendInt(lineNumber); |
1200 | 0 | element->SetAttr(kNameSpaceID_None, nsGkAtoms::id, val, true); |
1201 | 0 | return NS_OK; |
1202 | 0 | } |
1203 | 0 | case eTreeOpStartLayout: { |
1204 | 0 | aBuilder->StartLayout( |
1205 | 0 | aInterrupted); // this causes a notification flush anyway |
1206 | 0 | return NS_OK; |
1207 | 0 | } |
1208 | 0 | default: { |
1209 | 0 | MOZ_CRASH("Bogus tree op"); |
1210 | 0 | } |
1211 | 0 | } |
1212 | 0 | return NS_OK; // keep compiler happy |
1213 | 0 | } |