michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsXMLPrettyPrinter.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIDOMCSSStyleDeclaration.h" michael@0: #include "nsIDOMDocumentXBL.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIXSLTProcessor.h" michael@0: #include "nsSyncLoadService.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsNetUtil.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsIDOMDocumentFragment.h" michael@0: #include "nsBindingManager.h" michael@0: #include "nsXBLService.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsVariant.h" michael@0: #include "nsIDOMCustomEvent.h" michael@0: #include "GeneratedEvents.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsXMLPrettyPrinter, michael@0: nsIDocumentObserver, michael@0: nsIMutationObserver) michael@0: michael@0: nsXMLPrettyPrinter::nsXMLPrettyPrinter() : mDocument(nullptr), michael@0: mUnhookPending(false) michael@0: { michael@0: } michael@0: michael@0: nsXMLPrettyPrinter::~nsXMLPrettyPrinter() michael@0: { michael@0: NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still"); michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, michael@0: bool* aDidPrettyPrint) michael@0: { michael@0: *aDidPrettyPrint = false; michael@0: michael@0: // Check for iframe with display:none. Such iframes don't have presshells michael@0: if (!aDocument->GetShell()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // check if we're in an invisible iframe michael@0: nsPIDOMWindow *internalWin = aDocument->GetWindow(); michael@0: nsCOMPtr frameElem; michael@0: if (internalWin) { michael@0: internalWin->GetFrameElement(getter_AddRefs(frameElem)); michael@0: } michael@0: michael@0: if (frameElem) { michael@0: nsCOMPtr computedStyle; michael@0: nsCOMPtr frameOwnerDoc; michael@0: frameElem->GetOwnerDocument(getter_AddRefs(frameOwnerDoc)); michael@0: if (frameOwnerDoc) { michael@0: nsCOMPtr window; michael@0: frameOwnerDoc->GetDefaultView(getter_AddRefs(window)); michael@0: if (window) { michael@0: window->GetComputedStyle(frameElem, michael@0: EmptyString(), michael@0: getter_AddRefs(computedStyle)); michael@0: } michael@0: } michael@0: michael@0: if (computedStyle) { michael@0: nsAutoString visibility; michael@0: computedStyle->GetPropertyValue(NS_LITERAL_STRING("visibility"), michael@0: visibility); michael@0: if (!visibility.EqualsLiteral("visible")) { michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // check the pref michael@0: if (!Preferences::GetBool("layout.xml.prettyprint", true)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Ok, we should prettyprint. Let's do it! michael@0: *aDidPrettyPrint = true; michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Load the XSLT michael@0: nsCOMPtr xslUri; michael@0: rv = NS_NewURI(getter_AddRefs(xslUri), michael@0: NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr xslDocument; michael@0: rv = nsSyncLoadService::LoadDocument(xslUri, nullptr, nullptr, true, michael@0: getter_AddRefs(xslDocument)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Transform the document michael@0: nsCOMPtr transformer = michael@0: do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = transformer->ImportStylesheet(xslDocument); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr resultFragment; michael@0: nsCOMPtr sourceDocument = do_QueryInterface(aDocument); michael@0: rv = transformer->TransformToFragment(sourceDocument, sourceDocument, michael@0: getter_AddRefs(resultFragment)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // michael@0: // Apply the prettprint XBL binding. michael@0: // michael@0: // We take some shortcuts here. In particular, we don't bother invoking the michael@0: // contstructor (since the binding has no constructor), and we don't bother michael@0: // calling LoadBindingDocument because it's a chrome:// URI and thus will get michael@0: // sync loaded no matter what. michael@0: // michael@0: michael@0: // Grab the XBL service. michael@0: nsXBLService* xblService = nsXBLService::GetInstance(); michael@0: NS_ENSURE_TRUE(xblService, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: // Compute the binding URI. michael@0: nsCOMPtr bindingUri; michael@0: rv = NS_NewURI(getter_AddRefs(bindingUri), michael@0: NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Compute the bound element. michael@0: nsCOMPtr rootCont = aDocument->GetRootElement(); michael@0: NS_ENSURE_TRUE(rootCont, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Grab the system principal. michael@0: nsCOMPtr sysPrincipal; michael@0: nsContentUtils::GetSecurityManager()-> michael@0: GetSystemPrincipal(getter_AddRefs(sysPrincipal)); michael@0: michael@0: // Load the bindings. michael@0: nsRefPtr unused; michael@0: bool ignored; michael@0: rv = xblService->LoadBindings(rootCont, bindingUri, sysPrincipal, michael@0: getter_AddRefs(unused), &ignored); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Fire an event at the bound element to pass it |resultFragment|. michael@0: nsCOMPtr domEvent; michael@0: rv = NS_NewDOMCustomEvent(getter_AddRefs(domEvent), rootCont, michael@0: nullptr, nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr customEvent = do_QueryInterface(domEvent); michael@0: MOZ_ASSERT(customEvent); michael@0: nsCOMPtr resultFragmentVariant = new nsVariant(); michael@0: rv = resultFragmentVariant->SetAsISupports(resultFragment); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: rv = customEvent->InitCustomEvent(NS_LITERAL_STRING("prettyprint-dom-created"), michael@0: /* bubbles = */ false, /* cancelable = */ false, michael@0: /* detail = */ resultFragmentVariant); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: customEvent->SetTrusted(true); michael@0: bool dummy; michael@0: rv = rootCont->DispatchEvent(domEvent, &dummy); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Observe the document so we know when to switch to "normal" view michael@0: aDocument->AddObserver(this); michael@0: mDocument = aDocument; michael@0: michael@0: NS_ADDREF_THIS(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent) michael@0: { michael@0: // If there either aContent is null (the document-node was modified) or michael@0: // there isn't a binding parent we know it's non-anonymous content. michael@0: if ((!aContent || !aContent->GetBindingParent()) && !mUnhookPending) { michael@0: // Can't blindly to mUnhookPending after AddScriptRunner, michael@0: // since AddScriptRunner _could_ in theory run us michael@0: // synchronously michael@0: mUnhookPending = true; michael@0: nsContentUtils::AddScriptRunner( michael@0: NS_NewRunnableMethod(this, &nsXMLPrettyPrinter::Unhook)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::Unhook() michael@0: { michael@0: mDocument->RemoveObserver(this); michael@0: nsCOMPtr element = mDocument->GetDocumentElement(); michael@0: michael@0: if (element) { michael@0: mDocument->BindingManager()->ClearBinding(element); michael@0: } michael@0: michael@0: mDocument = nullptr; michael@0: michael@0: NS_RELEASE_THIS(); michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::AttributeChanged(nsIDocument* aDocument, michael@0: Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: MaybeUnhook(aElement); michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::ContentAppended(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aFirstNewContent, michael@0: int32_t aNewIndexInContainer) michael@0: { michael@0: MaybeUnhook(aContainer); michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::ContentInserted(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer) michael@0: { michael@0: MaybeUnhook(aContainer); michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::ContentRemoved(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent* aPreviousSibling) michael@0: { michael@0: MaybeUnhook(aContainer); michael@0: } michael@0: michael@0: void michael@0: nsXMLPrettyPrinter::NodeWillBeDestroyed(const nsINode* aNode) michael@0: { michael@0: mDocument = nullptr; michael@0: NS_RELEASE_THIS(); michael@0: } michael@0: michael@0: michael@0: nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter) michael@0: { michael@0: *aPrinter = new nsXMLPrettyPrinter; michael@0: NS_ADDREF(*aPrinter); michael@0: return NS_OK; michael@0: }