Back to index

lightning-sunbird  0.9+nobinonly
nsXMLPrettyPrinter.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <jonas@sicking.cc> (Original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038  
00039 #include "nsXMLPrettyPrinter.h"
00040 #include "nsContentUtils.h"
00041 #include "nsIDOMDocumentView.h"
00042 #include "nsIDOMAbstractView.h"
00043 #include "nsIDOMCSSStyleDeclaration.h"
00044 #include "nsIDOMViewCSS.h"
00045 #include "nsIDOMDocumentXBL.h"
00046 #include "nsIBindingManager.h"
00047 #include "nsIObserver.h"
00048 #include "nsIXSLTProcessor.h"
00049 #include "nsISyncLoadDOMService.h"
00050 #include "nsIScriptGlobalObject.h"
00051 #include "nsIDOMWindowInternal.h"
00052 #include "nsIDOMElement.h"
00053 #include "nsIDOMDocument.h"
00054 #include "nsIServiceManager.h"
00055 #include "nsNetUtil.h"
00056 #include "nsIContent.h"
00057 #include "nsIDOMDocumentFragment.h"
00058 
00059 NS_IMPL_ISUPPORTS1(nsXMLPrettyPrinter,
00060                    nsIDocumentObserver)
00061 
00062 nsXMLPrettyPrinter::nsXMLPrettyPrinter() : mDocument(nsnull),
00063                                            mUpdateDepth(0),
00064                                            mUnhookPending(PR_FALSE)
00065 {
00066 }
00067 
00068 nsXMLPrettyPrinter::~nsXMLPrettyPrinter()
00069 {
00070     NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
00071 }
00072 
00073 nsresult
00074 nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument)
00075 {
00076     // Check for iframe with display:none. Such iframes don't have presshells
00077     if (!aDocument->GetNumberOfShells()) {
00078         return NS_OK;
00079     }
00080 
00081     // check if we're in an invisible iframe
00082     nsCOMPtr<nsIDOMWindowInternal> internalWin = do_QueryInterface(aDocument->GetScriptGlobalObject());
00083     nsCOMPtr<nsIDOMElement> frameElem;
00084     if (internalWin) {
00085         internalWin->GetFrameElement(getter_AddRefs(frameElem));
00086     }
00087 
00088     if (frameElem) {
00089         nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle;
00090         nsCOMPtr<nsIDOMDocument> frameOwnerDoc;
00091         frameElem->GetOwnerDocument(getter_AddRefs(frameOwnerDoc));
00092         nsCOMPtr<nsIDOMDocumentView> docView = do_QueryInterface(frameOwnerDoc);
00093         if (docView) {
00094             nsCOMPtr<nsIDOMAbstractView> defaultView;
00095             docView->GetDefaultView(getter_AddRefs(defaultView));
00096             nsCOMPtr<nsIDOMViewCSS> defaultCSSView =
00097                 do_QueryInterface(defaultView);
00098             if (defaultCSSView) {
00099                 defaultCSSView->GetComputedStyle(frameElem,
00100                                                  EmptyString(),
00101                                                  getter_AddRefs(computedStyle));
00102             }
00103         }
00104 
00105         if (computedStyle) {
00106             nsAutoString visibility;
00107             computedStyle->GetPropertyValue(NS_LITERAL_STRING("visibility"),
00108                                             visibility);
00109             if (!visibility.EqualsLiteral("visible")) {
00110 
00111                 return NS_OK;
00112             }
00113         }
00114     }
00115 
00116     // check the pref
00117     if (!nsContentUtils::GetBoolPref("layout.xml.prettyprint", PR_TRUE)) {
00118         return NS_OK;
00119     }
00120 
00121     // Ok, we should prettyprint. Let's do it!
00122     nsresult rv = NS_OK;
00123 
00124     // Load the XSLT
00125     nsCOMPtr<nsIURI> xslUri;
00126     rv = NS_NewURI(getter_AddRefs(xslUri),
00127                    NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl"));
00128     NS_ENSURE_SUCCESS(rv, rv);
00129 
00130     nsCOMPtr<nsIChannel> channel;
00131     rv = NS_NewChannel(getter_AddRefs(channel), xslUri, nsnull, nsnull);
00132     NS_ENSURE_SUCCESS(rv, rv);
00133 
00134     nsCOMPtr<nsIDOMDocument> xslDocument;
00135     nsCOMPtr<nsISyncLoadDOMService> loader =
00136        do_GetService("@mozilla.org/content/syncload-dom-service;1", &rv);
00137     NS_ENSURE_SUCCESS(rv, rv);
00138     rv = loader->LoadLocalDocument(channel, nsnull, getter_AddRefs(xslDocument));
00139     NS_ENSURE_SUCCESS(rv, rv);
00140 
00141     // Transform the document
00142     nsCOMPtr<nsIXSLTProcessor> transformer =
00143         do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt", &rv);
00144     NS_ENSURE_SUCCESS(rv, rv);
00145 
00146     rv = transformer->ImportStylesheet(xslDocument);
00147     NS_ENSURE_SUCCESS(rv, rv);
00148 
00149     nsCOMPtr<nsIDOMDocumentFragment> resultFragment;
00150     nsCOMPtr<nsIDOMDocument> sourceDocument = do_QueryInterface(aDocument);
00151     rv = transformer->TransformToFragment(sourceDocument, sourceDocument,
00152                                           getter_AddRefs(resultFragment));
00153     NS_ENSURE_SUCCESS(rv, rv);
00154 
00155     // Add the binding
00156     nsCOMPtr<nsIDOMDocumentXBL> xblDoc = do_QueryInterface(aDocument);
00157     NS_ASSERTION(xblDoc, "xml document doesn't implement nsIDOMDocumentXBL");
00158     NS_ENSURE_TRUE(xblDoc, NS_ERROR_FAILURE);
00159 
00160     nsCOMPtr<nsIDOMDocument> dummy;
00161     xblDoc->LoadBindingDocument(NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml"),
00162                                 getter_AddRefs(dummy));
00163 
00164     nsCOMPtr<nsIDOMElement> rootElem;
00165     sourceDocument->GetDocumentElement(getter_AddRefs(rootElem));
00166     NS_ENSURE_TRUE(rootElem, NS_ERROR_UNEXPECTED);
00167 
00168     rv = xblDoc->AddBinding(rootElem,
00169                             NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint"));
00170     NS_ENSURE_SUCCESS(rv, rv);
00171 
00172     // Hand the result document to the binding
00173     nsCOMPtr<nsIObserver> binding;
00174     nsCOMPtr<nsIContent> rootCont = do_QueryInterface(rootElem);
00175     NS_ASSERTION(rootCont, "Element doesn't implement nsIContent");
00176     aDocument->BindingManager()->GetBindingImplementation(rootCont,
00177                                               NS_GET_IID(nsIObserver),
00178                                               (void**)getter_AddRefs(binding));
00179     NS_ASSERTION(binding, "Prettyprint binding doesn't implement nsIObserver");
00180     NS_ENSURE_TRUE(binding, NS_ERROR_UNEXPECTED);
00181     
00182     rv = binding->Observe(resultFragment, "prettyprint-dom-created",
00183                           EmptyString().get());
00184     NS_ENSURE_SUCCESS(rv, rv);
00185 
00186     // Observe the document so we know when to switch to "normal" view
00187     aDocument->AddObserver(this);
00188     mDocument = aDocument;
00189 
00190     NS_ADDREF_THIS();
00191 
00192     return NS_OK;
00193 }
00194 
00195 void
00196 nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent)
00197 {
00198     // If there either aContent is null (the document-node was modified) or
00199     // there isn't a binding parent we know it's non-anonymous content.
00200     if (!aContent || !aContent->GetBindingParent()) {
00201         mUnhookPending = PR_TRUE;
00202     }
00203 }
00204 
00205 // nsIDocumentObserver implementation
00206 
00207 void
00208 nsXMLPrettyPrinter::BeginUpdate(nsIDocument* aDocument,
00209                                 nsUpdateType aUpdateType)
00210 {
00211     mUpdateDepth++;
00212 }
00213 
00214 void
00215 nsXMLPrettyPrinter::EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType)
00216 {
00217     mUpdateDepth--;
00218 
00219     // Only remove the binding once we're outside all updates. This protects us
00220     // from nasty surprices of elements being removed from the document in the
00221     // midst of setting attributes etc.
00222     if (mUnhookPending && mUpdateDepth == 0) {
00223         mDocument->RemoveObserver(this);
00224         nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(mDocument);
00225         nsCOMPtr<nsIDOMElement> rootElem;
00226         document->GetDocumentElement(getter_AddRefs(rootElem));
00227 
00228         if (rootElem) {
00229             nsCOMPtr<nsIDOMDocumentXBL> xblDoc = do_QueryInterface(mDocument);
00230             xblDoc->RemoveBinding(rootElem,
00231                                   NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint"));
00232         }
00233 
00234         mDocument = nsnull;
00235 
00236         NS_RELEASE_THIS();
00237     }
00238 }
00239 
00240 void
00241 nsXMLPrettyPrinter::AttributeChanged(nsIDocument* aDocument,
00242                                      nsIContent* aContent,
00243                                      PRInt32 aNameSpaceID,
00244                                      nsIAtom* aAttribute,
00245                                      PRInt32 aModType)
00246 {
00247     MaybeUnhook(aContent);
00248 }
00249 
00250 void
00251 nsXMLPrettyPrinter::ContentAppended(nsIDocument* aDocument,
00252                                     nsIContent* aContainer,
00253                                     PRInt32 aNewIndexInContainer)
00254 {
00255     MaybeUnhook(aContainer);
00256 }
00257 
00258 void
00259 nsXMLPrettyPrinter::ContentInserted(nsIDocument* aDocument,
00260                                     nsIContent* aContainer,
00261                                     nsIContent* aChild,
00262                                     PRInt32 aIndexInContainer)
00263 {
00264     MaybeUnhook(aContainer);
00265 }
00266 
00267 void
00268 nsXMLPrettyPrinter::ContentRemoved(nsIDocument* aDocument,
00269                                    nsIContent* aContainer,
00270                                    nsIContent* aChild,
00271                                    PRInt32 aIndexInContainer)
00272 {
00273     MaybeUnhook(aContainer);
00274 }
00275 
00276 void
00277 nsXMLPrettyPrinter::DocumentWillBeDestroyed(nsIDocument* aDocument)
00278 {
00279     mDocument = nsnull;
00280     NS_RELEASE_THIS();
00281 }
00282 
00283 
00284 nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter)
00285 {
00286     *aPrinter = new nsXMLPrettyPrinter;
00287     NS_ENSURE_TRUE(*aPrinter, NS_ERROR_OUT_OF_MEMORY);
00288     NS_ADDREF(*aPrinter);
00289     return NS_OK;
00290 }