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