Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xml/nsXMLPrettyPrinter.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsIObserver.h"
10
#include "nsSyncLoadService.h"
11
#include "nsPIDOMWindow.h"
12
#include "nsIServiceManager.h"
13
#include "nsNetUtil.h"
14
#include "mozilla/dom/Element.h"
15
#include "nsBindingManager.h"
16
#include "nsXBLService.h"
17
#include "nsIScriptSecurityManager.h"
18
#include "mozilla/Preferences.h"
19
#include "nsIDocument.h"
20
#include "nsVariant.h"
21
#include "mozilla/dom/CustomEvent.h"
22
#include "mozilla/dom/DocumentFragment.h"
23
#include "mozilla/dom/ScriptSettings.h"
24
#include "mozilla/dom/ToJSValue.h"
25
#include "mozilla/dom/txMozillaXSLTProcessor.h"
26
27
using namespace mozilla;
28
using namespace mozilla::dom;
29
30
NS_IMPL_ISUPPORTS(nsXMLPrettyPrinter,
31
                  nsIDocumentObserver,
32
                  nsIMutationObserver)
33
34
nsXMLPrettyPrinter::nsXMLPrettyPrinter() : mDocument(nullptr),
35
                                           mUnhookPending(false)
36
0
{
37
0
}
38
39
nsXMLPrettyPrinter::~nsXMLPrettyPrinter()
40
0
{
41
0
    NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
42
0
}
43
44
nsresult
45
nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument,
46
                                bool* aDidPrettyPrint)
