tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }