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