1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonas Sicking <jonas@sicking.cc> (Original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsXMLPrettyPrinter.h"
40 : #include "nsContentUtils.h"
41 : #include "nsIDOMCSSStyleDeclaration.h"
42 : #include "nsIDOMDocumentXBL.h"
43 : #include "nsIObserver.h"
44 : #include "nsIXSLTProcessor.h"
45 : #include "nsSyncLoadService.h"
46 : #include "nsPIDOMWindow.h"
47 : #include "nsIDOMElement.h"
48 : #include "nsIDOMDocument.h"
49 : #include "nsIServiceManager.h"
50 : #include "nsNetUtil.h"
51 : #include "mozilla/dom/Element.h"
52 : #include "nsIDOMDocumentFragment.h"
53 : #include "nsBindingManager.h"
54 : #include "nsIScriptSecurityManager.h"
55 : #include "mozilla/Preferences.h"
56 :
57 : using namespace mozilla;
58 : using namespace mozilla::dom;
59 :
60 0 : NS_IMPL_ISUPPORTS2(nsXMLPrettyPrinter,
61 : nsIDocumentObserver,
62 : nsIMutationObserver)
63 :
64 0 : nsXMLPrettyPrinter::nsXMLPrettyPrinter() : mDocument(nsnull),
65 : mUpdateDepth(0),
66 0 : mUnhookPending(false)
67 : {
68 0 : }
69 :
70 0 : nsXMLPrettyPrinter::~nsXMLPrettyPrinter()
71 : {
72 0 : NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
73 0 : }
74 :
75 : nsresult
76 0 : nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument,
77 : bool* aDidPrettyPrint)
78 : {
79 0 : *aDidPrettyPrint = false;
80 :
81 : // Check for iframe with display:none. Such iframes don't have presshells
82 0 : if (!aDocument->GetShell()) {
83 0 : return NS_OK;
84 : }
85 :
86 : // check if we're in an invisible iframe
87 0 : nsPIDOMWindow *internalWin = aDocument->GetWindow();
88 0 : nsCOMPtr<nsIDOMElement> frameElem;
89 0 : if (internalWin) {
90 0 : internalWin->GetFrameElement(getter_AddRefs(frameElem));
91 : }
92 :
93 0 : if (frameElem) {
94 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle;
95 0 : nsCOMPtr<nsIDOMDocument> frameOwnerDoc;
96 0 : frameElem->GetOwnerDocument(getter_AddRefs(frameOwnerDoc));
97 0 : if (frameOwnerDoc) {
98 0 : nsCOMPtr<nsIDOMWindow> window;
99 0 : frameOwnerDoc->GetDefaultView(getter_AddRefs(window));
100 0 : if (window) {
101 0 : window->GetComputedStyle(frameElem,
102 0 : EmptyString(),
103 0 : getter_AddRefs(computedStyle));
104 : }
105 : }
106 :
107 0 : if (computedStyle) {
108 0 : nsAutoString visibility;
109 0 : computedStyle->GetPropertyValue(NS_LITERAL_STRING("visibility"),
110 0 : visibility);
111 0 : if (!visibility.EqualsLiteral("visible")) {
112 :
113 0 : return NS_OK;
114 : }
115 : }
116 : }
117 :
118 : // check the pref
119 0 : if (!Preferences::GetBool("layout.xml.prettyprint", true)) {
120 0 : return NS_OK;
121 : }
122 :
123 : // Ok, we should prettyprint. Let's do it!
124 0 : *aDidPrettyPrint = true;
125 0 : nsresult rv = NS_OK;
126 :
127 : // Load the XSLT
128 0 : nsCOMPtr<nsIURI> xslUri;
129 0 : rv = NS_NewURI(getter_AddRefs(xslUri),
130 0 : NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl"));
131 0 : NS_ENSURE_SUCCESS(rv, rv);
132 :
133 0 : nsCOMPtr<nsIDOMDocument> xslDocument;
134 : rv = nsSyncLoadService::LoadDocument(xslUri, nsnull, nsnull, true,
135 0 : getter_AddRefs(xslDocument));
136 0 : NS_ENSURE_SUCCESS(rv, rv);
137 :
138 : // Transform the document
139 : nsCOMPtr<nsIXSLTProcessor> transformer =
140 0 : do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt", &rv);
141 0 : NS_ENSURE_SUCCESS(rv, rv);
142 :
143 0 : rv = transformer->ImportStylesheet(xslDocument);
144 0 : NS_ENSURE_SUCCESS(rv, rv);
145 :
146 0 : nsCOMPtr<nsIDOMDocumentFragment> resultFragment;
147 0 : nsCOMPtr<nsIDOMDocument> sourceDocument = do_QueryInterface(aDocument);
148 0 : rv = transformer->TransformToFragment(sourceDocument, sourceDocument,
149 0 : getter_AddRefs(resultFragment));
150 0 : NS_ENSURE_SUCCESS(rv, rv);
151 :
152 : // Add the binding
153 0 : nsCOMPtr<nsIDOMDocumentXBL> xblDoc = do_QueryInterface(aDocument);
154 0 : NS_ASSERTION(xblDoc, "xml document doesn't implement nsIDOMDocumentXBL");
155 0 : NS_ENSURE_TRUE(xblDoc, NS_ERROR_FAILURE);
156 :
157 0 : nsCOMPtr<nsIURI> bindingUri;
158 0 : rv = NS_NewURI(getter_AddRefs(bindingUri),
159 0 : NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint"));
160 0 : NS_ENSURE_SUCCESS(rv, rv);
161 :
162 0 : nsCOMPtr<nsIPrincipal> sysPrincipal;
163 0 : nsContentUtils::GetSecurityManager()->
164 0 : GetSystemPrincipal(getter_AddRefs(sysPrincipal));
165 : aDocument->BindingManager()->LoadBindingDocument(aDocument, bindingUri,
166 0 : sysPrincipal);
167 :
168 0 : nsCOMPtr<nsIContent> rootCont = aDocument->GetRootElement();
169 0 : NS_ENSURE_TRUE(rootCont, NS_ERROR_UNEXPECTED);
170 :
171 : rv = aDocument->BindingManager()->AddLayeredBinding(rootCont, bindingUri,
172 0 : sysPrincipal);
173 0 : NS_ENSURE_SUCCESS(rv, rv);
174 :
175 : // Hand the result document to the binding
176 0 : nsCOMPtr<nsIObserver> binding;
177 : aDocument->BindingManager()->GetBindingImplementation(rootCont,
178 : NS_GET_IID(nsIObserver),
179 0 : (void**)getter_AddRefs(binding));
180 0 : NS_ASSERTION(binding, "Prettyprint binding doesn't implement nsIObserver");
181 0 : NS_ENSURE_TRUE(binding, NS_ERROR_UNEXPECTED);
182 :
183 0 : rv = binding->Observe(resultFragment, "prettyprint-dom-created",
184 0 : EmptyString().get());
185 0 : NS_ENSURE_SUCCESS(rv, rv);
186 :
187 : // Observe the document so we know when to switch to "normal" view
188 0 : aDocument->AddObserver(this);
189 0 : mDocument = aDocument;
190 :
191 0 : NS_ADDREF_THIS();
192 :
193 0 : return NS_OK;
194 : }
195 :
196 : void
197 0 : nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent)
198 : {
199 : // If there either aContent is null (the document-node was modified) or
200 : // there isn't a binding parent we know it's non-anonymous content.
201 0 : if ((!aContent || !aContent->GetBindingParent()) && !mUnhookPending) {
202 : // Can't blindly to mUnhookPending after AddScriptRunner,
203 : // since AddScriptRunner _could_ in theory run us
204 : // synchronously
205 0 : mUnhookPending = true;
206 : nsContentUtils::AddScriptRunner(
207 0 : NS_NewRunnableMethod(this, &nsXMLPrettyPrinter::Unhook));
208 : }
209 0 : }
210 :
211 : void
212 0 : nsXMLPrettyPrinter::Unhook()
213 : {
214 0 : mDocument->RemoveObserver(this);
215 0 : nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(mDocument);
216 0 : nsCOMPtr<nsIDOMElement> rootElem;
217 0 : document->GetDocumentElement(getter_AddRefs(rootElem));
218 :
219 0 : if (rootElem) {
220 0 : nsCOMPtr<nsIDOMDocumentXBL> xblDoc = do_QueryInterface(mDocument);
221 0 : xblDoc->RemoveBinding(rootElem,
222 0 : NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint"));
223 : }
224 :
225 0 : mDocument = nsnull;
226 :
227 0 : NS_RELEASE_THIS();
228 0 : }
229 :
230 : void
231 0 : nsXMLPrettyPrinter::AttributeChanged(nsIDocument* aDocument,
232 : Element* aElement,
233 : PRInt32 aNameSpaceID,
234 : nsIAtom* aAttribute,
235 : PRInt32 aModType)
236 : {
237 0 : MaybeUnhook(aElement);
238 0 : }
239 :
240 : void
241 0 : nsXMLPrettyPrinter::ContentAppended(nsIDocument* aDocument,
242 : nsIContent* aContainer,
243 : nsIContent* aFirstNewContent,
244 : PRInt32 aNewIndexInContainer)
245 : {
246 0 : MaybeUnhook(aContainer);
247 0 : }
248 :
249 : void
250 0 : nsXMLPrettyPrinter::ContentInserted(nsIDocument* aDocument,
251 : nsIContent* aContainer,
252 : nsIContent* aChild,
253 : PRInt32 aIndexInContainer)
254 : {
255 0 : MaybeUnhook(aContainer);
256 0 : }
257 :
258 : void
259 0 : nsXMLPrettyPrinter::ContentRemoved(nsIDocument* aDocument,
260 : nsIContent* aContainer,
261 : nsIContent* aChild,
262 : PRInt32 aIndexInContainer,
263 : nsIContent* aPreviousSibling)
264 : {
265 0 : MaybeUnhook(aContainer);
266 0 : }
267 :
268 : void
269 0 : nsXMLPrettyPrinter::NodeWillBeDestroyed(const nsINode* aNode)
270 : {
271 0 : mDocument = nsnull;
272 0 : NS_RELEASE_THIS();
273 0 : }
274 :
275 :
276 0 : nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter)
277 : {
278 0 : *aPrinter = new nsXMLPrettyPrinter;
279 0 : NS_ENSURE_TRUE(*aPrinter, NS_ERROR_OUT_OF_MEMORY);
280 0 : NS_ADDREF(*aPrinter);
281 0 : return NS_OK;
282 : }
|