nsHTMLDocument.cpp (28031B)
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 #include "nsHTMLDocument.h" 8 9 #include "DocumentInlines.h" 10 #include "mozilla/PresShell.h" 11 #include "mozilla/StaticPrefs_intl.h" 12 #include "mozilla/css/Loader.h" 13 #include "mozilla/dom/PrototypeDocumentContentSink.h" 14 #include "mozilla/parser/PrototypeDocumentParser.h" 15 #include "nsArrayUtils.h" 16 #include "nsAttrName.h" 17 #include "nsCOMPtr.h" 18 #include "nsCommandManager.h" 19 #include "nsContentList.h" 20 #include "nsContentUtils.h" 21 #include "nsDOMString.h" 22 #include "nsDocShell.h" 23 #include "nsDocShellLoadTypes.h" 24 #include "nsError.h" 25 #include "nsFrameSelection.h" 26 #include "nsGenericHTMLElement.h" 27 #include "nsGkAtoms.h" 28 #include "nsHTMLParts.h" 29 #include "nsICachingChannel.h" 30 #include "nsIDocumentViewer.h" 31 #include "nsIPrincipal.h" 32 #include "nsIProtocolHandler.h" 33 #include "nsIScriptContext.h" 34 #include "nsIScriptElement.h" 35 #include "nsIStreamListener.h" 36 #include "nsIURI.h" 37 #include "nsIXMLContentSink.h" 38 #include "nsJSPrincipals.h" 39 #include "nsJSUtils.h" 40 #include "nsNameSpaceManager.h" 41 #include "nsNetCID.h" 42 #include "nsNetUtil.h" 43 #include "nsPIDOMWindow.h" 44 #include "nsPresContext.h" 45 #include "nsPrintfCString.h" 46 #include "nsReadableUtils.h" 47 #include "nsString.h" 48 #include "nsUnicharUtils.h" 49 50 // AHMED 12-2 51 #include "mozAutoDocUpdate.h" 52 #include "mozilla/Encoding.h" 53 #include "mozilla/EventListenerManager.h" 54 #include "mozilla/IdentifierMapEntry.h" 55 #include "mozilla/LoadInfo.h" 56 #include "mozilla/Preferences.h" 57 #include "mozilla/ScopeExit.h" 58 #include "mozilla/StyleSheet.h" 59 #include "mozilla/StyleSheetInlines.h" 60 #include "mozilla/dom/Element.h" 61 #include "mozilla/dom/HTMLBodyElement.h" 62 #include "mozilla/dom/HTMLDocumentBinding.h" 63 #include "mozilla/dom/HTMLIFrameElement.h" 64 #include "mozilla/dom/Selection.h" 65 #include "mozilla/dom/ShadowIncludingTreeIterator.h" 66 #include "mozilla/dom/nsCSPContext.h" 67 #include "mozilla/glean/DomMetrics.h" 68 #include "nsBidiUtils.h" 69 #include "nsCCUncollectableMarker.h" 70 #include "nsCharsetSource.h" 71 #include "nsFocusManager.h" 72 #include "nsHtml5Module.h" 73 #include "nsHtml5Parser.h" 74 #include "nsHtml5TreeOpExecutor.h" 75 #include "nsIContent.h" 76 #include "nsIFrame.h" 77 #include "nsIRequest.h" 78 #include "nsMimeTypes.h" 79 #include "nsNodeInfoManager.h" 80 #include "nsParser.h" 81 #include "nsRange.h" 82 #include "nsSandboxFlags.h" 83 84 using namespace mozilla; 85 using namespace mozilla::dom; 86 87 #include "prtime.h" 88 89 // #define DEBUG_charset 90 91 // ================================================================== 92 // = 93 // ================================================================== 94 95 static bool IsAsciiCompatible(const Encoding* aEncoding) { 96 return aEncoding->IsAsciiCompatible() || aEncoding == ISO_2022_JP_ENCODING; 97 } 98 99 nsresult NS_NewHTMLDocument(Document** aInstancePtrResult, 100 nsIPrincipal* aPrincipal, 101 nsIPrincipal* aPartitionedPrincipal, 102 mozilla::dom::LoadedAsData aLoadedAsData) { 103 RefPtr<nsHTMLDocument> doc = new nsHTMLDocument(aLoadedAsData); 104 105 nsresult rv = doc->Init(aPrincipal, aPartitionedPrincipal); 106 107 if (NS_FAILED(rv)) { 108 *aInstancePtrResult = nullptr; 109 return rv; 110 } 111 112 doc->SetLoadedAsData(aLoadedAsData != mozilla::dom::LoadedAsData::No, 113 /* aConsiderForMemoryReporting */ true); 114 doc.forget(aInstancePtrResult); 115 116 return NS_OK; 117 } 118 119 nsHTMLDocument::nsHTMLDocument(mozilla::dom::LoadedAsData aLoadedAsData) 120 : Document("text/html", aLoadedAsData), 121 mContentListHolder(nullptr), 122 mNumForms(0), 123 mLoadFlags(0), 124 mWarnedWidthHeight(false), 125 mIsPlainText(false), 126 mViewSource(false) { 127 mType = eHTML; 128 mDefaultElementType = kNameSpaceID_XHTML; 129 mCompatMode = eCompatibility_NavQuirks; 130 } 131 132 nsHTMLDocument::~nsHTMLDocument() = default; 133 134 JSObject* nsHTMLDocument::WrapNode(JSContext* aCx, 135 JS::Handle<JSObject*> aGivenProto) { 136 return HTMLDocument_Binding::Wrap(aCx, this, aGivenProto); 137 } 138 139 nsresult nsHTMLDocument::Init(nsIPrincipal* aPrincipal, 140 nsIPrincipal* aPartitionedPrincipal) { 141 nsresult rv = Document::Init(aPrincipal, aPartitionedPrincipal); 142 NS_ENSURE_SUCCESS(rv, rv); 143 144 // Now reset the compatibility mode of the CSSLoader 145 // to match our compat mode. 146 if (mCSSLoader) { 147 mCSSLoader->SetCompatibilityMode(mCompatMode); 148 } 149 150 return NS_OK; 151 } 152 153 void nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) { 154 Document::Reset(aChannel, aLoadGroup); 155 156 if (aChannel) { 157 aChannel->GetLoadFlags(&mLoadFlags); 158 } 159 } 160 161 void nsHTMLDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup, 162 nsIPrincipal* aPrincipal, 163 nsIPrincipal* aPartitionedPrincipal) { 164 mLoadFlags = nsIRequest::LOAD_NORMAL; 165 166 Document::ResetToURI(aURI, aLoadGroup, aPrincipal, aPartitionedPrincipal); 167 168 mImages = nullptr; 169 mApplets = nullptr; 170 mEmbeds = nullptr; 171 mLinks = nullptr; 172 mAnchors = nullptr; 173 mScripts = nullptr; 174 175 mForms = nullptr; 176 177 // Make the content type default to "text/html", we are a HTML 178 // document, after all. Once we start getting data, this may be 179 // changed. 180 SetContentType(nsDependentCString("text/html")); 181 } 182 183 void nsHTMLDocument::TryReloadCharset(nsIDocumentViewer* aViewer, 184 int32_t& aCharsetSource, 185 NotNull<const Encoding*>& aEncoding) { 186 if (aViewer) { 187 int32_t reloadEncodingSource; 188 const auto reloadEncoding = 189 aViewer->GetReloadEncodingAndSource(&reloadEncodingSource); 190 if (kCharsetUninitialized != reloadEncodingSource) { 191 aViewer->ForgetReloadEncoding(); 192 193 if (reloadEncodingSource <= aCharsetSource || 194 !IsAsciiCompatible(aEncoding)) { 195 return; 196 } 197 198 if (reloadEncoding && IsAsciiCompatible(reloadEncoding)) { 199 aCharsetSource = reloadEncodingSource; 200 aEncoding = WrapNotNull(reloadEncoding); 201 } 202 } 203 } 204 } 205 206 void nsHTMLDocument::TryUserForcedCharset(nsIDocumentViewer* aViewer, 207 nsIDocShell* aDocShell, 208 int32_t& aCharsetSource, 209 NotNull<const Encoding*>& aEncoding, 210 bool& aForceAutoDetection) { 211 auto resetForce = MakeScopeExit([&] { 212 if (aDocShell) { 213 nsDocShell::Cast(aDocShell)->ResetForcedAutodetection(); 214 } 215 }); 216 217 if (aCharsetSource >= kCharsetFromOtherComponent) { 218 return; 219 } 220 221 // mCharacterSet not updated yet for channel, so check aEncoding, too. 222 if (WillIgnoreCharsetOverride() || !IsAsciiCompatible(aEncoding)) { 223 return; 224 } 225 226 if (aDocShell && nsDocShell::Cast(aDocShell)->GetForcedAutodetection()) { 227 // This is the Character Encoding menu code path in Firefox 228 aForceAutoDetection = true; 229 } 230 } 231 232 void nsHTMLDocument::TryParentCharset(nsIDocShell* aDocShell, 233 int32_t& aCharsetSource, 234 NotNull<const Encoding*>& aEncoding, 235 bool& aForceAutoDetection) { 236 if (!aDocShell) { 237 return; 238 } 239 if (aCharsetSource >= kCharsetFromOtherComponent) { 240 return; 241 } 242 243 int32_t parentSource; 244 const Encoding* parentCharset; 245 nsCOMPtr<nsIPrincipal> parentPrincipal; 246 aDocShell->GetParentCharset(parentCharset, &parentSource, 247 getter_AddRefs(parentPrincipal)); 248 if (!parentCharset) { 249 return; 250 } 251 if (kCharsetFromInitialUserForcedAutoDetection == parentSource || 252 kCharsetFromFinalUserForcedAutoDetection == parentSource) { 253 if (WillIgnoreCharsetOverride() || 254 !IsAsciiCompatible(aEncoding) || // if channel said UTF-16 255 !IsAsciiCompatible(parentCharset)) { 256 return; 257 } 258 aEncoding = WrapNotNull(parentCharset); 259 aCharsetSource = kCharsetFromParentFrame; 260 aForceAutoDetection = true; 261 return; 262 } 263 264 if (aCharsetSource >= kCharsetFromParentFrame) { 265 return; 266 } 267 268 if (kCharsetFromInitialAutoDetectionASCII <= parentSource) { 269 // Make sure that's OK 270 if (!NodePrincipal()->Equals(parentPrincipal) || 271 !IsAsciiCompatible(parentCharset)) { 272 return; 273 } 274 275 aEncoding = WrapNotNull(parentCharset); 276 aCharsetSource = kCharsetFromParentFrame; 277 } 278 } 279 280 // Using a prototype document is only allowed with chrome privilege. 281 bool ShouldUsePrototypeDocument(nsIChannel* aChannel, Document* aDoc) { 282 if (!aChannel || !aDoc || 283 !StaticPrefs::dom_prototype_document_cache_enabled()) { 284 return false; 285 } 286 return nsContentUtils::IsChromeDoc(aDoc); 287 } 288 289 nsresult nsHTMLDocument::StartDocumentLoad( 290 const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, 291 nsISupports* aContainer, nsIStreamListener** aDocListener, bool aReset) { 292 if (!aCommand) { 293 MOZ_ASSERT(false, "Command is mandatory"); 294 return NS_ERROR_INVALID_POINTER; 295 } 296 if (mType != eHTML) { 297 MOZ_ASSERT(mType == eXHTML); 298 MOZ_ASSERT(false, "Must not set HTML doc to XHTML mode before load start."); 299 return NS_ERROR_DOM_INVALID_STATE_ERR; 300 } 301 302 nsAutoCString contentType; 303 aChannel->GetContentType(contentType); 304 305 bool view = 306 !strcmp(aCommand, "view") || !strcmp(aCommand, "external-resource"); 307 mViewSource = !strcmp(aCommand, "view-source"); 308 bool asData = !strcmp(aCommand, kLoadAsData); 309 if (!(view || mViewSource || asData)) { 310 MOZ_ASSERT(false, "Bad parser command"); 311 return NS_ERROR_INVALID_ARG; 312 } 313 314 bool html = contentType.EqualsLiteral(TEXT_HTML); 315 bool xhtml = !html && (contentType.EqualsLiteral(APPLICATION_XHTML_XML) || 316 contentType.EqualsLiteral(APPLICATION_WAPXHTML_XML)); 317 mIsPlainText = 318 !html && !xhtml && nsContentUtils::IsPlainTextType(contentType); 319 if (!(html || xhtml || mIsPlainText || mViewSource)) { 320 MOZ_ASSERT(false, "Channel with bad content type."); 321 return NS_ERROR_INVALID_ARG; 322 } 323 324 bool forceUtf8 = 325 mIsPlainText && nsContentUtils::IsUtf8OnlyPlainTextType(contentType); 326 327 bool loadAsHtml5 = true; 328 329 if (!mViewSource && xhtml) { 330 // We're parsing XHTML as XML, remember that. 331 mType = eXHTML; 332 SetCompatibilityMode(eCompatibility_FullStandards); 333 loadAsHtml5 = false; 334 } 335 336 nsresult rv = Document::StartDocumentLoad(aCommand, aChannel, aLoadGroup, 337 aContainer, aDocListener, aReset); 338 if (NS_FAILED(rv)) { 339 return rv; 340 } 341 342 nsCOMPtr<nsIURI> uri; 343 rv = aChannel->GetURI(getter_AddRefs(uri)); 344 if (NS_FAILED(rv)) { 345 return rv; 346 } 347 348 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer)); 349 350 bool loadWithPrototype = false; 351 RefPtr<nsHtml5Parser> html5Parser; 352 if (loadAsHtml5) { 353 html5Parser = nsHtml5Module::NewHtml5Parser(); 354 mParser = html5Parser; 355 if (mIsPlainText) { 356 if (mViewSource) { 357 html5Parser->MarkAsNotScriptCreated("view-source-plain"); 358 } else { 359 html5Parser->MarkAsNotScriptCreated("plain-text"); 360 } 361 } else if (mViewSource && !html) { 362 html5Parser->MarkAsNotScriptCreated("view-source-xml"); 363 } else if (view && NS_IsAboutBlank(uri)) { 364 // Sadness: There are Chromium-originating WPTs that assume that 365 // as soon as `iframe.contentWindow.location.href == "about:blank"`, 366 // the about:blank DOM exists even for _non-initial_ navigations to 367 // about:blank. Since Chromium-originating WPTs manage to expect this, 368 // chances are that Web content might expect this as well, and the 369 // expectation was valid in Gecko previously. Therefore, let's 370 // special-case even _non-initial_ about:blank. 371 // /content-security-policy/inheritance/history-iframe.sub.html 372 // /content-security-policy/inheritance/window-open-local-after-network-scheme.sub.html 373 html5Parser->MarkAsNotScriptCreated("about-blank"); 374 } else { 375 html5Parser->MarkAsNotScriptCreated(aCommand); 376 } 377 } else if (xhtml && ShouldUsePrototypeDocument(aChannel, this)) { 378 loadWithPrototype = true; 379 nsCOMPtr<nsIURI> originalURI; 380 aChannel->GetOriginalURI(getter_AddRefs(originalURI)); 381 mParser = new mozilla::parser::PrototypeDocumentParser(originalURI, this); 382 } else { 383 mParser = new nsParser(); 384 } 385 386 // Look for the parent document. Note that at this point we don't have our 387 // content viewer set up yet, and therefore do not have a useful 388 // mParentDocument. 389 390 // in this block of code, if we get an error result, we return it 391 // but if we get a null pointer, that's perfectly legal for parent 392 // and parentViewer 393 nsCOMPtr<nsIDocShellTreeItem> parentAsItem; 394 if (docShell) { 395 docShell->GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)); 396 } 397 398 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem)); 399 nsCOMPtr<nsIDocumentViewer> parentViewer; 400 if (parent) { 401 rv = parent->GetDocViewer(getter_AddRefs(parentViewer)); 402 NS_ENSURE_SUCCESS(rv, rv); 403 } 404 405 nsCOMPtr<nsIDocumentViewer> viewer; 406 if (docShell) { 407 docShell->GetDocViewer(getter_AddRefs(viewer)); 408 } 409 if (!viewer) { 410 viewer = std::move(parentViewer); 411 } 412 413 nsAutoCString urlSpec; 414 uri->GetSpec(urlSpec); 415 #ifdef DEBUG_charset 416 printf("Determining charset for %s\n", urlSpec.get()); 417 #endif 418 419 // These are the charset source and charset for our document 420 bool forceAutoDetection = false; 421 int32_t charsetSource = kCharsetUninitialized; 422 auto encoding = UTF_8_ENCODING; 423 424 // For error reporting and referrer policy setting 425 nsHtml5TreeOpExecutor* executor = nullptr; 426 if (loadAsHtml5) { 427 executor = static_cast<nsHtml5TreeOpExecutor*>(mParser->GetContentSink()); 428 } 429 430 if (forceUtf8) { 431 charsetSource = kCharsetFromUtf8OnlyMime; 432 } else if (!IsHTMLDocument() || !docShell) { // no docshell for text/html XHR 433 charsetSource = 434 IsHTMLDocument() ? kCharsetFromFallback : kCharsetFromDocTypeDefault; 435 TryChannelCharset(aChannel, charsetSource, encoding, executor); 436 } else { 437 NS_ASSERTION(docShell, "Unexpected null value"); 438 439 // The following will try to get the character encoding from various 440 // sources. Each Try* function will return early if the source is already 441 // at least as large as any of the sources it might look at. Some of 442 // these functions (like TryReloadCharset and TryParentCharset) can set 443 // charsetSource to various values depending on where the charset they 444 // end up finding originally comes from. 445 446 // Try the channel's charset (e.g., charset from HTTP 447 // "Content-Type" header) first. This way, we get to reject overrides in 448 // TryParentCharset and TryUserForcedCharset if the channel said UTF-16. 449 // This is to avoid socially engineered XSS by adding user-supplied 450 // content to a UTF-16 site such that the byte have a dangerous 451 // interpretation as ASCII and the user can be lured to using the 452 // charset menu. 453 TryChannelCharset(aChannel, charsetSource, encoding, executor); 454 455 TryUserForcedCharset(viewer, docShell, charsetSource, encoding, 456 forceAutoDetection); 457 458 TryReloadCharset(viewer, charsetSource, encoding); // For encoding reload 459 TryParentCharset(docShell, charsetSource, encoding, forceAutoDetection); 460 } 461 462 SetDocumentCharacterSetSource(charsetSource); 463 SetDocumentCharacterSet(encoding); 464 465 // Set the parser as the stream listener for the document loader... 466 rv = NS_OK; 467 nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener(); 468 listener.forget(aDocListener); 469 470 #ifdef DEBUG_charset 471 printf(" charset = %s source %d\n", charset.get(), charsetSource); 472 #endif 473 mParser->SetDocumentCharset(encoding, charsetSource, forceAutoDetection); 474 mParser->SetCommand(aCommand); 475 476 if (!IsHTMLDocument()) { 477 MOZ_ASSERT(!loadAsHtml5); 478 if (loadWithPrototype) { 479 nsCOMPtr<nsIContentSink> sink; 480 NS_NewPrototypeDocumentContentSink(getter_AddRefs(sink), this, uri, 481 docShell, aChannel); 482 mParser->SetContentSink(sink); 483 } else { 484 nsCOMPtr<nsIXMLContentSink> xmlsink; 485 NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri, docShell, 486 aChannel); 487 mParser->SetContentSink(xmlsink); 488 } 489 } else { 490 MOZ_ASSERT(loadAsHtml5); 491 html5Parser->Initialize(this, uri, docShell, aChannel); 492 } 493 494 // parser the content of the URI 495 mParser->Parse(uri); 496 497 return rv; 498 } 499 500 bool nsHTMLDocument::UseWidthDeviceWidthFallbackViewport() const { 501 if (mIsPlainText) { 502 // Plain text documents are simple enough that font inflation doesn't offer 503 // any appreciable advantage over defaulting to "width=device-width" and 504 // subsequently turning on word-wrapping. 505 return true; 506 } 507 return Document::UseWidthDeviceWidthFallbackViewport(); 508 } 509 510 Element* nsHTMLDocument::GetUnfocusedKeyEventTarget() { 511 if (nsGenericHTMLElement* body = GetBody()) { 512 return body; 513 } 514 return Document::GetUnfocusedKeyEventTarget(); 515 } 516 517 bool nsHTMLDocument::IsRegistrableDomainSuffixOfOrEqualTo( 518 const nsAString& aHostSuffixString, const nsACString& aOrigHost) { 519 // https://html.spec.whatwg.org/multipage/browsers.html#is-a-registrable-domain-suffix-of-or-is-equal-to 520 if (aHostSuffixString.IsEmpty()) { 521 return false; 522 } 523 524 nsCOMPtr<nsIURI> origURI = CreateInheritingURIForHost(aOrigHost); 525 if (!origURI) { 526 // Error: failed to parse input domain 527 return false; 528 } 529 530 nsCOMPtr<nsIURI> newURI = 531 RegistrableDomainSuffixOfInternal(aHostSuffixString, origURI); 532 if (!newURI) { 533 // Error: illegal domain 534 return false; 535 } 536 return true; 537 } 538 539 void nsHTMLDocument::AddedForm() { ++mNumForms; } 540 541 void nsHTMLDocument::RemovedForm() { --mNumForms; } 542 543 int32_t nsHTMLDocument::GetNumFormsSynchronous() const { return mNumForms; } 544 545 // https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem 546 void nsHTMLDocument::NamedGetter(JSContext* aCx, const nsAString& aName, 547 bool& aFound, 548 JS::MutableHandle<JSObject*> aRetVal, 549 mozilla::ErrorResult& aRv) { 550 if (!StaticPrefs::dom_document_name_getter_follow_spec_enabled()) { 551 JS::Rooted<JS::Value> v(aCx); 552 if ((aFound = ResolveNameForWindow(aCx, aName, &v, aRv))) { 553 SetUseCounter(mozilla::eUseCounter_custom_HTMLDocumentNamedGetterHit); 554 aRetVal.set(v.toObjectOrNull()); 555 } 556 return; 557 } 558 559 aFound = false; 560 aRetVal.set(nullptr); 561 562 // Step 1. Let elements be the list of named elements with the name name that 563 // are in a document tree with the Document as their root. 564 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName); 565 if (!entry) { 566 return; 567 } 568 569 nsBaseContentList* list = entry->GetDocumentNameContentList(); 570 if (!list || list->Length() == 0) { 571 return; 572 } 573 574 JS::Rooted<JS::Value> v(aCx); 575 if (list->Length() == 1) { 576 nsIContent* element = list->Item(0); 577 if (auto iframe = HTMLIFrameElement::FromNode(element)) { 578 // Step 2. If elements has only one element, and that element is an iframe 579 // element, and that iframe element's content navigable is not null, then 580 // return the active WindowProxy of the element's content navigable. 581 Nullable<WindowProxyHolder> win = iframe->GetContentWindow(); 582 if (win.IsNull()) { 583 return; 584 } 585 586 if (!ToJSValue(aCx, win.Value(), &v)) { 587 aRv.NoteJSContextException(aCx); 588 return; 589 } 590 591 if (v.isNullOrUndefined()) { 592 return; 593 } 594 } else { 595 // Step 3. Otherwise, if elements has only one element, return that 596 // element. 597 if (!ToJSValue(aCx, element, &v)) { 598 aRv.NoteJSContextException(aCx); 599 return; 600 } 601 } 602 } else { 603 // Step 4. Otherwise, return an HTMLCollection rooted at the Document node, 604 // whose filter matches only named elements with the name name. 605 if (!ToJSValue(aCx, list, &v)) { 606 aRv.NoteJSContextException(aCx); 607 return; 608 } 609 } 610 611 bool collect = false; 612 #ifdef NIGHTLY_BUILD 613 bool preventShadowing = false; 614 if (StaticPrefs::dom_document_name_getter_prevent_shadowing_enabled()) { 615 if (HTMLDocument_Binding::InterfaceHasProperty(aName)) { 616 preventShadowing = true; 617 collect = mShadowedHTMLDocumentProperties.Length() <= 10; 618 } 619 } else 620 #endif 621 { 622 // To limit the possible performance/memory impact, only collect at most 10 623 // properties. 624 collect = mShadowedHTMLDocumentProperties.Length() <= 10 && 625 HTMLDocument_Binding::InterfaceHasProperty(aName); 626 } 627 628 if (collect) { 629 if (!mShadowedHTMLDocumentProperties.Contains(aName)) { 630 mShadowedHTMLDocumentProperties.AppendElement(aName); 631 } 632 } 633 634 #ifdef NIGHTLY_BUILD 635 if (preventShadowing) { 636 AutoTArray<nsString, 1> params; 637 params.AppendElement(aName); 638 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, this, 639 nsContentUtils::eDOM_PROPERTIES, 640 "DocumentShadowingBlockedWarning", params); 641 return; 642 } 643 #endif 644 645 SetUseCounter(mozilla::eUseCounter_custom_HTMLDocumentNamedGetterHit); 646 aFound = true; 647 aRetVal.set(&v.toObject()); 648 } 649 650 void nsHTMLDocument::GetSupportedNames(nsTArray<nsString>& aNames) { 651 if (!StaticPrefs::dom_document_name_getter_follow_spec_enabled()) { 652 GetSupportedNamesForWindow(aNames); 653 return; 654 } 655 656 for (const auto& entry : mIdentifierMap) { 657 if (entry.HasDocumentNameElement()) { 658 aNames.AppendElement(entry.GetKeyAsString()); 659 } 660 } 661 } 662 663 bool nsHTMLDocument::ResolveNameForWindow(JSContext* aCx, 664 const nsAString& aName, 665 JS::MutableHandle<JS::Value> aRetval, 666 ErrorResult& aError) { 667 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName); 668 if (!entry) { 669 return false; 670 } 671 672 nsBaseContentList* list = entry->GetNameContentList(); 673 uint32_t length = list ? list->Length() : 0; 674 675 nsIContent* node; 676 if (length > 0) { 677 if (length > 1) { 678 // The list contains more than one element, return the whole list. 679 if (!ToJSValue(aCx, list, aRetval)) { 680 aError.NoteJSContextException(aCx); 681 return false; 682 } 683 return true; 684 } 685 686 // Only one element in the list, return the element instead of returning 687 // the list. 688 node = list->Item(0); 689 } else { 690 // No named items were found, see if there's one registerd by id for aName. 691 Element* e = entry->GetIdElement(); 692 693 if (!e || !nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(e)) { 694 return false; 695 } 696 697 node = e; 698 } 699 700 if (!ToJSValue(aCx, node, aRetval)) { 701 aError.NoteJSContextException(aCx); 702 return false; 703 } 704 705 return true; 706 } 707 708 void nsHTMLDocument::GetSupportedNamesForWindow(nsTArray<nsString>& aNames) { 709 for (const auto& entry : mIdentifierMap) { 710 if (entry.HasNameElement() || 711 entry.HasIdElementExposedAsHTMLDocumentProperty()) { 712 aNames.AppendElement(entry.GetKeyAsString()); 713 } 714 } 715 } 716 717 //---------------------------- 718 719 // forms related stuff 720 721 bool nsHTMLDocument::MatchFormControls(Element* aElement, int32_t aNamespaceID, 722 nsAtom* aAtom, void* aData) { 723 return aElement->IsHTMLFormControlElement(); 724 } 725 726 nsresult nsHTMLDocument::Clone(dom::NodeInfo* aNodeInfo, 727 nsINode** aResult) const { 728 NS_ASSERTION(aNodeInfo->NodeInfoManager() == mNodeInfoManager, 729 "Can't import this document into another document!"); 730 731 RefPtr<nsHTMLDocument> clone = new nsHTMLDocument(LoadedAsData::AsData); 732 nsresult rv = CloneDocHelper(clone.get()); 733 NS_ENSURE_SUCCESS(rv, rv); 734 735 // State from nsHTMLDocument 736 clone->mLoadFlags = mLoadFlags; 737 738 clone.forget(aResult); 739 return NS_OK; 740 } 741 742 /* virtual */ 743 void nsHTMLDocument::DocAddSizeOfExcludingThis( 744 nsWindowSizes& aWindowSizes) const { 745 Document::DocAddSizeOfExcludingThis(aWindowSizes); 746 747 // Measurement of the following members may be added later if DMD finds it is 748 // worthwhile: 749 // - mLinks 750 // - mAnchors 751 } 752 753 bool nsHTMLDocument::WillIgnoreCharsetOverride() { 754 if (mEncodingMenuDisabled) { 755 return true; 756 } 757 if (mType != eHTML) { 758 MOZ_ASSERT(mType == eXHTML); 759 return true; 760 } 761 if (mCharacterSetSource >= kCharsetFromByteOrderMark) { 762 return true; 763 } 764 if (!mCharacterSet->IsAsciiCompatible() && 765 mCharacterSet != ISO_2022_JP_ENCODING) { 766 return true; 767 } 768 nsIURI* uri = GetOriginalURI(); 769 if (uri) { 770 if (uri->SchemeIs("about")) { 771 return true; 772 } 773 bool isResource; 774 nsresult rv = NS_URIChainHasFlags( 775 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isResource); 776 if (NS_FAILED(rv) || isResource) { 777 return true; 778 } 779 } 780 781 switch (mCharacterSetSource) { 782 case kCharsetUninitialized: 783 case kCharsetFromFallback: 784 case kCharsetFromDocTypeDefault: 785 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8: 786 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD: 787 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII: 788 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD: 789 case kCharsetFromParentFrame: 790 case kCharsetFromXmlDeclaration: 791 case kCharsetFromMetaTag: 792 case kCharsetFromChannel: 793 return false; 794 } 795 796 bool potentialEffect = false; 797 nsIPrincipal* parentPrincipal = NodePrincipal(); 798 799 auto subDoc = [&potentialEffect, parentPrincipal](Document& aSubDoc) { 800 if (parentPrincipal->Equals(aSubDoc.NodePrincipal()) && 801 !aSubDoc.WillIgnoreCharsetOverride()) { 802 potentialEffect = true; 803 return CallState::Stop; 804 } 805 return CallState::Continue; 806 }; 807 EnumerateSubDocuments(subDoc); 808 809 return !potentialEffect; 810 } 811 812 void nsHTMLDocument::GetFormsAndFormControls(nsContentList** aFormList, 813 nsContentList** aFormControlList) { 814 RefPtr<ContentListHolder> holder = mContentListHolder; 815 if (!holder) { 816 // Flush our content model so it'll be up to date 817 // If this becomes unnecessary and the following line is removed, 818 // please also remove the corresponding flush operation from 819 // nsHtml5TreeBuilderCppSupplement.h. (Look for "See bug 497861." there.) 820 // XXXsmaug nsHtml5TreeBuilderCppSupplement doesn't seem to have such flush 821 // anymore. 822 FlushPendingNotifications(FlushType::Content); 823 824 RefPtr<nsContentList> htmlForms = GetExistingForms(); 825 if (!htmlForms) { 826 // If the document doesn't have an existing forms content list, create a 827 // new one which will be released soon by ContentListHolder. The idea is 828 // that we don't have that list hanging around for a long time and slowing 829 // down future DOM mutations. 830 // 831 // Please keep this in sync with Document::Forms(). 832 htmlForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form, 833 nsGkAtoms::form, 834 /* aDeep = */ true, 835 /* aLiveList = */ true); 836 } 837 838 RefPtr<nsContentList> htmlFormControls = new nsContentList( 839 this, nsHTMLDocument::MatchFormControls, nullptr, nullptr, 840 /* aDeep = */ true, 841 /* aMatchAtom = */ nullptr, 842 /* aMatchNameSpaceId = */ kNameSpaceID_None, 843 /* aFuncMayDependOnAttr = */ true, 844 /* aLiveList = */ true); 845 846 holder = new ContentListHolder(this, htmlForms, htmlFormControls); 847 RefPtr<ContentListHolder> runnable = holder; 848 if (NS_SUCCEEDED(Dispatch(runnable.forget()))) { 849 mContentListHolder = holder; 850 } 851 } 852 853 NS_ADDREF(*aFormList = holder->mFormList); 854 NS_ADDREF(*aFormControlList = holder->mFormControlList); 855 }