tor-browser

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

nsXMLPrettyPrinter.cpp (6724B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsXMLPrettyPrinter.h"
      7 
      8 #include "mozilla/Preferences.h"
      9 #include "mozilla/dom/CustomEvent.h"
     10 #include "mozilla/dom/Document.h"
     11 #include "mozilla/dom/DocumentFragment.h"
     12 #include "mozilla/dom/DocumentL10n.h"
     13 #include "mozilla/dom/Element.h"
     14 #include "mozilla/dom/ScriptSettings.h"
     15 #include "mozilla/dom/ShadowRoot.h"
     16 #include "mozilla/dom/ToJSValue.h"
     17 #include "mozilla/dom/txMozillaXSLTProcessor.h"
     18 #include "nsContentUtils.h"
     19 #include "nsICSSDeclaration.h"
     20 #include "nsNetUtil.h"
     21 #include "nsPIDOMWindow.h"
     22 #include "nsSyncLoadService.h"
     23 #include "nsVariant.h"
     24 
     25 using namespace mozilla;
     26 using namespace mozilla::dom;
     27 
     28 NS_IMPL_ISUPPORTS(nsXMLPrettyPrinter, nsIDocumentObserver, nsIMutationObserver)
     29 
     30 nsXMLPrettyPrinter::nsXMLPrettyPrinter()
     31    : mDocument(nullptr), mUnhookPending(false) {}
     32 
     33 nsXMLPrettyPrinter::~nsXMLPrettyPrinter() {
     34  NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
     35 }
     36 
     37 nsresult nsXMLPrettyPrinter::PrettyPrint(Document* aDocument,
     38                                         bool* aDidPrettyPrint) {
     39  *aDidPrettyPrint = false;
     40 
     41  // check the pref
     42  if (!Preferences::GetBool("layout.xml.prettyprint", true)) {
     43    return NS_OK;
     44  }
     45 
     46  // Find the root element
     47  RefPtr<Element> rootElement = aDocument->GetRootElement();
     48  NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
     49 
     50  // nsXMLContentSink should not ask us to pretty print an XML doc that comes
     51  // with a CanAttachShadowDOM() == true root element, but just in case:
     52  if (rootElement->CanAttachShadowDOM()) {
     53    MOZ_DIAGNOSTIC_CRASH("We shouldn't be getting this root element");
     54    return NS_ERROR_UNEXPECTED;
     55  }
     56 
     57  // Ok, we should prettyprint. Let's do it!
     58  *aDidPrettyPrint = true;
     59  nsresult rv = NS_OK;
     60 
     61  // Load the XSLT
     62  nsCOMPtr<nsIURI> xslUri;
     63  rv = NS_NewURI(getter_AddRefs(xslUri),
     64                 "chrome://global/content/xml/XMLPrettyPrint.xsl"_ns);
     65  NS_ENSURE_SUCCESS(rv, rv);
     66 
     67  nsCOMPtr<Document> xslDocument;
     68  rv = nsSyncLoadService::LoadDocument(
     69      xslUri, nsIContentPolicy::TYPE_XSLT, nullptr,
     70      nsContentUtils::GetSystemPrincipal(),
     71      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, nullptr,
     72      aDocument->CookieJarSettings(), true, ReferrerPolicy::_empty,
     73      getter_AddRefs(xslDocument));
     74  NS_ENSURE_SUCCESS(rv, rv);
     75 
     76  // Transform the document
     77  RefPtr<txMozillaXSLTProcessor> transformer = new txMozillaXSLTProcessor();
     78  ErrorResult err;
     79  transformer->ImportStylesheet(*xslDocument, err);
     80  if (NS_WARN_IF(err.Failed())) {
     81    return err.StealNSResult();
     82  }
     83 
     84  RefPtr<DocumentFragment> resultFragment =
     85      transformer->TransformToFragment(*aDocument, *aDocument, err);
     86  if (NS_WARN_IF(err.Failed())) {
     87    return err.StealNSResult();
     88  }
     89 
     90  // Attach an UA Widget Shadow Root on it.
     91  rootElement->AttachAndSetUAShadowRoot(Element::NotifyUAWidgetSetup::No);
     92  RefPtr<ShadowRoot> shadowRoot = rootElement->GetShadowRoot();
     93  MOZ_RELEASE_ASSERT(shadowRoot && shadowRoot->IsUAWidget(),
     94                     "There should be a UA Shadow Root here.");
     95 
     96  // Append the document fragment to the shadow dom.
     97  shadowRoot->AppendChild(*resultFragment, err);
     98  if (NS_WARN_IF(err.Failed())) {
     99    return err.StealNSResult();
    100  }
    101 
    102  // Create a DocumentL10n, as the XML document is not allowed to have one.
    103  // Make it sync so that the test for bug 590812 does not require a setTimeout.
    104  RefPtr<DocumentL10n> l10n;
    105  if (aDocument->ShouldResistFingerprinting(RFPTarget::JSLocale)) {
    106    AutoTArray<nsCString, 1> langs = {nsRFPService::GetSpoofedJSLocale()};
    107    l10n = DocumentL10n::Create(aDocument, true, langs);
    108  } else {
    109    l10n = DocumentL10n::Create(aDocument, true);
    110  }
    111  NS_ENSURE_TRUE(l10n, NS_ERROR_UNEXPECTED);
    112  l10n->AddResourceId("dom/XMLPrettyPrint.ftl"_ns);
    113 
    114  // Localize the shadow DOM header
    115  Element* l10nRoot = shadowRoot->GetElementById(u"header"_ns);
    116  NS_ENSURE_TRUE(l10nRoot, NS_ERROR_UNEXPECTED);
    117  l10n->SetRootInfo(l10nRoot);
    118  l10n->ConnectRoot(*l10nRoot, true, err);
    119  if (NS_WARN_IF(err.Failed())) {
    120    return err.StealNSResult();
    121  }
    122  RefPtr<Promise> promise = l10n->TranslateRoots(err);
    123  if (NS_WARN_IF(err.Failed())) {
    124    return err.StealNSResult();
    125  }
    126 
    127  // Observe the document so we know when to switch to "normal" view
    128  aDocument->AddObserver(this);
    129  mDocument = aDocument;
    130 
    131  NS_ADDREF_THIS();
    132 
    133  return NS_OK;
    134 }
    135 
    136 void nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent) {
    137  // If aContent is null, the document-node was modified.
    138  // If it is not null but in the shadow tree or the <scrollbar> NACs,
    139  // the change was in the generated content, and it should be ignored.
    140  bool isGeneratedContent =
    141      aContent &&
    142      (aContent->IsInNativeAnonymousSubtree() || aContent->IsInShadowTree());
    143 
    144  if (!isGeneratedContent && !mUnhookPending) {
    145    // Can't blindly to mUnhookPending after AddScriptRunner,
    146    // since AddScriptRunner _could_ in theory run us
    147    // synchronously
    148    mUnhookPending = true;
    149    nsContentUtils::AddScriptRunner(NewRunnableMethod(
    150        "nsXMLPrettyPrinter::Unhook", this, &nsXMLPrettyPrinter::Unhook));
    151  }
    152 }
    153 
    154 void nsXMLPrettyPrinter::Unhook() {
    155  mDocument->RemoveObserver(this);
    156  nsCOMPtr<Element> element = mDocument->GetDocumentElement();
    157 
    158  if (element) {
    159    // Remove the shadow root
    160    element->UnattachShadow();
    161  }
    162 
    163  mDocument = nullptr;
    164 
    165  NS_RELEASE_THIS();
    166 }
    167 
    168 void nsXMLPrettyPrinter::AttributeChanged(Element* aElement,
    169                                          int32_t aNameSpaceID,
    170                                          nsAtom* aAttribute, AttrModType,
    171                                          const nsAttrValue* aOldValue) {
    172  MaybeUnhook(aElement);
    173 }
    174 
    175 void nsXMLPrettyPrinter::ContentAppended(nsIContent* aFirstNewContent,
    176                                         const ContentAppendInfo&) {
    177  MaybeUnhook(aFirstNewContent->GetParent());
    178 }
    179 
    180 void nsXMLPrettyPrinter::ContentInserted(nsIContent* aChild,
    181                                         const ContentInsertInfo&) {
    182  MaybeUnhook(aChild->GetParent());
    183 }
    184 
    185 void nsXMLPrettyPrinter::ContentWillBeRemoved(nsIContent* aChild,
    186                                              const ContentRemoveInfo&) {
    187  MaybeUnhook(aChild->GetParent());
    188 }
    189 
    190 void nsXMLPrettyPrinter::NodeWillBeDestroyed(nsINode* aNode) {
    191  mDocument = nullptr;
    192  NS_RELEASE_THIS();
    193 }
    194 
    195 nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter) {
    196  *aPrinter = new nsXMLPrettyPrinter;
    197  NS_ADDREF(*aPrinter);
    198  return NS_OK;
    199 }