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 }