47
0
{
48
0
    *aDidPrettyPrint = false;
49
0
50
0
    // Check for iframe with display:none. Such iframes don't have presshells
51
0
    nsCOMPtr<nsIPresShell> shell = aDocument->GetShell();
52
0
    if (!shell) {
53
0
        return NS_OK;
54
0
    }
55
0
56
0
    // check if we're in an invisible iframe
57
0
    nsPIDOMWindowOuter *internalWin = aDocument->GetWindow();
58
0
    nsCOMPtr<Element> frameElem;
59
0
    if (internalWin) {
60
0
        frameElem = internalWin->GetFrameElementInternal();
61
0
    }
62
0
63
0
    if (frameElem) {
64
0
        nsCOMPtr<nsICSSDeclaration> computedStyle;
65
0
        if (nsIDocument* frameOwnerDoc = frameElem->OwnerDoc()) {
66
0
            nsPIDOMWindowOuter* window = frameOwnerDoc->GetDefaultView();
67
0
            if (window) {
68
0
                nsCOMPtr<nsPIDOMWindowInner> innerWindow =
69
0
                    window->GetCurrentInnerWindow();
70
0
71
0
                ErrorResult dummy;
72
0
                computedStyle = innerWindow->GetComputedStyle(*frameElem,
73
0
                                                              EmptyString(),
74
0
                                                              dummy);
75
0
                dummy.SuppressException();
76
0
            }
77
0
        }
78
0
79
0
        if (computedStyle) {
80
0
            nsAutoString visibility;
81
0
            computedStyle->GetPropertyValue(NS_LITERAL_STRING("visibility"),
82
0
                                            visibility);
83
0
            if (!visibility.EqualsLiteral("visible")) {
84
0
85
0
                return NS_OK;
86
0
            }
87
0
        }
88
0
    }
89
0
90
0
    // check the pref
91
0
    if (!Preferences::GetBool("layout.xml.prettyprint", true)) {
92
0
        return NS_OK;
93
0
    }
94
0
95
0
    // Ok, we should prettyprint. Let's do it!
96
0
    *aDidPrettyPrint = true;
97
0
    nsresult rv = NS_OK;
98
0
99
0
    // Load the XSLT
100
0
    nsCOMPtr<nsIURI> xslUri;
101
0
    rv = NS_NewURI(getter_AddRefs(xslUri),
102
0
                   NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl"));
103
0
    NS_ENSURE_SUCCESS(rv, rv);
104
0
105
0
    nsCOMPtr<nsIDocument> xslDocument;
106
0
    rv = nsSyncLoadService::LoadDocument(xslUri, nsIContentPolicy::TYPE_XSLT,
107
0
                                         nsContentUtils::GetSystemPrincipal(),
108
0
                                         nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
109
0
                                         nullptr, true, mozilla::net::RP_Unset,
110
0
                                         getter_AddRefs(xslDocument));
111
0
    NS_ENSURE_SUCCESS(rv, rv);
112
0
113
0
    // Transform the document
114
0
    RefPtr<txMozillaXSLTProcessor> transformer = new txMozillaXSLTProcessor();
115
0
    ErrorResult err;
116
0
    transformer->ImportStylesheet(*xslDocument, err);
117
0
    if (NS_WARN_IF(err.Failed())) {
118
0
        return err.StealNSResult();
119
0
    }
120
0
121
0
    RefPtr<DocumentFragment> resultFragment =
122
0
        transformer->TransformToFragment(*aDocument, *aDocument, err);
123
0
    if (NS_WARN_IF(err.Failed())) {
124
0
        return err.StealNSResult();
125
0
    }
126
0
127
0
    // Find the root element
128
0
    RefPtr<Element> rootElement = aDocument->GetRootElement();
129
0
    NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
130
0
131
0
    if (nsContentUtils::IsShadowDOMEnabled()) {
132
0
        // Attach a closed shadow root on it.
133
0
        RefPtr<ShadowRoot> shadowRoot =
134
0
            rootElement->AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
135
0
136
0
        // Append the document fragment to the shadow dom.
137
0
        shadowRoot->AppendChild(*resultFragment, err);
138
0
        if (NS_WARN_IF(err.Failed())) {
139
0
            return err.StealNSResult();
140
0
        }
141
0
    } else {
142
0
        //
143
0
        // Apply the prettprint XBL binding.
144
0
        //
145
0
        // We take some shortcuts here. In particular, we don't bother invoking the
146
0
        // contstructor (since the binding has no constructor), and we don't bother
147
0
        // calling LoadBindingDocument because it's a chrome:// URI and thus will get
148
0
        // sync loaded no matter what.
149
0
        //
150
0
151
0
        // Grab the XBL service.
152
0
        nsXBLService* xblService = nsXBLService::GetInstance();
153
0
        NS_ENSURE_TRUE(xblService, NS_ERROR_NOT_AVAILABLE);
154
0
155
0
        // Compute the binding URI.
156
0
        nsCOMPtr<nsIURI> bindingUri;
157
0
        rv = NS_NewURI(getter_AddRefs(bindingUri),
158
0
            NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint"));
159
0
        NS_ENSURE_SUCCESS(rv, rv);
160
0
161
0
        // Grab the system principal.
162
0
        nsCOMPtr<nsIPrincipal> sysPrincipal;
163
0
        nsContentUtils::GetSecurityManager()->
164
0
            GetSystemPrincipal(getter_AddRefs(sysPrincipal));
165
0
166
0
        // Destroy any existing frames before we unbind anonymous content.
167
0
        // Note that the shell might be Destroy'ed by now (see bug 1415541).
168
0
        if (!shell->IsDestroying()) {
169
0
            shell->DestroyFramesForAndRestyle(rootElement);
170
0
        }
171
0
172
0
        // Load the bindings.
173
0
        RefPtr<nsXBLBinding> unused;
174
0
        bool ignored;
175
0
        rv = xblService->LoadBindings(rootElement, bindingUri, sysPrincipal,
176
0
                                      getter_AddRefs(unused), &ignored);
177
0
        NS_ENSURE_SUCCESS(rv, rv);
178
0
179
0
        // Fire an event at the bound element to pass it |resultFragment|.
180
0
        RefPtr<CustomEvent> event =
181
0
          NS_NewDOMCustomEvent(rootElement, nullptr, nullptr);
182
0
        MOZ_ASSERT(event);
183
0
        AutoJSAPI jsapi;
184
0
        if (!jsapi.Init(event->GetParentObject())) {
185
0
            return NS_ERROR_UNEXPECTED;
186
0
        }
187
0
        JSContext* cx = jsapi.cx();
188
0
        JS::Rooted<JS::Value> detail(cx);
189
0
        if (!ToJSValue(cx, resultFragment, &detail)) {
190
0
            return NS_ERROR_UNEXPECTED;
191
0
        }
192
0
        event->InitCustomEvent(cx, NS_LITERAL_STRING("prettyprint-dom-created"),
193
0
                               /* bubbles = */ false, /* cancelable = */ false,
194
0
                               detail);
195
0
196
0
        event->SetTrusted(true);
197
0
        rootElement->DispatchEvent(*event, err);
198
0
        if (NS_WARN_IF(err.Failed())) {
199
0
            return err.StealNSResult();
200
0
        }
201
0
    }
202
0
203
0
    // Observe the document so we know when to switch to "normal" view
204
0
    aDocument->AddObserver(this);
205
0
    mDocument = aDocument;
206
0
207
0
    NS_ADDREF_THIS();
208
0
209
0
    return NS_OK;
210
0
}
211
212
void
213
nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent)
214
0
{
215
0
    // If aContent is null, the document-node was modified.
216
0
    // If it is not null but in the shadow tree, the <scrollbar> NACs,
217
0
    // or the XBL binding, the change was in the generated content, and
218
0
    // it should be ignored.
219
0
    bool isGeneratedContent = !aContent ?
220
0
        false :
221
0
        aContent->GetBindingParent() || aContent->IsInShadowTree();
222
0
223
0
    if (!isGeneratedContent && !mUnhookPending) {
224
0
        // Can't blindly to mUnhookPending after AddScriptRunner,
225
0
        // since AddScriptRunner _could_ in theory run us
226
0
        // synchronously
227
0
        mUnhookPending = true;
228
0
        nsContentUtils::AddScriptRunner(NewRunnableMethod(
229
0
          "nsXMLPrettyPrinter::Unhook", this, &nsXMLPrettyPrinter::Unhook));
230
0
    }
231
0
}
232
233
void
234
nsXMLPrettyPrinter::Unhook()
235
0
{
236
0
    mDocument->RemoveObserver(this);
237
0
    nsCOMPtr<Element> element = mDocument->GetDocumentElement();
238
0
239
0
    if (element) {
240
0
        // Remove the shadow root
241
0
        element->UnattachShadow();
242
0
243
0
        // Remove the bound XBL binding
244
0
        mDocument->BindingManager()->ClearBinding(element);
245
0
    }
246
0
247
0
    mDocument = nullptr;
248
0
249
0
    NS_RELEASE_THIS();
250
0
}
251
252
void
253
nsXMLPrettyPrinter::AttributeChanged(Element* aElement,
254
                                     int32_t aNameSpaceID,
255
                                     nsAtom* aAttribute,
256
                                     int32_t aModType,
257
                                     const nsAttrValue* aOldValue)
258
0
{
259
0
    MaybeUnhook(aElement);
260
0
}
261
262
void
263
nsXMLPrettyPrinter::ContentAppended(nsIContent* aFirstNewContent)
264
0
{
265
0
    MaybeUnhook(aFirstNewContent->GetParent());
266
0
}
267
268
void
269
nsXMLPrettyPrinter::ContentInserted(nsIContent* aChild)
270
0
{
271
0
    MaybeUnhook(aChild->GetParent());
272
0
}
273
274
void
275
nsXMLPrettyPrinter::ContentRemoved(nsIContent* aChild,
276
                                   nsIContent* aPreviousSibling)
277
0
{
278
0
    MaybeUnhook(aChild->GetParent());
279
0
}
280
281
void
282
nsXMLPrettyPrinter::NodeWillBeDestroyed(const nsINode* aNode)
283
0
{
284
0
    mDocument = nullptr;
285
0
    NS_RELEASE_THIS();
286
0
}
287
288
289
nsresult NS_NewXMLPrettyPrinter(nsXMLPrettyPrinter** aPrinter)
290
0
{
291
0
    *aPrinter = new nsXMLPrettyPrinter;
292
0
    NS_ADDREF(*aPrinter);
293
0
    return NS_OK;
294
0
}