gecko-dev/dom/xml/nsXMLPrettyPrinter.cpp
Timothy Guan-tin Chien c4c5775807 Bug 1472237 - Always do XML Pretty print even if the frame is hidden r=mats
XML pretty print, as it was implemented in bug 64945, skips itself when the loaded document is not visible. There is no explanation why it does that, other than the fact that it is backed by an XBL binding, and XBL bindings always bound to DOM with layout frames. A later change in bug 1400618 made the existence of presshell necessary before binding the XBL binding.

With bug 1437956 and bug 1503019, XML pretty print is now backed by Shadow DOM. That leaves no reason for keeping this behavior.

The test file test_bug590812.html also revealed that XML pretty print might incorrectly opt-out itself because of the said behavior. We don't know if this something only happens in the test or it is something that may happen in the wild. Nonetheless, it's a better idea to simplify the behavior here.

Differential Revision: https://phabricator.services.mozilla.com/D12088

--HG--
extra : moz-landing-system : lando
2018-11-16 08:16:27 +00:00

187 lines
5.4 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsXMLPrettyPrinter.h"
#include "nsContentUtils.h"
#include "nsICSSDeclaration.h"
#include "nsIObserver.h"
#include "nsSyncLoadService.h"
#include "nsPIDOMWindow.h"
#include "nsIServiceManager.h"
#include "nsNetUtil.h"
#include "mozilla/dom/Element.h"
#include "nsIScriptSecurityManager.h"
#include "mozilla/Preferences.h"
#include "nsIDocument.h"
#include "nsVariant.h"
#include "mozilla/dom/CustomEvent.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/txMozillaXSLTProcessor.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(nsXMLPrettyPrinter,
nsIDocumentObserver,
nsIMutationObserver)
nsXMLPrettyPrinter::nsXMLPrettyPrinter() : mDocument(nullptr),
mUnhookPending(false)
{
}
nsXMLPrettyPrinter::~nsXMLPrettyPrinter()
{
NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
}
nsresult
nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument,
bool* aDidPrettyPrint)
{
*aDidPrettyPrint = false;
// check the pref
if (!Preferences::GetBool("layout.xml.prettyprint", true)) {
return NS_OK;
}
// Ok, we should prettyprint. Let's do it!
*aDidPrettyPrint = true;
nsresult rv = NS_OK;
// Load the XSLT
nsCOMPtr<nsIURI> xslUri;
rv = NS_NewURI(getter_AddRefs(xslUri),
NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl"));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocument> xslDocument;
rv = nsSyncLoadService::LoadDocument(xslUri, nsIContentPolicy::TYPE_XSLT,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nullptr, true, mozilla::net::RP_Unset,
getter_AddRefs(xslDocument));
NS_ENSURE_SUCCESS(rv, rv);
// Transform the document
RefPtr<txMozillaXSLTProcessor> transformer = new txMozillaXSLTProcessor();
ErrorResult err;
transformer->ImportStylesheet(*xslDocument, err);
if (NS_WARN_IF(err.Failed())) {
return err.StealNSResult();
}
RefPtr<DocumentFragment> resultFragment =
transformer->TransformToFragment(*aDocument, *aDocument, err);
if (NS_WARN_IF(err.Failed())) {
return err.StealNSResult();
}
// Find the root element
RefPtr<Element> rootElement = aDocument->GetRootElement();
NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
// Attach a closed shadow root on it.
RefPtr<ShadowRoot> shadowRoot =
rootElement->AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
// Append the document fragment to the shadow dom.
shadowRoot->AppendChild(*resultFragment, err);
if (NS_WARN_IF(err.Failed())) {
return err.StealNSResult();
}
// Observe the document so we know when to switch to "normal" view
aDocument->AddObserver(this);
mDocument = aDocument;
NS_ADDREF_THIS();
return NS_OK;
}
void
nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent)
{
// If aContent is null, the document-node was modified.
// If it is not null but in the shadow tree or the <scrollbar> NACs,
// the change was in the generated content, and it should be ignored.
bool isGeneratedContent = !aContent ?
false :
aContent->GetBindingParent() || aContent->IsInShadowTree();
if (!isGeneratedContent && !mUnhookPending) {
// Can't blindly to mUnhookPending after AddScriptRunner,
// since AddScriptRunner _could_ in theory run us
// synchronously
mUnhookPending = true;
nsContentUtils::AddScriptRunner(NewRunnableMethod(
"nsXMLPrettyPrinter::Unhook", this, &nsXMLPrettyPrinter::Unhook));
}
}
void
nsXMLPrettyPrinter::Unhook()
{
mDocument->RemoveObserver(this);
nsCOMPtr<Element> element = mDocument->GetDocumentElement();
if (element) {
// Remove the shadow root
element->UnattachShadow();
}
mDocument = nullptr;
NS_RELEASE_THIS();
}
void
nsXMLPrettyPrinter::AttributeChanged(Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
MaybeUnhook(aElement);
}
void
nsXMLPrettyPrinter::ContentAppended(nsIContent* aFirstNewContent)
{
MaybeUnhook(aFirstNewContent->GetParent());
}
void
nsXMLPrettyPrinter::ContentInserted(nsIContent* aChild)
{
MaybeUnhook(aChild->GetParent());
}
void
nsXMLPrettyPrinter::ContentRemoved(nsIContent* aChild,
nsIContent* aPreviousSibling)
{
MaybeUnhook(aChild->GetParent());
}
void
nsXMLPrettyPrinter::NodeWillBeDestroyed(const nsINode* aNode)
{
mDocument = nullptr;
NS_RELEASE_THIS();
}
nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter)
{
*aPrinter = new nsXMLPrettyPrinter;
NS_ADDREF(*aPrinter);
return NS_OK;
}