|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 #include "nsContentUtils.h" |
|
8 #include "nsIDOMCSSStyleDeclaration.h" |
|
9 #include "nsIDOMDocumentXBL.h" |
|
10 #include "nsIObserver.h" |
|
11 #include "nsIXSLTProcessor.h" |
|
12 #include "nsSyncLoadService.h" |
|
13 #include "nsPIDOMWindow.h" |
|
14 #include "nsIDOMElement.h" |
|
15 #include "nsIDOMDocument.h" |
|
16 #include "nsIServiceManager.h" |
|
17 #include "nsNetUtil.h" |
|
18 #include "mozilla/dom/Element.h" |
|
19 #include "nsIDOMDocumentFragment.h" |
|
20 #include "nsBindingManager.h" |
|
21 #include "nsXBLService.h" |
|
22 #include "nsIScriptSecurityManager.h" |
|
23 #include "mozilla/Preferences.h" |
|
24 #include "nsIDocument.h" |
|
25 #include "nsVariant.h" |
|
26 #include "nsIDOMCustomEvent.h" |
|
27 #include "GeneratedEvents.h" |
|
28 |
|
29 using namespace mozilla; |
|
30 using namespace mozilla::dom; |
|
31 |
|
32 NS_IMPL_ISUPPORTS(nsXMLPrettyPrinter, |
|
33 nsIDocumentObserver, |
|
34 nsIMutationObserver) |
|
35 |
|
36 nsXMLPrettyPrinter::nsXMLPrettyPrinter() : mDocument(nullptr), |
|
37 mUnhookPending(false) |
|
38 { |
|
39 } |
|
40 |
|
41 nsXMLPrettyPrinter::~nsXMLPrettyPrinter() |
|
42 { |
|
43 NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still"); |
|
44 } |
|
45 |
|
46 nsresult |
|
47 nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, |
|
48 bool* aDidPrettyPrint) |
|
49 { |
|
50 *aDidPrettyPrint = false; |
|
51 |
|
52 // Check for iframe with display:none. Such iframes don't have presshells |
|
53 if (!aDocument->GetShell()) { |
|
54 return NS_OK; |
|
55 } |
|
56 |
|
57 // check if we're in an invisible iframe |
|
58 nsPIDOMWindow *internalWin = aDocument->GetWindow(); |
|
59 nsCOMPtr<nsIDOMElement> frameElem; |
|
60 if (internalWin) { |
|
61 internalWin->GetFrameElement(getter_AddRefs(frameElem)); |
|
62 } |
|
63 |
|
64 if (frameElem) { |
|
65 nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle; |
|
66 nsCOMPtr<nsIDOMDocument> frameOwnerDoc; |
|
67 frameElem->GetOwnerDocument(getter_AddRefs(frameOwnerDoc)); |
|
68 if (frameOwnerDoc) { |
|
69 nsCOMPtr<nsIDOMWindow> window; |
|
70 frameOwnerDoc->GetDefaultView(getter_AddRefs(window)); |
|
71 if (window) { |
|
72 window->GetComputedStyle(frameElem, |
|
73 EmptyString(), |
|
74 getter_AddRefs(computedStyle)); |
|
75 } |
|
76 } |
|
77 |
|
78 if (computedStyle) { |
|
79 nsAutoString visibility; |
|
80 computedStyle->GetPropertyValue(NS_LITERAL_STRING("visibility"), |
|
81 visibility); |
|
82 if (!visibility.EqualsLiteral("visible")) { |
|
83 |
|
84 return NS_OK; |
|
85 } |
|
86 } |
|
87 } |
|
88 |
|
89 // check the pref |
|
90 if (!Preferences::GetBool("layout.xml.prettyprint", true)) { |
|
91 return NS_OK; |
|
92 } |
|
93 |
|
94 // Ok, we should prettyprint. Let's do it! |
|
95 *aDidPrettyPrint = true; |
|
96 nsresult rv = NS_OK; |
|
97 |
|
98 // Load the XSLT |
|
99 nsCOMPtr<nsIURI> xslUri; |
|
100 rv = NS_NewURI(getter_AddRefs(xslUri), |
|
101 NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl")); |
|
102 NS_ENSURE_SUCCESS(rv, rv); |
|
103 |
|
104 nsCOMPtr<nsIDOMDocument> xslDocument; |
|
105 rv = nsSyncLoadService::LoadDocument(xslUri, nullptr, nullptr, true, |
|
106 getter_AddRefs(xslDocument)); |
|
107 NS_ENSURE_SUCCESS(rv, rv); |
|
108 |
|
109 // Transform the document |
|
110 nsCOMPtr<nsIXSLTProcessor> transformer = |
|
111 do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt", &rv); |
|
112 NS_ENSURE_SUCCESS(rv, rv); |
|
113 |
|
114 rv = transformer->ImportStylesheet(xslDocument); |
|
115 NS_ENSURE_SUCCESS(rv, rv); |
|
116 |
|
117 nsCOMPtr<nsIDOMDocumentFragment> resultFragment; |
|
118 nsCOMPtr<nsIDOMDocument> sourceDocument = do_QueryInterface(aDocument); |
|
119 rv = transformer->TransformToFragment(sourceDocument, sourceDocument, |
|
120 getter_AddRefs(resultFragment)); |
|
121 NS_ENSURE_SUCCESS(rv, rv); |
|
122 |
|
123 // |
|
124 // Apply the prettprint XBL binding. |
|
125 // |
|
126 // We take some shortcuts here. In particular, we don't bother invoking the |
|
127 // contstructor (since the binding has no constructor), and we don't bother |
|
128 // calling LoadBindingDocument because it's a chrome:// URI and thus will get |
|
129 // sync loaded no matter what. |
|
130 // |
|
131 |
|
132 // Grab the XBL service. |
|
133 nsXBLService* xblService = nsXBLService::GetInstance(); |
|
134 NS_ENSURE_TRUE(xblService, NS_ERROR_NOT_AVAILABLE); |
|
135 |
|
136 // Compute the binding URI. |
|
137 nsCOMPtr<nsIURI> bindingUri; |
|
138 rv = NS_NewURI(getter_AddRefs(bindingUri), |
|
139 NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint")); |
|
140 NS_ENSURE_SUCCESS(rv, rv); |
|
141 |
|
142 // Compute the bound element. |
|
143 nsCOMPtr<nsIContent> rootCont = aDocument->GetRootElement(); |
|
144 NS_ENSURE_TRUE(rootCont, NS_ERROR_UNEXPECTED); |
|
145 |
|
146 // Grab the system principal. |
|
147 nsCOMPtr<nsIPrincipal> sysPrincipal; |
|
148 nsContentUtils::GetSecurityManager()-> |
|
149 GetSystemPrincipal(getter_AddRefs(sysPrincipal)); |
|
150 |
|
151 // Load the bindings. |
|
152 nsRefPtr<nsXBLBinding> unused; |
|
153 bool ignored; |
|
154 rv = xblService->LoadBindings(rootCont, bindingUri, sysPrincipal, |
|
155 getter_AddRefs(unused), &ignored); |
|
156 NS_ENSURE_SUCCESS(rv, rv); |
|
157 |
|
158 // Fire an event at the bound element to pass it |resultFragment|. |
|
159 nsCOMPtr<nsIDOMEvent> domEvent; |
|
160 rv = NS_NewDOMCustomEvent(getter_AddRefs(domEvent), rootCont, |
|
161 nullptr, nullptr); |
|
162 NS_ENSURE_SUCCESS(rv, rv); |
|
163 nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent); |
|
164 MOZ_ASSERT(customEvent); |
|
165 nsCOMPtr<nsIWritableVariant> resultFragmentVariant = new nsVariant(); |
|
166 rv = resultFragmentVariant->SetAsISupports(resultFragment); |
|
167 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
168 rv = customEvent->InitCustomEvent(NS_LITERAL_STRING("prettyprint-dom-created"), |
|
169 /* bubbles = */ false, /* cancelable = */ false, |
|
170 /* detail = */ resultFragmentVariant); |
|
171 NS_ENSURE_SUCCESS(rv, rv); |
|
172 customEvent->SetTrusted(true); |
|
173 bool dummy; |
|
174 rv = rootCont->DispatchEvent(domEvent, &dummy); |
|
175 NS_ENSURE_SUCCESS(rv, rv); |
|
176 |
|
177 // Observe the document so we know when to switch to "normal" view |
|
178 aDocument->AddObserver(this); |
|
179 mDocument = aDocument; |
|
180 |
|
181 NS_ADDREF_THIS(); |
|
182 |
|
183 return NS_OK; |
|
184 } |
|
185 |
|
186 void |
|
187 nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent) |
|
188 { |
|
189 // If there either aContent is null (the document-node was modified) or |
|
190 // there isn't a binding parent we know it's non-anonymous content. |
|
191 if ((!aContent || !aContent->GetBindingParent()) && !mUnhookPending) { |
|
192 // Can't blindly to mUnhookPending after AddScriptRunner, |
|
193 // since AddScriptRunner _could_ in theory run us |
|
194 // synchronously |
|
195 mUnhookPending = true; |
|
196 nsContentUtils::AddScriptRunner( |
|
197 NS_NewRunnableMethod(this, &nsXMLPrettyPrinter::Unhook)); |
|
198 } |
|
199 } |
|
200 |
|
201 void |
|
202 nsXMLPrettyPrinter::Unhook() |
|
203 { |
|
204 mDocument->RemoveObserver(this); |
|
205 nsCOMPtr<Element> element = mDocument->GetDocumentElement(); |
|
206 |
|
207 if (element) { |
|
208 mDocument->BindingManager()->ClearBinding(element); |
|
209 } |
|
210 |
|
211 mDocument = nullptr; |
|
212 |
|
213 NS_RELEASE_THIS(); |
|
214 } |
|
215 |
|
216 void |
|
217 nsXMLPrettyPrinter::AttributeChanged(nsIDocument* aDocument, |
|
218 Element* aElement, |
|
219 int32_t aNameSpaceID, |
|
220 nsIAtom* aAttribute, |
|
221 int32_t aModType) |
|
222 { |
|
223 MaybeUnhook(aElement); |
|
224 } |
|
225 |
|
226 void |
|
227 nsXMLPrettyPrinter::ContentAppended(nsIDocument* aDocument, |
|
228 nsIContent* aContainer, |
|
229 nsIContent* aFirstNewContent, |
|
230 int32_t aNewIndexInContainer) |
|
231 { |
|
232 MaybeUnhook(aContainer); |
|
233 } |
|
234 |
|
235 void |
|
236 nsXMLPrettyPrinter::ContentInserted(nsIDocument* aDocument, |
|
237 nsIContent* aContainer, |
|
238 nsIContent* aChild, |
|
239 int32_t aIndexInContainer) |
|
240 { |
|
241 MaybeUnhook(aContainer); |
|
242 } |
|
243 |
|
244 void |
|
245 nsXMLPrettyPrinter::ContentRemoved(nsIDocument* aDocument, |
|
246 nsIContent* aContainer, |
|
247 nsIContent* aChild, |
|
248 int32_t aIndexInContainer, |
|
249 nsIContent* aPreviousSibling) |
|
250 { |
|
251 MaybeUnhook(aContainer); |
|
252 } |
|
253 |
|
254 void |
|
255 nsXMLPrettyPrinter::NodeWillBeDestroyed(const nsINode* aNode) |
|
256 { |
|
257 mDocument = nullptr; |
|
258 NS_RELEASE_THIS(); |
|
259 } |
|
260 |
|
261 |
|
262 nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter) |
|
263 { |
|
264 *aPrinter = new nsXMLPrettyPrinter; |
|
265 NS_ADDREF(*aPrinter); |
|
266 return NS_OK; |
|
267 } |