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