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/. */
7 #include "nsXULPrototypeDocument.h"
8 #include "XULDocument.h"
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"
21 #include "jsfriendapi.h"
23 #include "nsIConsoleService.h"
24 #include "nsIScriptError.h"
25 #include "nsIDOMScriptObjectFactory.h"
27 #include "nsNodeInfoManager.h"
28 #include "nsContentUtils.h"
29 #include "nsCCUncollectableMarker.h"
30 #include "xpcpublic.h"
31 #include "mozilla/dom/BindingUtils.h"
33 using mozilla::dom::DestroyProtoAndIfaceCache
;
34 using mozilla::dom::XULDocument
;
36 uint32_t nsXULPrototypeDocument::gRefCnt
;
38 //----------------------------------------------------------------------
40 // ctors, dtors, n' stuff
43 nsXULPrototypeDocument::nsXULPrototypeDocument()
54 nsXULPrototypeDocument::Init()
56 mNodeInfoManager
= new nsNodeInfoManager();
57 NS_ENSURE_TRUE(mNodeInfoManager
, NS_ERROR_OUT_OF_MEMORY
);
59 return mNodeInfoManager
->Init(nullptr);
62 nsXULPrototypeDocument::~nsXULPrototypeDocument()
65 mRoot
->ReleaseSubtree();
68 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument
)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument
)
71 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters
)
72 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument
)
74 if (nsCCUncollectableMarker::InGeneration(cb
, tmp
->mCCGeneration
)) {
75 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
77 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager
)
79 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeWaiters
)
80 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
82 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument
)
83 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
84 NS_INTERFACE_MAP_ENTRY(nsISupports
)
87 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument
)
88 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument
)
91 NS_NewXULPrototypeDocument(nsXULPrototypeDocument
** aResult
)
93 *aResult
= new nsXULPrototypeDocument();
95 return NS_ERROR_OUT_OF_MEMORY
;
98 rv
= (*aResult
)->Init();
109 //----------------------------------------------------------------------
111 // nsISerializable methods
115 nsXULPrototypeDocument::Read(nsIObjectInputStream
* aStream
)
119 nsCOMPtr
<nsISupports
> supports
;
120 rv
= aStream
->ReadObject(true, getter_AddRefs(supports
));
121 mURI
= do_QueryInterface(supports
);
124 nsCOMPtr
<nsIURI
> styleOverlayURI
;
126 nsresult tmp
= aStream
->Read32(&count
);
127 if (NS_FAILED(tmp
)) {
134 for (i
= 0; i
< count
; ++i
) {
135 tmp
= aStream
->ReadObject(true, getter_AddRefs(supports
));
136 if (NS_FAILED(tmp
)) {
139 styleOverlayURI
= do_QueryInterface(supports
);
140 mStyleSheetReferences
.AppendObject(styleOverlayURI
);
144 // nsIPrincipal mNodeInfoManager->mPrincipal
145 nsCOMPtr
<nsIPrincipal
> principal
;
146 tmp
= aStream
->ReadObject(true, getter_AddRefs(supports
));
147 principal
= do_QueryInterface(supports
);
148 if (NS_FAILED(tmp
)) {
151 // Better safe than sorry....
152 mNodeInfoManager
->SetDocumentPrincipal(principal
);
154 mRoot
= new nsXULPrototypeElement();
156 return NS_ERROR_OUT_OF_MEMORY
;
158 // mozilla::dom::NodeInfo table
159 nsTArray
<nsRefPtr
<mozilla::dom::NodeInfo
>> nodeInfos
;
161 tmp
= aStream
->Read32(&count
);
162 if (NS_FAILED(tmp
)) {
165 nsAutoString namespaceURI
, prefixStr
, localName
;
167 nsCOMPtr
<nsIAtom
> prefix
;
168 for (i
= 0; i
< count
; ++i
) {
169 tmp
= aStream
->ReadString(namespaceURI
);
170 if (NS_FAILED(tmp
)) {
173 tmp
= aStream
->ReadBoolean(&prefixIsNull
);
174 if (NS_FAILED(tmp
)) {
180 tmp
= aStream
->ReadString(prefixStr
);
181 if (NS_FAILED(tmp
)) {
184 prefix
= do_GetAtom(prefixStr
);
186 tmp
= aStream
->ReadString(localName
);
187 if (NS_FAILED(tmp
)) {
191 nsRefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
192 // Using UINT16_MAX here as we don't know which nodeinfos will be
193 // used for attributes and which for elements. And that doesn't really
195 tmp
= mNodeInfoManager
->GetNodeInfo(localName
, prefix
, namespaceURI
,
197 getter_AddRefs(nodeInfo
));
198 if (NS_FAILED(tmp
)) {
201 nodeInfos
.AppendElement(nodeInfo
);
206 while (NS_SUCCEEDED(rv
)) {
207 tmp
= aStream
->Read32(&type
);
208 if (NS_FAILED(tmp
)) {
212 if ((nsXULPrototypeNode::Type
)type
== nsXULPrototypeNode::eType_PI
) {
213 nsRefPtr
<nsXULPrototypePI
> pi
= new nsXULPrototypePI();
215 rv
= NS_ERROR_OUT_OF_MEMORY
;
219 tmp
= pi
->Deserialize(aStream
, this, mURI
, &nodeInfos
);
220 if (NS_FAILED(tmp
)) {
223 tmp
= AddProcessingInstruction(pi
);
224 if (NS_FAILED(tmp
)) {
227 } else if ((nsXULPrototypeNode::Type
)type
== nsXULPrototypeNode::eType_Element
) {
228 tmp
= mRoot
->Deserialize(aStream
, this, mURI
, &nodeInfos
);
229 if (NS_FAILED(tmp
)) {
234 NS_NOTREACHED("Unexpected prototype node type");
235 rv
= NS_ERROR_FAILURE
;
239 tmp
= NotifyLoadDone();
240 if (NS_FAILED(tmp
)) {
248 GetNodeInfos(nsXULPrototypeElement
* aPrototype
,
249 nsTArray
<nsRefPtr
<mozilla::dom::NodeInfo
>>& aArray
)
251 if (aArray
.IndexOf(aPrototype
->mNodeInfo
) == aArray
.NoIndex
) {
252 aArray
.AppendElement(aPrototype
->mNodeInfo
);
257 for (i
= 0; i
< aPrototype
->mNumAttributes
; ++i
) {
258 nsRefPtr
<mozilla::dom::NodeInfo
> ni
;
259 nsAttrName
* name
= &aPrototype
->mAttributes
[i
].mName
;
260 if (name
->IsAtom()) {
261 ni
= aPrototype
->mNodeInfo
->NodeInfoManager()->
262 GetNodeInfo(name
->Atom(), nullptr, kNameSpaceID_None
,
263 nsIDOMNode::ATTRIBUTE_NODE
);
266 ni
= name
->NodeInfo();
269 if (aArray
.IndexOf(ni
) == aArray
.NoIndex
) {
270 aArray
.AppendElement(ni
);
275 for (i
= 0; i
< aPrototype
->mChildren
.Length(); ++i
) {
276 nsXULPrototypeNode
* child
= aPrototype
->mChildren
[i
];
277 if (child
->mType
== nsXULPrototypeNode::eType_Element
) {
279 GetNodeInfos(static_cast<nsXULPrototypeElement
*>(child
), aArray
);
280 NS_ENSURE_SUCCESS(rv
, rv
);
288 nsXULPrototypeDocument::Write(nsIObjectOutputStream
* aStream
)
292 rv
= aStream
->WriteCompoundObject(mURI
, NS_GET_IID(nsIURI
), true);
296 count
= mStyleSheetReferences
.Count();
297 nsresult tmp
= aStream
->Write32(count
);
298 if (NS_FAILED(tmp
)) {
303 for (i
= 0; i
< count
; ++i
) {
304 tmp
= aStream
->WriteCompoundObject(mStyleSheetReferences
[i
],
305 NS_GET_IID(nsIURI
), true);
306 if (NS_FAILED(tmp
)) {
311 // nsIPrincipal mNodeInfoManager->mPrincipal
312 tmp
= aStream
->WriteObject(mNodeInfoManager
->DocumentPrincipal(),
314 if (NS_FAILED(tmp
)) {
319 // XXX Worrisome if we're caching things without system principal.
320 if (!nsContentUtils::IsSystemPrincipal(mNodeInfoManager
->DocumentPrincipal())) {
321 NS_WARNING("Serializing document without system principal");
325 // mozilla::dom::NodeInfo table
326 nsTArray
<nsRefPtr
<mozilla::dom::NodeInfo
>> nodeInfos
;
328 tmp
= GetNodeInfos(mRoot
, nodeInfos
);
329 if (NS_FAILED(tmp
)) {
334 uint32_t nodeInfoCount
= nodeInfos
.Length();
335 tmp
= aStream
->Write32(nodeInfoCount
);
336 if (NS_FAILED(tmp
)) {
339 for (i
= 0; i
< nodeInfoCount
; ++i
) {
340 mozilla::dom::NodeInfo
*nodeInfo
= nodeInfos
[i
];
341 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_FAILURE
);
343 nsAutoString namespaceURI
;
344 nodeInfo
->GetNamespaceURI(namespaceURI
);
345 tmp
= aStream
->WriteWStringZ(namespaceURI
.get());
346 if (NS_FAILED(tmp
)) {
351 nodeInfo
->GetPrefix(prefix
);
352 bool nullPrefix
= DOMStringIsNull(prefix
);
353 tmp
= aStream
->WriteBoolean(nullPrefix
);
354 if (NS_FAILED(tmp
)) {
358 tmp
= aStream
->WriteWStringZ(prefix
.get());
359 if (NS_FAILED(tmp
)) {
364 nsAutoString localName
;
365 nodeInfo
->GetName(localName
);
366 tmp
= aStream
->WriteWStringZ(localName
.get());
367 if (NS_FAILED(tmp
)) {
372 // Now serialize the document contents
373 count
= mProcessingInstructions
.Length();
374 for (i
= 0; i
< count
; ++i
) {
375 nsXULPrototypePI
* pi
= mProcessingInstructions
[i
];
376 tmp
= pi
->Serialize(aStream
, this, &nodeInfos
);
377 if (NS_FAILED(tmp
)) {
383 tmp
= mRoot
->Serialize(aStream
, this, &nodeInfos
);
384 if (NS_FAILED(tmp
)) {
393 //----------------------------------------------------------------------
397 nsXULPrototypeDocument::InitPrincipal(nsIURI
* aURI
, nsIPrincipal
* aPrincipal
)
399 NS_ENSURE_ARG_POINTER(aURI
);
402 mNodeInfoManager
->SetDocumentPrincipal(aPrincipal
);
408 nsXULPrototypeDocument::GetURI()
410 NS_ASSERTION(mURI
, "null URI");
415 nsXULPrototypeElement
*
416 nsXULPrototypeDocument::GetRootElement()
423 nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement
* aElement
)
429 nsXULPrototypeDocument::AddProcessingInstruction(nsXULPrototypePI
* aPI
)
431 NS_PRECONDITION(aPI
, "null ptr");
432 if (!mProcessingInstructions
.AppendElement(aPI
)) {
433 return NS_ERROR_OUT_OF_MEMORY
;
438 const nsTArray
<nsRefPtr
<nsXULPrototypePI
> >&
439 nsXULPrototypeDocument::GetProcessingInstructions() const
441 return mProcessingInstructions
;
445 nsXULPrototypeDocument::AddStyleSheetReference(nsIURI
* aURI
)
447 NS_PRECONDITION(aURI
, "null ptr");
448 if (!mStyleSheetReferences
.AppendObject(aURI
)) {
449 NS_WARNING("mStyleSheetReferences->AppendElement() failed."
450 "Stylesheet overlay dropped.");
454 const nsCOMArray
<nsIURI
>&
455 nsXULPrototypeDocument::GetStyleSheetReferences() const
457 return mStyleSheetReferences
;
461 nsXULPrototypeDocument::GetHeaderData(nsIAtom
* aField
, nsAString
& aData
) const
463 // XXX Not implemented
470 nsXULPrototypeDocument::SetHeaderData(nsIAtom
* aField
, const nsAString
& aData
)
472 // XXX Not implemented
479 nsXULPrototypeDocument::DocumentPrincipal()
481 NS_PRECONDITION(mNodeInfoManager
, "missing nodeInfoManager");
482 return mNodeInfoManager
->DocumentPrincipal();
486 nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal
* aPrincipal
)
488 mNodeInfoManager
->SetDocumentPrincipal(aPrincipal
);
492 nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration
)
494 mCCGeneration
= aCCGeneration
;
498 nsXULPrototypeDocument::GetNodeInfoManager()
500 return mNodeInfoManager
;
505 nsXULPrototypeDocument::AwaitLoadDone(XULDocument
* aDocument
, bool* aResult
)
512 rv
= mPrototypeWaiters
.AppendElement(aDocument
)
513 ? NS_OK
: NS_ERROR_OUT_OF_MEMORY
; // addrefs
521 nsXULPrototypeDocument::NotifyLoadDone()
523 // Call back to each XUL document that raced to start the same
524 // prototype document load, lost the race, but hit the XUL
525 // prototype cache because the winner filled the cache with
526 // the not-yet-loaded prototype object.
532 for (uint32_t i
= mPrototypeWaiters
.Length(); i
> 0; ) {
534 // true means that OnPrototypeLoadDone will also
535 // call ResumeWalk().
536 rv
= mPrototypeWaiters
[i
]->OnPrototypeLoadDone(true);
537 if (NS_FAILED(rv
)) break;
539 mPrototypeWaiters
.Clear();
545 nsXULPrototypeDocument::TraceProtos(JSTracer
* aTrc
, uint32_t aGCNumber
)
547 // Only trace the protos once per GC.
548 if (mGCNumber
== aGCNumber
) {
552 mGCNumber
= aGCNumber
;
554 mRoot
->TraceAllScripts(aTrc
);