1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 /*
8 * An implementation for a Gecko-style content sink that knows how
9 * to build a content model (the "prototype" document) from XUL.
10 *
11 * For more information on XUL,
12 * see http://developer.mozilla.org/en/docs/XUL
13 */
14
15 #include "nsXULContentSink.h"
16
17 #include "jsfriendapi.h"
18
19 #include "nsCOMPtr.h"
20 #include "nsHTMLStyleSheet.h"
21 #include "nsIContentSink.h"
22 #include "mozilla/dom/Document.h"
23 #include "nsIFormControl.h"
24 #include "mozilla/dom/NodeInfo.h"
25 #include "nsIScriptContext.h"
26 #include "nsIScriptGlobalObject.h"
27 #include "nsNameSpaceManager.h"
28 #include "nsParserBase.h"
29 #include "nsViewManager.h"
30 #include "nsIScriptSecurityManager.h"
31 #include "nsLayoutCID.h"
32 #include "nsNetUtil.h"
33 #include "nsString.h"
34 #include "nsReadableUtils.h"
35 #include "nsXULElement.h"
36 #include "mozilla/Logging.h"
37 #include "nsCRT.h"
38
39 #include "nsXULPrototypeDocument.h" // XXXbe temporary
40 #include "mozilla/css/Loader.h"
41
42 #include "nsUnicharUtils.h"
43 #include "nsGkAtoms.h"
44 #include "nsContentUtils.h"
45 #include "nsAttrName.h"
46 #include "nsXMLContentSink.h"
47 #include "nsIScriptError.h"
48 #include "nsContentTypeParser.h"
49
50 static mozilla::LazyLogModule gContentSinkLog("nsXULContentSink");
51
52 using namespace mozilla;
53 using namespace mozilla::dom;
54 //----------------------------------------------------------------------
55
ContextStack()56 XULContentSinkImpl::ContextStack::ContextStack() : mTop(nullptr), mDepth(0) {}
57
~ContextStack()58 XULContentSinkImpl::ContextStack::~ContextStack() {
59 while (mTop) {
60 Entry* doomed = mTop;
61 mTop = mTop->mNext;
62 delete doomed;
63 }
64 }
65
Push(RefPtr<nsXULPrototypeNode> && aNode,State aState)66 void XULContentSinkImpl::ContextStack::Push(RefPtr<nsXULPrototypeNode>&& aNode,
67 State aState) {
68 mTop = new Entry(std::move(aNode), aState, mTop);
69 ++mDepth;
70 }
71
Pop(State * aState)72 nsresult XULContentSinkImpl::ContextStack::Pop(State* aState) {
73 if (mDepth == 0) return NS_ERROR_UNEXPECTED;
74
75 Entry* entry = mTop;
76 mTop = mTop->mNext;
77 --mDepth;
78
79 *aState = entry->mState;
80 delete entry;
81
82 return NS_OK;
83 }
84
GetTopNode(RefPtr<nsXULPrototypeNode> & aNode)85 nsresult XULContentSinkImpl::ContextStack::GetTopNode(
86 RefPtr<nsXULPrototypeNode>& aNode) {
87 if (mDepth == 0) return NS_ERROR_UNEXPECTED;
88
89 aNode = mTop->mNode;
90 return NS_OK;
91 }
92
GetTopChildren(nsPrototypeArray ** aChildren)93 nsresult XULContentSinkImpl::ContextStack::GetTopChildren(
94 nsPrototypeArray** aChildren) {
95 if (mDepth == 0) return NS_ERROR_UNEXPECTED;
96
97 *aChildren = &(mTop->mChildren);
98 return NS_OK;
99 }
100
Clear()101 void XULContentSinkImpl::ContextStack::Clear() {
102 Entry* cur = mTop;
103 while (cur) {
104 // Release the root element (and its descendants).
105 Entry* next = cur->mNext;
106 delete cur;
107 cur = next;
108 }
109
110 mTop = nullptr;
111 mDepth = 0;
112 }
113
Traverse(nsCycleCollectionTraversalCallback & aCb)114 void XULContentSinkImpl::ContextStack::Traverse(
115 nsCycleCollectionTraversalCallback& aCb) {
116 nsCycleCollectionTraversalCallback& cb = aCb;
117 for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) {
118 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
119 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
120 }
121 }
122
123 //----------------------------------------------------------------------
124
XULContentSinkImpl()125 XULContentSinkImpl::XULContentSinkImpl()
126 : mText(nullptr),
127 mTextLength(0),
128 mTextSize(0),
129 mConstrainSize(true),
130 mState(eInProlog) {}
131
~XULContentSinkImpl()132 XULContentSinkImpl::~XULContentSinkImpl() {
133 // The context stack _should_ be empty, unless something has gone wrong.
134 NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?");
135 mContextStack.Clear();
136
137 free(mText);
138 }
139
140 //----------------------------------------------------------------------
141 // nsISupports interface
142
143 NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl)
144
145 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl)
146 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
147 tmp->mContextStack.Clear();
148 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype)
149 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
150 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
151
152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl)
153 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
154 tmp->mContextStack.Traverse(cb);
155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
158
159 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl)
160 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink)
161 NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
162 NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
163 NS_INTERFACE_MAP_ENTRY(nsIContentSink)
164 NS_INTERFACE_MAP_END
165
166 NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl)
167 NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl)
168
169 //----------------------------------------------------------------------
170 // nsIContentSink interface
171
172 NS_IMETHODIMP
173 XULContentSinkImpl::DidBuildModel(bool aTerminated) {
174 nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
175 if (doc) {
176 mPrototype->NotifyLoadDone();
177 mDocument = nullptr;
178 }
179
180 // Drop our reference to the parser to get rid of a circular
181 // reference.
182 mParser = nullptr;
183 return NS_OK;
184 }
185
186 NS_IMETHODIMP
WillInterrupt(void)187 XULContentSinkImpl::WillInterrupt(void) {
188 // XXX Notify the docshell, if necessary
189 return NS_OK;
190 }
191
WillResume()192 void XULContentSinkImpl::WillResume() {
193 // XXX Notify the docshell, if necessary
194 }
195
196 NS_IMETHODIMP
SetParser(nsParserBase * aParser)197 XULContentSinkImpl::SetParser(nsParserBase* aParser) {
198 mParser = aParser;
199 return NS_OK;
200 }
201
SetDocumentCharset(NotNull<const Encoding * > aEncoding)202 void XULContentSinkImpl::SetDocumentCharset(
203 NotNull<const Encoding*> aEncoding) {
204 nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
205 if (doc) {
206 doc->SetDocumentCharacterSet(aEncoding);
207 }
208 }
209
GetTarget()210 nsISupports* XULContentSinkImpl::GetTarget() {
211 nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
212 return ToSupports(doc);
213 }
214
215 //----------------------------------------------------------------------
216
Init(Document * aDocument,nsXULPrototypeDocument * aPrototype)217 nsresult XULContentSinkImpl::Init(Document* aDocument,
218 nsXULPrototypeDocument* aPrototype) {
219 MOZ_ASSERT(aDocument != nullptr, "null ptr");
220 if (!aDocument) return NS_ERROR_NULL_POINTER;
221
222 mDocument = do_GetWeakReference(aDocument);
223 mPrototype = aPrototype;
224
225 mDocumentURL = mPrototype->GetURI();
226 mNodeInfoManager = aPrototype->GetNodeInfoManager();
227 if (!mNodeInfoManager) return NS_ERROR_UNEXPECTED;
228
229 mState = eInProlog;
230 return NS_OK;
231 }
232
233 //----------------------------------------------------------------------
234 //
235 // Text buffering
236 //
237
IsDataInBuffer(char16_t * buffer,int32_t length)238 bool XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length) {
239 for (int32_t i = 0; i < length; ++i) {
240 if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n' ||
241 buffer[i] == '\r')
242 continue;
243
244 return true;
245 }
246 return false;
247 }
248
FlushText(bool aCreateTextNode)249 nsresult XULContentSinkImpl::FlushText(bool aCreateTextNode) {
250 nsresult rv;
251
252 do {
253 // Don't do anything if there's no text to create a node from, or
254 // if they've told us not to create a text node
255 if (!mTextLength) break;
256
257 if (!aCreateTextNode) break;
258
259 RefPtr<nsXULPrototypeNode> node;
260 rv = mContextStack.GetTopNode(node);
261 if (NS_FAILED(rv)) return rv;
262
263 bool stripWhitespace = false;
264 if (node->mType == nsXULPrototypeNode::eType_Element) {
265 mozilla::dom::NodeInfo* nodeInfo =
266 static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo;
267
268 if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL))
269 stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) &&
270 !nodeInfo->Equals(nsGkAtoms::description);
271 }
272
273 // Don't bother if there's nothing but whitespace.
274 if (stripWhitespace && !IsDataInBuffer(mText, mTextLength)) break;
275
276 // Don't bother if we're not in XUL document body
277 if (mState != eInDocumentElement || mContextStack.Depth() == 0) break;
278
279 RefPtr<nsXULPrototypeText> text = new nsXULPrototypeText();
280 text->mValue.Assign(mText, mTextLength);
281 if (stripWhitespace) text->mValue.Trim(" \t\n\r");
282
283 // hook it up
284 nsPrototypeArray* children = nullptr;
285 rv = mContextStack.GetTopChildren(&children);
286 if (NS_FAILED(rv)) return rv;
287
288 children->AppendElement(text.forget());
289 } while (0);
290
291 // Reset our text buffer
292 mTextLength = 0;
293 return NS_OK;
294 }
295
296 //----------------------------------------------------------------------
297
NormalizeAttributeString(const char16_t * aExpatName,nsAttrName & aName)298 nsresult XULContentSinkImpl::NormalizeAttributeString(
299 const char16_t* aExpatName, nsAttrName& aName) {
300 int32_t nameSpaceID;
301 RefPtr<nsAtom> prefix, localName;
302 nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix),
303 getter_AddRefs(localName), &nameSpaceID);
304
305 if (nameSpaceID == kNameSpaceID_None) {
306 aName.SetTo(localName);
307
308 return NS_OK;
309 }
310
311 RefPtr<mozilla::dom::NodeInfo> ni;
312 ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
313 nsINode::ATTRIBUTE_NODE);
314 aName.SetTo(ni);
315
316 return NS_OK;
317 }
318
319 /**** BEGIN NEW APIs ****/
320
321 NS_IMETHODIMP
HandleStartElement(const char16_t * aName,const char16_t ** aAtts,uint32_t aAttsCount,uint32_t aLineNumber,uint32_t aColumnNumber)322 XULContentSinkImpl::HandleStartElement(const char16_t* aName,
323 const char16_t** aAtts,
324 uint32_t aAttsCount,
325 uint32_t aLineNumber,
326 uint32_t aColumnNumber) {
327 // XXX Hopefully the parser will flag this before we get here. If
328 // we're in the epilog, there should be no new elements
329 MOZ_ASSERT(mState != eInEpilog, "tag in XUL doc epilog");
330 MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount");
331
332 // Adjust aAttsCount so it's the actual number of attributes
333 aAttsCount /= 2;
334
335 if (mState == eInEpilog) return NS_ERROR_UNEXPECTED;
336
337 if (mState != eInScript) {
338 FlushText();
339 }
340
341 int32_t nameSpaceID;
342 RefPtr<nsAtom> prefix, localName;
343 nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
344 getter_AddRefs(localName), &nameSpaceID);
345
346 RefPtr<mozilla::dom::NodeInfo> nodeInfo;
347 nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
348 nsINode::ELEMENT_NODE);
349
350 nsresult rv = NS_OK;
351 switch (mState) {
352 case eInProlog:
353 // We're the root document element
354 rv = OpenRoot(aAtts, aAttsCount, nodeInfo);
355 break;
356
357 case eInDocumentElement:
358 rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo);
359 break;
360
361 case eInEpilog:
362 case eInScript:
363 MOZ_LOG(
364 gContentSinkLog, LogLevel::Warning,
365 ("xul: warning: unexpected tags in epilog at line %d", aLineNumber));
366 rv = NS_ERROR_UNEXPECTED; // XXX
367 break;
368 }
369
370 return rv;
371 }
372
373 NS_IMETHODIMP
HandleEndElement(const char16_t * aName)374 XULContentSinkImpl::HandleEndElement(const char16_t* aName) {
375 // Never EVER return anything but NS_OK or
376 // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow
377 // the parser's little mind all over the planet.
378 nsresult rv;
379
380 RefPtr<nsXULPrototypeNode> node;
381 rv = mContextStack.GetTopNode(node);
382
383 if (NS_FAILED(rv)) {
384 return NS_OK;
385 }
386
387 switch (node->mType) {
388 case nsXULPrototypeNode::eType_Element: {
389 // Flush any text _now_, so that we'll get text nodes created
390 // before popping the stack.
391 FlushText();
392
393 // Pop the context stack and do prototype hookup.
394 nsPrototypeArray* children = nullptr;
395 rv = mContextStack.GetTopChildren(&children);
396 if (NS_FAILED(rv)) return rv;
397
398 nsXULPrototypeElement* element =
399 static_cast<nsXULPrototypeElement*>(node.get());
400
401 int32_t count = children->Length();
402 if (count) {
403 element->mChildren.SetCapacity(count);
404
405 for (int32_t i = 0; i < count; ++i)
406 element->mChildren.AppendElement(children->ElementAt(i));
407 }
408 } break;
409
410 case nsXULPrototypeNode::eType_Script: {
411 nsXULPrototypeScript* script =
412 static_cast<nsXULPrototypeScript*>(node.get());
413
414 // If given a src= attribute, we must ignore script tag content.
415 if (!script->mSrcURI && !script->HasStencil()) {
416 nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
417
418 script->mOutOfLine = false;
419 if (doc) {
420 script->Compile(mText, mTextLength, JS::SourceOwnership::Borrowed,
421 mDocumentURL, script->mLineNo, doc);
422 }
423 }
424
425 FlushText(false);
426 } break;
427
428 default:
429 NS_ERROR("didn't expect that");
430 break;
431 }
432
433 rv = mContextStack.Pop(&mState);
434 NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted");
435 if (NS_FAILED(rv)) return rv;
436
437 if (mContextStack.Depth() == 0) {
438 // The root element should -always- be an element, because
439 // it'll have been created via XULContentSinkImpl::OpenRoot().
440 NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element,
441 "root is not an element");
442 if (node->mType != nsXULPrototypeNode::eType_Element)
443 return NS_ERROR_UNEXPECTED;
444
445 // Now that we're done parsing, set the prototype document's
446 // root element. This transfers ownership of the prototype
447 // element tree to the prototype document.
448 nsXULPrototypeElement* element =
449 static_cast<nsXULPrototypeElement*>(node.get());
450
451 mPrototype->SetRootElement(element);
452 mState = eInEpilog;
453 }
454
455 return NS_OK;
456 }
457
458 NS_IMETHODIMP
HandleComment(const char16_t * aName)459 XULContentSinkImpl::HandleComment(const char16_t* aName) {
460 FlushText();
461 return NS_OK;
462 }
463
464 NS_IMETHODIMP
HandleCDataSection(const char16_t * aData,uint32_t aLength)465 XULContentSinkImpl::HandleCDataSection(const char16_t* aData,
466 uint32_t aLength) {
467 FlushText();
468 return AddText(aData, aLength);
469 }
470
471 NS_IMETHODIMP
HandleDoctypeDecl(const nsAString & aSubset,const nsAString & aName,const nsAString & aSystemId,const nsAString & aPublicId,nsISupports * aCatalogData)472 XULContentSinkImpl::HandleDoctypeDecl(const nsAString& aSubset,
473 const nsAString& aName,
474 const nsAString& aSystemId,
475 const nsAString& aPublicId,
476 nsISupports* aCatalogData) {
477 return NS_OK;
478 }
479
480 NS_IMETHODIMP
HandleCharacterData(const char16_t * aData,uint32_t aLength)481 XULContentSinkImpl::HandleCharacterData(const char16_t* aData,
482 uint32_t aLength) {
483 if (aData && mState != eInProlog && mState != eInEpilog) {
484 return AddText(aData, aLength);
485 }
486 return NS_OK;
487 }
488
489 NS_IMETHODIMP
HandleProcessingInstruction(const char16_t * aTarget,const char16_t * aData)490 XULContentSinkImpl::HandleProcessingInstruction(const char16_t* aTarget,
491 const char16_t* aData) {
492 FlushText();
493
494 const nsDependentString target(aTarget);
495 const nsDependentString data(aData);
496
497 // Note: the created nsXULPrototypePI has mRefCnt == 1
498 RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
499 pi->mTarget = target;
500 pi->mData = data;
501
502 if (mState == eInProlog) {
503 // Note: passing in already addrefed pi
504 return mPrototype->AddProcessingInstruction(pi);
505 }
506
507 nsresult rv;
508 nsPrototypeArray* children = nullptr;
509 rv = mContextStack.GetTopChildren(&children);
510 if (NS_FAILED(rv)) {
511 return rv;
512 }
513
514 // XXX(Bug 1631371) Check if this should use a fallible operation as it
515 // pretended earlier.
516 children->AppendElement(pi);
517
518 return NS_OK;
519 }
520
521 NS_IMETHODIMP
HandleXMLDeclaration(const char16_t * aVersion,const char16_t * aEncoding,int32_t aStandalone)522 XULContentSinkImpl::HandleXMLDeclaration(const char16_t* aVersion,
523 const char16_t* aEncoding,
524 int32_t aStandalone) {
525 return NS_OK;
526 }
527
528 NS_IMETHODIMP
ReportError(const char16_t * aErrorText,const char16_t * aSourceText,nsIScriptError * aError,bool * _retval)529 XULContentSinkImpl::ReportError(const char16_t* aErrorText,
530 const char16_t* aSourceText,
531 nsIScriptError* aError, bool* _retval) {
532 MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!");
533
534 // The expat driver should report the error.
535 *_retval = true;
536
537 nsresult rv = NS_OK;
538
539 // make sure to empty the context stack so that
540 // <parsererror> could become the root element.
541 mContextStack.Clear();
542
543 mState = eInProlog;
544
545 // Clear any buffered-up text we have. It's enough to set the length to 0.
546 // The buffer itself is allocated when we're created and deleted in our
547 // destructor, so don't mess with it.
548 mTextLength = 0;
549
550 // return leaving the document empty if we're asked to not add a <parsererror>
551 // root node
552 nsCOMPtr<Document> idoc = do_QueryReferent(mDocument);
553 if (idoc && idoc->SuppressParserErrorElement()) {
554 return NS_OK;
555 };
556
557 const char16_t* noAtts[] = {0, 0};
558
559 constexpr auto errorNs =
560 u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
561
562 nsAutoString parsererror(errorNs);
563 parsererror.Append((char16_t)0xFFFF);
564 parsererror.AppendLiteral("parsererror");
565
566 rv = HandleStartElement(parsererror.get(), noAtts, 0, 0, 0);
567 NS_ENSURE_SUCCESS(rv, rv);
568
569 rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText));
570 NS_ENSURE_SUCCESS(rv, rv);
571
572 nsAutoString sourcetext(errorNs);
573 sourcetext.Append((char16_t)0xFFFF);
574 sourcetext.AppendLiteral("sourcetext");
575
576 rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0, 0);
577 NS_ENSURE_SUCCESS(rv, rv);
578
579 rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText));
580 NS_ENSURE_SUCCESS(rv, rv);
581
582 rv = HandleEndElement(sourcetext.get());
583 NS_ENSURE_SUCCESS(rv, rv);
584
585 rv = HandleEndElement(parsererror.get());
586 NS_ENSURE_SUCCESS(rv, rv);
587
588 return rv;
589 }
590
OpenRoot(const char16_t ** aAttributes,const uint32_t aAttrLen,mozilla::dom::NodeInfo * aNodeInfo)591 nsresult XULContentSinkImpl::OpenRoot(const char16_t** aAttributes,
592 const uint32_t aAttrLen,
593 mozilla::dom::NodeInfo* aNodeInfo) {
594 NS_ASSERTION(mState == eInProlog, "how'd we get here?");
595 if (mState != eInProlog) return NS_ERROR_UNEXPECTED;
596
597 if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
598 aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
599 MOZ_LOG(gContentSinkLog, LogLevel::Error,
600 ("xul: script tag not allowed as root content element"));
601
602 return NS_ERROR_UNEXPECTED;
603 }
604
605 // Create the element
606 RefPtr<nsXULPrototypeElement> element = new nsXULPrototypeElement(aNodeInfo);
607
608 // Add the attributes
609 nsresult rv = AddAttributes(aAttributes, aAttrLen, element);
610 if (NS_FAILED(rv)) return rv;
611
612 // Push the element onto the context stack, so that child
613 // containers will hook up to us as their parent.
614 mContextStack.Push(std::move(element), mState);
615
616 mState = eInDocumentElement;
617 return NS_OK;
618 }
619
OpenTag(const char16_t ** aAttributes,const uint32_t aAttrLen,const uint32_t aLineNumber,mozilla::dom::NodeInfo * aNodeInfo)620 nsresult XULContentSinkImpl::OpenTag(const char16_t** aAttributes,
621 const uint32_t aAttrLen,
622 const uint32_t aLineNumber,
623 mozilla::dom::NodeInfo* aNodeInfo) {
624 // Create the element
625 RefPtr<nsXULPrototypeElement> element = new nsXULPrototypeElement(aNodeInfo);
626
627 // Link this element to its parent.
628 nsPrototypeArray* children = nullptr;
629 nsresult rv = mContextStack.GetTopChildren(&children);
630 if (NS_FAILED(rv)) {
631 return rv;
632 }
633
634 // Add the attributes
635 rv = AddAttributes(aAttributes, aAttrLen, element);
636 if (NS_FAILED(rv)) return rv;
637
638 children->AppendElement(element);
639
640 if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
641 aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
642 // Do scripty things now
643 rv = OpenScript(aAttributes, aLineNumber);
644 NS_ENSURE_SUCCESS(rv, rv);
645
646 NS_ASSERTION(mState == eInScript || mState == eInDocumentElement,
647 "Unexpected state");
648 if (mState == eInScript) {
649 // OpenScript has pushed the nsPrototypeScriptElement onto the
650 // stack, so we're done.
651 return NS_OK;
652 }
653 }
654
655 // Push the element onto the context stack, so that child
656 // containers will hook up to us as their parent.
657 mContextStack.Push(std::move(element), mState);
658
659 mState = eInDocumentElement;
660 return NS_OK;
661 }
662
OpenScript(const char16_t ** aAttributes,const uint32_t aLineNumber)663 nsresult XULContentSinkImpl::OpenScript(const char16_t** aAttributes,
664 const uint32_t aLineNumber) {
665 bool isJavaScript = true;
666 nsresult rv;
667
668 // Look for SRC attribute and look for a LANGUAGE attribute
669 nsAutoString src;
670 while (*aAttributes) {
671 const nsDependentString key(aAttributes[0]);
672 if (key.EqualsLiteral("src")) {
673 src.Assign(aAttributes[1]);
674 } else if (key.EqualsLiteral("type")) {
675 nsDependentString str(aAttributes[1]);
676 nsContentTypeParser parser(str);
677 nsAutoString mimeType;
678 rv = parser.GetType(mimeType);
679 if (NS_FAILED(rv)) {
680 if (rv == NS_ERROR_INVALID_ARG) {
681 // Fail immediately rather than checking if later things
682 // are okay.
683 return NS_OK;
684 }
685 // We do want the warning here
686 NS_ENSURE_SUCCESS(rv, rv);
687 }
688
689 // NOTE(emilio): Module scripts don't pass this test, aren't cached yet.
690 // If they become cached, then we need to tweak
691 // PrototypeDocumentContentSink and remove the special cases there.
692 if (nsContentUtils::IsJavascriptMIMEType(mimeType)) {
693 isJavaScript = true;
694
695 // Get the version string, and ensure that JavaScript supports it.
696 nsAutoString versionName;
697 rv = parser.GetParameter("version", versionName);
698
699 if (NS_SUCCEEDED(rv)) {
700 nsContentUtils::ReportToConsoleNonLocalized(
701 u"Versioned JavaScripts are no longer supported. "
702 "Please remove the version parameter."_ns,
703 nsIScriptError::errorFlag, "XUL Document"_ns, nullptr,
704 mDocumentURL, u""_ns, aLineNumber);
705 isJavaScript = false;
706 } else if (rv != NS_ERROR_INVALID_ARG) {
707 return rv;
708 }
709 } else {
710 isJavaScript = false;
711 }
712 } else if (key.EqualsLiteral("language")) {
713 // Language is deprecated, and the impl in ScriptLoader ignores the
714 // various version strings anyway. So we make no attempt to support
715 // languages other than JS for language=
716 nsAutoString lang(aAttributes[1]);
717 if (nsContentUtils::IsJavaScriptLanguage(lang)) {
718 isJavaScript = true;
719 }
720 }
721 aAttributes += 2;
722 }
723
724 // Don't process scripts that aren't JavaScript.
725 if (!isJavaScript) {
726 return NS_OK;
727 }
728
729 nsCOMPtr<Document> doc(do_QueryReferent(mDocument));
730 nsCOMPtr<nsIScriptGlobalObject> globalObject;
731 if (doc) globalObject = do_QueryInterface(doc->GetWindow());
732 RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(aLineNumber);
733
734 // If there is a SRC attribute...
735 if (!src.IsEmpty()) {
736 // Use the SRC attribute value to load the URL
737 rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL);
738
739 // Check if this document is allowed to load a script from this source
740 // NOTE: if we ever allow scripts added via the DOM to run, we need to
741 // add a CheckLoadURI call for that as well.
742 if (NS_SUCCEEDED(rv)) {
743 if (!mSecMan)
744 mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
745 if (NS_SUCCEEDED(rv)) {
746 nsCOMPtr<Document> doc = do_QueryReferent(mDocument, &rv);
747
748 if (NS_SUCCEEDED(rv)) {
749 rv = mSecMan->CheckLoadURIWithPrincipal(
750 doc->NodePrincipal(), script->mSrcURI,
751 nsIScriptSecurityManager::ALLOW_CHROME, doc->InnerWindowID());
752 }
753 }
754 }
755
756 if (NS_FAILED(rv)) {
757 return rv;
758 }
759
760 // Attempt to deserialize an out-of-line script from the FastLoad
761 // file right away. Otherwise we'll end up reloading the script and
762 // corrupting the FastLoad file trying to serialize it, in the case
763 // where it's already there.
764 script->DeserializeOutOfLine(nullptr, mPrototype);
765 }
766
767 nsPrototypeArray* children = nullptr;
768 rv = mContextStack.GetTopChildren(&children);
769 if (NS_FAILED(rv)) {
770 return rv;
771 }
772
773 children->AppendElement(script);
774
775 mConstrainSize = false;
776
777 mContextStack.Push(script, mState);
778 mState = eInScript;
779
780 return NS_OK;
781 }
782
AddAttributes(const char16_t ** aAttributes,const uint32_t aAttrLen,nsXULPrototypeElement * aElement)783 nsresult XULContentSinkImpl::AddAttributes(const char16_t** aAttributes,
784 const uint32_t aAttrLen,
785 nsXULPrototypeElement* aElement) {
786 // Add tag attributes to the element
787 nsresult rv;
788
789 // Create storage for the attributes
790 nsXULPrototypeAttribute* attrs = nullptr;
791 if (aAttrLen > 0) {
792 attrs = aElement->mAttributes.AppendElements(aAttrLen);
793 }
794
795 // Copy the attributes into the prototype
796 uint32_t i;
797 for (i = 0; i < aAttrLen; ++i) {
798 rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName);
799 NS_ENSURE_SUCCESS(rv, rv);
800
801 rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]),
802 mDocumentURL);
803 NS_ENSURE_SUCCESS(rv, rv);
804
805 if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Debug)) {
806 nsAutoString extraWhiteSpace;
807 int32_t cnt = mContextStack.Depth();
808 while (--cnt >= 0) extraWhiteSpace.AppendLiteral(" ");
809 nsAutoString qnameC, valueC;
810 qnameC.Assign(aAttributes[0]);
811 valueC.Assign(aAttributes[1]);
812 MOZ_LOG(gContentSinkLog, LogLevel::Debug,
813 ("xul: %.5d. %s %s=%s",
814 -1, // XXX pass in line number
815 NS_ConvertUTF16toUTF8(extraWhiteSpace).get(),
816 NS_ConvertUTF16toUTF8(qnameC).get(),
817 NS_ConvertUTF16toUTF8(valueC).get()));
818 }
819 }
820
821 return NS_OK;
822 }
823
AddText(const char16_t * aText,int32_t aLength)824 nsresult XULContentSinkImpl::AddText(const char16_t* aText, int32_t aLength) {
825 // Create buffer when we first need it
826 if (0 == mTextSize) {
827 mText = (char16_t*)malloc(sizeof(char16_t) * 4096);
828 if (nullptr == mText) {
829 return NS_ERROR_OUT_OF_MEMORY;
830 }
831 mTextSize = 4096;
832 }
833
834 // Copy data from string into our buffer; flush buffer when it fills up
835 int32_t offset = 0;
836 while (0 != aLength) {
837 int32_t amount = mTextSize - mTextLength;
838 if (amount > aLength) {
839 amount = aLength;
840 }
841 if (0 == amount) {
842 if (mConstrainSize) {
843 nsresult rv = FlushText();
844 if (NS_OK != rv) {
845 return rv;
846 }
847 } else {
848 CheckedInt32 size = mTextSize;
849 size += aLength;
850 if (!size.isValid()) {
851 return NS_ERROR_OUT_OF_MEMORY;
852 }
853 mTextSize = size.value();
854
855 mText = (char16_t*)realloc(mText, sizeof(char16_t) * mTextSize);
856 if (nullptr == mText) {
857 return NS_ERROR_OUT_OF_MEMORY;
858 }
859 }
860 }
861 memcpy(&mText[mTextLength], aText + offset, sizeof(char16_t) * amount);
862
863 mTextLength += amount;
864 offset += amount;
865 aLength -= amount;
866 }
867
868 return NS_OK;
869 }
870