tor-browser

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

DOMParser.cpp (11584B)


      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 "mozilla/dom/DOMParser.h"
      8 
      9 #include "MainThreadUtils.h"
     10 #include "SystemPrincipal.h"
     11 #include "mozilla/BasePrincipal.h"
     12 #include "mozilla/LoadInfo.h"
     13 #include "mozilla/NullPrincipal.h"
     14 #include "mozilla/dom/BindingUtils.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/ScriptSettings.h"
     17 #include "mozilla/dom/TrustedTypeUtils.h"
     18 #include "mozilla/dom/TrustedTypesConstants.h"
     19 #include "nsCRT.h"
     20 #include "nsContentUtils.h"
     21 #include "nsDOMJSUtils.h"
     22 #include "nsDOMString.h"
     23 #include "nsError.h"
     24 #include "nsIScriptGlobalObject.h"
     25 #include "nsIStreamListener.h"
     26 #include "nsNetUtil.h"
     27 #include "nsPIDOMWindow.h"
     28 #include "nsStreamUtils.h"
     29 #include "nsStringStream.h"
     30 
     31 using namespace mozilla;
     32 using namespace mozilla::dom;
     33 
     34 DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal,
     35                     nsIURI* aDocumentURI)
     36    : mOwner(aOwner),
     37      mPrincipal(aDocPrincipal),
     38      mDocumentURI(aDocumentURI),
     39      mForceEnableXULXBL(false),
     40      mForceEnableDTD(false) {
     41  MOZ_ASSERT(aDocPrincipal);
     42  MOZ_ASSERT(aDocumentURI);
     43 }
     44 
     45 DOMParser::~DOMParser() = default;
     46 
     47 // QueryInterface implementation for DOMParser
     48 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser)
     49  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     50  NS_INTERFACE_MAP_ENTRY(nsISupports)
     51 NS_INTERFACE_MAP_END
     52 
     53 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser, mOwner)
     54 
     55 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser)
     56 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser)
     57 
     58 already_AddRefed<Document> DOMParser::ParseFromStringInternal(
     59    const nsAString& aStr, SupportedType aType, ErrorResult& aRv) {
     60  if (aType == SupportedType::Text_html) {
     61    nsCOMPtr<Document> document = SetUpDocument(DocumentFlavor::HTML, aRv);
     62    if (NS_WARN_IF(aRv.Failed())) {
     63      return nullptr;
     64    }
     65 
     66    // Keep the XULXBL state in sync with the XML case.
     67    if (mForceEnableXULXBL) {
     68      document->ForceEnableXULXBL();
     69    }
     70 
     71    if (mForceEnableDTD) {
     72      document->ForceSkipDTDSecurityChecks();
     73    }
     74 
     75    nsresult rv = nsContentUtils::ParseDocumentHTML(aStr, document, false);
     76    if (NS_WARN_IF(NS_FAILED(rv))) {
     77      aRv.Throw(rv);
     78      return nullptr;
     79    }
     80 
     81    return document.forget();
     82  }
     83 
     84  nsAutoCString utf8str;
     85  // Convert from UTF16 to UTF8 using fallible allocations
     86  if (!AppendUTF16toUTF8(aStr, utf8str, mozilla::fallible)) {
     87    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     88    return nullptr;
     89  }
     90 
     91  // The new stream holds a reference to the buffer
     92  nsCOMPtr<nsIInputStream> stream;
     93  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), utf8str,
     94                                      NS_ASSIGNMENT_DEPEND);
     95  if (NS_WARN_IF(NS_FAILED(rv))) {
     96    aRv.Throw(rv);
     97    return nullptr;
     98  }
     99 
    100  return ParseFromStream(stream, u"UTF-8"_ns, utf8str.Length(), aType, aRv);
    101 }
    102 
    103 already_AddRefed<Document> DOMParser::ParseFromString(
    104    const TrustedHTMLOrString& aStr, SupportedType aType,
    105    nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) {
    106  constexpr nsLiteralString sink = u"DOMParser parseFromString"_ns;
    107 
    108  MOZ_ASSERT(mOwner);
    109  nsCOMPtr<nsIGlobalObject> pinnedOwner = mOwner;
    110  Maybe<nsAutoString> compliantStringHolder;
    111  const nsAString* compliantString =
    112      TrustedTypeUtils::GetTrustedTypesCompliantString(
    113          aStr, sink, kTrustedTypesOnlySinkGroup, *pinnedOwner,
    114          aSubjectPrincipal, compliantStringHolder, aRv);
    115  if (aRv.Failed()) {
    116    return nullptr;
    117  }
    118 
    119  return ParseFromStringInternal(*compliantString, aType, aRv);
    120 }
    121 
    122 already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
    123                                                          SupportedType aType,
    124                                                          ErrorResult& aRv) {
    125  // Create the new document with the same principal as `mOwner`, even if it is
    126  // the system principal. This will ensure that nodes from the returned
    127  // document are in the same DocGroup as the owner global's document, allowing
    128  // nodes to be adopted.
    129  nsCOMPtr<nsIPrincipal> docPrincipal = mPrincipal;
    130  if (mOwner && mOwner->PrincipalOrNull()) {
    131    mPrincipal = mOwner->PrincipalOrNull();
    132  }
    133 
    134  RefPtr<Document> ret = ParseFromStringInternal(aStr, aType, aRv);
    135  mPrincipal = docPrincipal;
    136  return ret.forget();
    137 }
    138 
    139 already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
    140                                                      SupportedType aType,
    141                                                      ErrorResult& aRv) {
    142  return aBuf.ProcessFixedData([&](const Span<uint8_t>& aData) {
    143    return ParseFromBuffer(aData, aType, aRv);
    144  });
    145 }
    146 
    147 already_AddRefed<Document> DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf,
    148                                                      SupportedType aType,
    149                                                      ErrorResult& aRv) {
    150  // The new stream holds a reference to the buffer
    151  nsCOMPtr<nsIInputStream> stream;
    152  nsresult rv = NS_NewByteInputStream(
    153      getter_AddRefs(stream),
    154      Span(reinterpret_cast<const char*>(aBuf.Elements()), aBuf.Length()),
    155      NS_ASSIGNMENT_DEPEND);
    156  if (NS_FAILED(rv)) {
    157    aRv.Throw(rv);
    158    return nullptr;
    159  }
    160 
    161  return ParseFromStream(stream, VoidString(), aBuf.Length(), aType, aRv);
    162 }
    163 
    164 already_AddRefed<Document> DOMParser::ParseFromStream(nsIInputStream* aStream,
    165                                                      const nsAString& aCharset,
    166                                                      int32_t aContentLength,
    167                                                      SupportedType aType,
    168                                                      ErrorResult& aRv) {
    169  bool svg = (aType == SupportedType::Image_svg_xml);
    170 
    171  // For now, we can only create XML documents.
    172  // XXXsmaug Should we create an HTMLDocument (in XHTML mode)
    173  //         for "application/xhtml+xml"?
    174  if (aType != SupportedType::Text_xml &&
    175      aType != SupportedType::Application_xml &&
    176      aType != SupportedType::Application_xhtml_xml && !svg) {
    177    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    178    return nullptr;
    179  }
    180 
    181  // Put the nsCOMPtr out here so we hold a ref to the stream as needed
    182  nsCOMPtr<nsIInputStream> stream = aStream;
    183  if (!NS_InputStreamIsBuffered(stream)) {
    184    nsCOMPtr<nsIInputStream> bufferedStream;
    185    nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
    186                                            stream.forget(), 4096);
    187    if (NS_WARN_IF(NS_FAILED(rv))) {
    188      aRv.Throw(rv);
    189      return nullptr;
    190    }
    191 
    192    stream = bufferedStream;
    193  }
    194 
    195  nsCOMPtr<Document> document = SetUpDocument(
    196      svg ? DocumentFlavor::SVG : DocumentFlavor::LegacyGuess, aRv);
    197  if (NS_WARN_IF(aRv.Failed())) {
    198    return nullptr;
    199  }
    200 
    201  // Create a fake channel
    202  nsCOMPtr<nsIChannel> parserChannel;
    203  NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI,
    204                           nullptr,  // aStream
    205                           mPrincipal, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
    206                           nsIContentPolicy::TYPE_OTHER, GetEnumString(aType));
    207  if (NS_WARN_IF(!parserChannel)) {
    208    aRv.Throw(NS_ERROR_UNEXPECTED);
    209    return nullptr;
    210  }
    211 
    212  if (!DOMStringIsNull(aCharset)) {
    213    parserChannel->SetContentCharset(NS_ConvertUTF16toUTF8(aCharset));
    214  }
    215 
    216  // Tell the document to start loading
    217  nsCOMPtr<nsIStreamListener> listener;
    218 
    219  // Keep the XULXBL state in sync with the HTML case
    220  if (mForceEnableXULXBL) {
    221    document->ForceEnableXULXBL();
    222  }
    223 
    224  if (mForceEnableDTD) {
    225    document->ForceSkipDTDSecurityChecks();
    226  }
    227 
    228  // Have to pass false for reset here, else the reset will remove
    229  // our event listener.  Should that listener addition move to later
    230  // than this call?
    231  nsresult rv =
    232      document->StartDocumentLoad(kLoadAsData, parserChannel, nullptr, nullptr,
    233                                  getter_AddRefs(listener), false);
    234 
    235  if (NS_FAILED(rv) || !listener) {
    236    aRv.Throw(NS_ERROR_FAILURE);
    237    return nullptr;
    238  }
    239 
    240  // Now start pumping data to the listener
    241  nsresult status;
    242 
    243  rv = listener->OnStartRequest(parserChannel);
    244  if (NS_FAILED(rv)) parserChannel->Cancel(rv);
    245  parserChannel->GetStatus(&status);
    246 
    247  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
    248    rv = listener->OnDataAvailable(parserChannel, stream, 0, aContentLength);
    249    if (NS_FAILED(rv)) parserChannel->Cancel(rv);
    250    parserChannel->GetStatus(&status);
    251  }
    252 
    253  rv = listener->OnStopRequest(parserChannel, status);
    254  // Failure returned from OnStopRequest does not affect the final status of
    255  // the channel, so we do not need to call Cancel(rv) as we do above.
    256 
    257  if (NS_FAILED(rv)) {
    258    aRv.Throw(NS_ERROR_FAILURE);
    259    return nullptr;
    260  }
    261 
    262  return document.forget();
    263 }
    264 
    265 /*static */
    266 already_AddRefed<DOMParser> DOMParser::Constructor(const GlobalObject& aOwner,
    267                                                   ErrorResult& rv) {
    268  MOZ_ASSERT(NS_IsMainThread());
    269  nsCOMPtr<nsIPrincipal> docPrincipal = aOwner.GetSubjectPrincipal();
    270  nsCOMPtr<nsIURI> documentURI;
    271  if (docPrincipal->IsSystemPrincipal()) {
    272    docPrincipal = NullPrincipal::Create(OriginAttributes());
    273    documentURI = docPrincipal->GetURI();
    274  } else {
    275    // Grab document URI off the window our constructor was called on.
    276    // Error out if anything untoward happens.
    277    nsCOMPtr<nsPIDOMWindowInner> window =
    278        do_QueryInterface(aOwner.GetAsSupports());
    279    if (!window) {
    280      rv.Throw(NS_ERROR_UNEXPECTED);
    281      return nullptr;
    282    }
    283    documentURI = window->GetDocumentURI();
    284  }
    285 
    286  if (!documentURI) {
    287    rv.Throw(NS_ERROR_UNEXPECTED);
    288    return nullptr;
    289  }
    290 
    291  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner.GetAsSupports());
    292  MOZ_ASSERT(global);
    293  RefPtr<DOMParser> domParser =
    294      new DOMParser(global, docPrincipal, documentURI);
    295  return domParser.forget();
    296 }
    297 
    298 // static
    299 already_AddRefed<DOMParser> DOMParser::CreateWithoutGlobal(ErrorResult& aRv) {
    300  nsCOMPtr<nsIPrincipal> docPrincipal =
    301      NullPrincipal::Create(OriginAttributes());
    302 
    303  nsCOMPtr<nsIURI> documentURI = docPrincipal->GetURI();
    304  if (!documentURI) {
    305    aRv.Throw(NS_ERROR_UNEXPECTED);
    306    return nullptr;
    307  }
    308 
    309  RefPtr<DOMParser> domParser =
    310      new DOMParser(nullptr, docPrincipal, documentURI);
    311  return domParser.forget();
    312 }
    313 
    314 already_AddRefed<Document> DOMParser::SetUpDocument(DocumentFlavor aFlavor,
    315                                                    ErrorResult& aRv) {
    316  // We should really just use mOwner here, but Document gets confused
    317  // if we pass it a scriptHandlingObject that doesn't QI to
    318  // nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without
    319  // a window global) breaks. The correct solution is just to wean Document off
    320  // of nsIScriptGlobalObject, but that's a yak to shave another day.
    321  nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
    322      do_QueryInterface(mOwner);
    323 
    324  // Try to inherit a style backend.
    325  NS_ASSERTION(mPrincipal, "Must have principal by now");
    326  NS_ASSERTION(mDocumentURI, "Must have document URI by now");
    327 
    328  nsCOMPtr<Document> doc;
    329  nsresult rv = NS_NewDOMDocument(
    330      getter_AddRefs(doc), u""_ns, u""_ns, nullptr, mDocumentURI, mDocumentURI,
    331      mPrincipal, LoadedAsData::AsData, scriptHandlingObject, aFlavor);
    332  if (NS_WARN_IF(NS_FAILED(rv))) {
    333    aRv.Throw(rv);
    334    return nullptr;
    335  }
    336 
    337  return doc.forget();
    338 }