Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "nsXULPrototypeDocument.h"
7
8
#include "nsXULElement.h"
9
#include "nsAString.h"
10
#include "nsIObjectInputStream.h"
11
#include "nsIObjectOutputStream.h"
12
#include "nsIPrincipal.h"
13
#include "nsJSPrincipals.h"
14
#include "nsIScriptObjectPrincipal.h"
15
#include "nsIURI.h"
16
#include "jsapi.h"
17
#include "jsfriendapi.h"
18
#include "nsString.h"
19
#include "nsDOMCID.h"
20
#include "nsNodeInfoManager.h"
21
#include "nsContentUtils.h"
22
#include "nsCCUncollectableMarker.h"
23
#include "xpcpublic.h"
24
#include "mozilla/BasePrincipal.h"
25
#include "mozilla/dom/BindingUtils.h"
26
#include "nsXULPrototypeCache.h"
27
#include "mozilla/DeclarationBlock.h"
28
#include "mozilla/dom/Element.h"
29
#include "mozilla/dom/Text.h"
30
31
using namespace mozilla;
32
using namespace mozilla::dom;
33
using mozilla::dom::DestroyProtoAndIfaceCache;
34
35
uint32_t nsXULPrototypeDocument::gRefCnt;
36
37
//----------------------------------------------------------------------
38
//
39
// ctors, dtors, n' stuff
40
//
41
42
nsXULPrototypeDocument::nsXULPrototypeDocument()
43
: mRoot(nullptr),
44
mLoaded(false),
45
mCCGeneration(0),
46
mGCNumber(0),
47
mWasL10nCached(false) {
48
++gRefCnt;
49
}
50
51
nsresult nsXULPrototypeDocument::Init() {
52
mNodeInfoManager = new nsNodeInfoManager();
53
return mNodeInfoManager->Init(nullptr);
54
}
55
56
nsXULPrototypeDocument::~nsXULPrototypeDocument() {
57
if (mRoot) mRoot->ReleaseSubtree();
58
}
59
60
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument)
61
62
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument)
63
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters)
64
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
65
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument)
66
if (nsCCUncollectableMarker::InGeneration(cb, tmp->mCCGeneration)) {
67
return NS_SUCCESS_INTERRUPTED_TRAVERSE;
68
}
69
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
70
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
71
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
72
73
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument)
74
NS_INTERFACE_MAP_ENTRY(nsISerializable)
75
NS_INTERFACE_MAP_ENTRY(nsISupports)
76
NS_INTERFACE_MAP_END
77
78
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument)
79
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument)
80
81
NS_IMETHODIMP
82
NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult) {
83
*aResult = nullptr;
84
RefPtr<nsXULPrototypeDocument> doc = new nsXULPrototypeDocument();
85
86
nsresult rv = doc->Init();
87
if (NS_FAILED(rv)) {
88
return rv;
89
}
90
91
doc.forget(aResult);
92
return rv;
93
}
94
95
//----------------------------------------------------------------------
96
//
97
// nsISerializable methods
98
//
99
100
NS_IMETHODIMP
101
nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) {
102
nsCOMPtr<nsISupports> supports;
103
nsresult rv = aStream->ReadObject(true, getter_AddRefs(supports));
104
if (NS_FAILED(rv)) {
105
return rv;
106
}
107
mURI = do_QueryInterface(supports);
108
109
// nsIPrincipal mNodeInfoManager->mPrincipal
110
nsAutoCString JSON;
111
rv = aStream->ReadCString(JSON);
112
if (NS_FAILED(rv)) {
113
return rv;
114
}
115
nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::FromJSON(JSON);
116
117
// Better safe than sorry....
118
mNodeInfoManager->SetDocumentPrincipal(principal);
119
120
rv = aStream->ReadBoolean(&mWasL10nCached);
121
if (NS_FAILED(rv)) {
122
return rv;
123
}
124
125
mRoot = new nsXULPrototypeElement();
126
127
// mozilla::dom::NodeInfo table
128
nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos;
129
130
uint32_t count, i;
131
rv = aStream->Read32(&count);
132
if (NS_FAILED(rv)) {
133
return rv;
134
}
135
nsAutoString namespaceURI, prefixStr, localName;
136
bool prefixIsNull;
137
RefPtr<nsAtom> prefix;
138
for (i = 0; i < count; ++i) {
139
rv = aStream->ReadString(namespaceURI);
140
if (NS_FAILED(rv)) {
141
return rv;
142
}
143
rv = aStream->ReadBoolean(&prefixIsNull);
144
if (NS_FAILED(rv)) {
145
return rv;
146
}
147
if (prefixIsNull) {
148
prefix = nullptr;
149
} else {
150
rv = aStream->ReadString(prefixStr);
151
if (NS_FAILED(rv)) {
152
return rv;
153
}
154
prefix = NS_Atomize(prefixStr);
155
}
156
rv = aStream->ReadString(localName);
157
if (NS_FAILED(rv)) {
158
return rv;
159
}
160
161
RefPtr<mozilla::dom::NodeInfo> nodeInfo;
162
// Using UINT16_MAX here as we don't know which nodeinfos will be
163
// used for attributes and which for elements. And that doesn't really
164
// matter.
165
rv = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI,
166
UINT16_MAX, getter_AddRefs(nodeInfo));
167
if (NS_FAILED(rv)) {
168
return rv;
169
}
170
nodeInfos.AppendElement(nodeInfo);
171
}
172
173
// Document contents
174
uint32_t type;
175
while (NS_SUCCEEDED(rv)) {
176
rv = aStream->Read32(&type);
177
if (NS_FAILED(rv)) {
178
return rv;
179
break;
180
}
181
182
if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
183
RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
184
185
rv = pi->Deserialize(aStream, this, mURI, &nodeInfos);
186
if (NS_FAILED(rv)) {
187
return rv;
188
}
189
rv = AddProcessingInstruction(pi);
190
if (NS_FAILED(rv)) {
191
return rv;
192
}
193
} else if ((nsXULPrototypeNode::Type)type ==
194
nsXULPrototypeNode::eType_Element) {
195
rv = mRoot->Deserialize(aStream, this, mURI, &nodeInfos);
196
if (NS_FAILED(rv)) {
197
return rv;
198
}
199
break;
200
} else {
201
MOZ_ASSERT_UNREACHABLE("Unexpected prototype node type");
202
return NS_ERROR_FAILURE;
203
}
204
}
205
206
return NotifyLoadDone();
207
}
208
209
static nsresult GetNodeInfos(nsXULPrototypeElement* aPrototype,
210
nsTArray<RefPtr<mozilla::dom::NodeInfo>>& aArray) {
211
if (aArray.IndexOf(aPrototype->mNodeInfo) == aArray.NoIndex) {
212
aArray.AppendElement(aPrototype->mNodeInfo);
213
}
214
215
// Search attributes
216
size_t i;
217
for (i = 0; i < aPrototype->mAttributes.Length(); ++i) {
218
RefPtr<mozilla::dom::NodeInfo> ni;
219
nsAttrName* name = &aPrototype->mAttributes[i].mName;
220
if (name->IsAtom()) {
221
ni = aPrototype->mNodeInfo->NodeInfoManager()->GetNodeInfo(
222
name->Atom(), nullptr, kNameSpaceID_None, nsINode::ATTRIBUTE_NODE);
223
} else {
224
ni = name->NodeInfo();
225
}
226
227
if (aArray.IndexOf(ni) == aArray.NoIndex) {
228
aArray.AppendElement(ni);
229
}
230
}
231
232
// Search children
233
for (i = 0; i < aPrototype->mChildren.Length(); ++i) {
234
nsXULPrototypeNode* child = aPrototype->mChildren[i];
235
if (child->mType == nsXULPrototypeNode::eType_Element) {
236
nsresult rv =
237
GetNodeInfos(static_cast<nsXULPrototypeElement*>(child), aArray);
238
NS_ENSURE_SUCCESS(rv, rv);
239
}
240
}
241
242
return NS_OK;
243
}
244
245
NS_IMETHODIMP
246
nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream) {
247
nsresult rv;
248
249
rv = aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), true);
250
251
// nsIPrincipal mNodeInfoManager->mPrincipal
252
nsAutoCString JSON;
253
mozilla::BasePrincipal::Cast(mNodeInfoManager->DocumentPrincipal())
254
->ToJSON(JSON);
255
nsresult tmp = aStream->WriteStringZ(JSON.get());
256
if (NS_FAILED(tmp)) {
257
rv = tmp;
258
}
259
260
#ifdef DEBUG
261
// XXX Worrisome if we're caching things without system principal.
262
if (!mNodeInfoManager->DocumentPrincipal()->IsSystemPrincipal()) {
263
NS_WARNING("Serializing document without system principal");
264
}
265
#endif
266
267
tmp = aStream->WriteBoolean(mWasL10nCached);
268
if (NS_FAILED(tmp)) {
269
rv = tmp;
270
}
271
272
// mozilla::dom::NodeInfo table
273
nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos;
274
if (mRoot) {
275
tmp = GetNodeInfos(mRoot, nodeInfos);
276
if (NS_FAILED(tmp)) {
277
rv = tmp;
278
}
279
}
280
281
uint32_t nodeInfoCount = nodeInfos.Length();
282
tmp = aStream->Write32(nodeInfoCount);
283
if (NS_FAILED(tmp)) {
284
rv = tmp;
285
}
286
uint32_t i;
287
for (i = 0; i < nodeInfoCount; ++i) {
288
mozilla::dom::NodeInfo* nodeInfo = nodeInfos[i];
289
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_FAILURE);
290
291
nsAutoString namespaceURI;
292
nodeInfo->GetNamespaceURI(namespaceURI);
293
tmp = aStream->WriteWStringZ(namespaceURI.get());
294
if (NS_FAILED(tmp)) {
295
rv = tmp;
296
}
297
298
nsAutoString prefix;
299
nodeInfo->GetPrefix(prefix);
300
bool nullPrefix = DOMStringIsNull(prefix);
301
tmp = aStream->WriteBoolean(nullPrefix);
302
if (NS_FAILED(tmp)) {
303
rv = tmp;
304
}
305
if (!nullPrefix) {
306
tmp = aStream->WriteWStringZ(prefix.get());
307
if (NS_FAILED(tmp)) {
308
rv = tmp;
309
}
310
}
311
312
nsAutoString localName;
313
nodeInfo->GetName(localName);
314
tmp = aStream->WriteWStringZ(localName.get());
315
if (NS_FAILED(tmp)) {
316
rv = tmp;
317
}
318
}
319
320
// Now serialize the document contents
321
uint32_t count = mProcessingInstructions.Length();
322
for (i = 0; i < count; ++i) {
323
nsXULPrototypePI* pi = mProcessingInstructions[i];
324
tmp = pi->Serialize(aStream, this, &nodeInfos);
325
if (NS_FAILED(tmp)) {
326
rv = tmp;
327
}
328
}
329
330
if (mRoot) {
331
tmp = mRoot->Serialize(aStream, this, &nodeInfos);
332
if (NS_FAILED(tmp)) {
333
rv = tmp;
334
}
335
}
336
337
return rv;
338
}
339
340
//----------------------------------------------------------------------
341
//
342
343
nsresult nsXULPrototypeDocument::InitPrincipal(nsIURI* aURI,
344
nsIPrincipal* aPrincipal) {
345
NS_ENSURE_ARG_POINTER(aURI);
346
347
mURI = aURI;
348
mNodeInfoManager->SetDocumentPrincipal(aPrincipal);
349
return NS_OK;
350
}
351
352
nsIURI* nsXULPrototypeDocument::GetURI() {
353
NS_ASSERTION(mURI, "null URI");
354
return mURI;
355
}
356
357
nsXULPrototypeElement* nsXULPrototypeDocument::GetRootElement() {
358
return mRoot;
359
}
360
361
void nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement* aElement) {
362
mRoot = aElement;
363
}
364
365
nsresult nsXULPrototypeDocument::AddProcessingInstruction(
366
nsXULPrototypePI* aPI) {
367
MOZ_ASSERT(aPI, "null ptr");
368
if (!mProcessingInstructions.AppendElement(aPI)) {
369
return NS_ERROR_OUT_OF_MEMORY;
370
}
371
return NS_OK;
372
}
373
374
const nsTArray<RefPtr<nsXULPrototypePI>>&
375
nsXULPrototypeDocument::GetProcessingInstructions() const {
376
return mProcessingInstructions;
377
}
378
379
nsIPrincipal* nsXULPrototypeDocument::DocumentPrincipal() {
380
MOZ_ASSERT(mNodeInfoManager, "missing nodeInfoManager");
381
return mNodeInfoManager->DocumentPrincipal();
382
}
383
384
void nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal) {
385
mNodeInfoManager->SetDocumentPrincipal(aPrincipal);
386
}
387
388
void nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration) {
389
mCCGeneration = aCCGeneration;
390
}
391
392
nsNodeInfoManager* nsXULPrototypeDocument::GetNodeInfoManager() {
393
return mNodeInfoManager;
394
}
395
396
nsresult nsXULPrototypeDocument::AwaitLoadDone(Callback&& aCallback,
397
bool* aResult) {
398
nsresult rv = NS_OK;
399
400
*aResult = mLoaded;
401
402
if (!mLoaded) {
403
rv = mPrototypeWaiters.AppendElement(std::move(aCallback))
404
? NS_OK
405
: NS_ERROR_OUT_OF_MEMORY; // addrefs
406
}
407
408
return rv;
409
}
410
411
nsresult nsXULPrototypeDocument::NotifyLoadDone() {
412
// Call back to each XUL document that raced to start the same
413
// prototype document load, lost the race, but hit the XUL
414
// prototype cache because the winner filled the cache with
415
// the not-yet-loaded prototype object.
416
417
mLoaded = true;
418
419
for (uint32_t i = mPrototypeWaiters.Length(); i > 0;) {
420
--i;
421
mPrototypeWaiters[i]();
422
}
423
mPrototypeWaiters.Clear();
424
425
return NS_OK;
426
}
427
428
void nsXULPrototypeDocument::TraceProtos(JSTracer* aTrc) {
429
// Only trace the protos once per GC if we are marking.
430
if (aTrc->isMarkingTracer()) {
431
uint32_t currentGCNumber = aTrc->gcNumberForMarking();
432
if (mGCNumber == currentGCNumber) {
433
return;
434
}
435
mGCNumber = currentGCNumber;
436
}
437
438
if (mRoot) {
439
mRoot->TraceAllScripts(aTrc);
440
}
441
}
442
443
void nsXULPrototypeDocument::SetIsL10nCached() { mWasL10nCached = true; }
444
445
void nsXULPrototypeDocument::RebuildPrototypeFromElement(
446
nsXULPrototypeElement* aPrototype, Element* aElement, bool aDeep) {
447
aPrototype->mHasIdAttribute = aElement->HasID();
448
aPrototype->mHasClassAttribute = aElement->MayHaveClass();
449
aPrototype->mHasStyleAttribute = aElement->MayHaveStyle();
450
NodeInfo* oldNodeInfo = aElement->NodeInfo();
451
RefPtr<NodeInfo> newNodeInfo = mNodeInfoManager->GetNodeInfo(
452
oldNodeInfo->NameAtom(), oldNodeInfo->GetPrefixAtom(),
453
oldNodeInfo->NamespaceID(), nsINode::ELEMENT_NODE);
454
aPrototype->mNodeInfo = newNodeInfo;
455
456
// First replace the prototype attributes with the new ones from this element.
457
aPrototype->mAttributes.Clear();
458
459
uint32_t count = aElement->GetAttrCount();
460
nsXULPrototypeAttribute* protoAttr =
461
aPrototype->mAttributes.AppendElements(count);
462
for (uint32_t index = 0; index < count; index++) {
463
BorrowedAttrInfo attr = aElement->GetAttrInfoAt(index);
464
465
if (attr.mName->IsAtom()) {
466
protoAttr->mName.SetTo(attr.mName->Atom());
467
} else {
468
NodeInfo* oldNodeInfo = attr.mName->NodeInfo();
469
RefPtr<NodeInfo> newNodeInfo = mNodeInfoManager->GetNodeInfo(
470
oldNodeInfo->NameAtom(), oldNodeInfo->GetPrefixAtom(),
471
oldNodeInfo->NamespaceID(), nsINode::ATTRIBUTE_NODE);
472
protoAttr->mName.SetTo(newNodeInfo);
473
}
474
protoAttr->mValue.SetTo(*attr.mValue);
475
476
if (protoAttr->mName.Equals(nsGkAtoms::is)) {
477
aPrototype->mIsAtom = protoAttr->mValue.GetAtomValue();
478
}
479
480
protoAttr++;
481
}
482
483
if (aDeep) {
484
// We have to rebuild the prototype children from this element.
485
// First release the tree under this element.
486
aPrototype->ReleaseSubtree();
487
488
RefPtr<nsXULPrototypeNode>* children =
489
aPrototype->mChildren.AppendElements(aElement->GetChildCount());
490
for (nsIContent* child = aElement->GetFirstChild(); child;
491
child = child->GetNextSibling()) {
492
if (child->IsElement()) {
493
Element* element = child->AsElement();
494
RefPtr<nsXULPrototypeElement> elemProto = new nsXULPrototypeElement;
495
RebuildPrototypeFromElement(elemProto, element, true);
496
*children = elemProto;
497
} else if (child->IsText()) {
498
Text* text = child->AsText();
499
RefPtr<nsXULPrototypeText> textProto = new nsXULPrototypeText();
500
text->AppendTextTo(textProto->mValue);
501
*children = textProto;
502
} else {
503
MOZ_ASSERT(false, "We handle only elements and text nodes here.");
504
}
505
506
children++;
507
}
508
}
509
}
510
511
void nsXULPrototypeDocument::RebuildL10nPrototype(Element* aElement,
512
bool aDeep) {
513
if (mWasL10nCached) {
514
return;
515
}
516
517
Document* doc = aElement->OwnerDoc();
518
519
nsAutoString id;
520
MOZ_RELEASE_ASSERT(aElement->GetAttr(nsGkAtoms::datal10nid, id));
521
522
if (!doc) {
523
return;
524
}
525
526
RefPtr<nsXULPrototypeElement> proto = doc->mL10nProtoElements.Get(aElement);
527
MOZ_RELEASE_ASSERT(proto);
528
RebuildPrototypeFromElement(proto, aElement, aDeep);
529
}