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/. */
6 #include "nsXULPrototypeDocument.h"
8 #include "nsXULElement.h"
10 #include "nsIObjectInputStream.h"
11 #include "nsIObjectOutputStream.h"
12 #include "nsIPrincipal.h"
13 #include "nsJSPrincipals.h"
14 #include "nsIScriptObjectPrincipal.h"
17 #include "jsfriendapi.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/CustomElementRegistry.h"
29 #include "mozilla/dom/Document.h"
30 #include "mozilla/dom/Element.h"
31 #include "mozilla/dom/Text.h"
33 using namespace mozilla
;
34 using namespace mozilla::dom
;
35 using mozilla::dom::DestroyProtoAndIfaceCache
;
37 uint32_t nsXULPrototypeDocument::gRefCnt
;
39 //----------------------------------------------------------------------
41 // ctors, dtors, n' stuff
44 nsXULPrototypeDocument::nsXULPrototypeDocument()
45 : mRoot(nullptr), mLoaded(false), mCCGeneration(0), mWasL10nCached(false) {
49 nsresult
nsXULPrototypeDocument::Init() {
50 mNodeInfoManager
= new nsNodeInfoManager(nullptr, nullptr);
54 nsXULPrototypeDocument::~nsXULPrototypeDocument() {
55 if (mRoot
) mRoot
->ReleaseSubtree();
58 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument
)
60 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument
)
61 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters
)
62 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument
)
64 if (nsCCUncollectableMarker::InGeneration(cb
, tmp
->mCCGeneration
)) {
65 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager
)
69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
71 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument
)
72 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
73 NS_INTERFACE_MAP_ENTRY(nsISupports
)
76 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument
)
77 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument
)
80 NS_NewXULPrototypeDocument(nsXULPrototypeDocument
** aResult
) {
82 RefPtr
<nsXULPrototypeDocument
> doc
= new nsXULPrototypeDocument();
84 nsresult rv
= doc
->Init();
93 //----------------------------------------------------------------------
95 // nsISerializable methods
99 nsXULPrototypeDocument::Read(nsIObjectInputStream
* aStream
) {
100 nsCOMPtr
<nsISupports
> supports
;
101 nsresult rv
= aStream
->ReadObject(true, getter_AddRefs(supports
));
105 mURI
= do_QueryInterface(supports
);
107 // nsIPrincipal mNodeInfoManager->mPrincipal
109 rv
= aStream
->ReadCString(JSON
);
113 nsCOMPtr
<nsIPrincipal
> principal
= mozilla::BasePrincipal::FromJSON(JSON
);
115 // Better safe than sorry....
116 mNodeInfoManager
->SetDocumentPrincipal(principal
);
118 rv
= aStream
->ReadBoolean(&mWasL10nCached
);
123 mRoot
= new nsXULPrototypeElement();
125 // mozilla::dom::NodeInfo table
126 nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>> nodeInfos
;
129 rv
= aStream
->Read32(&count
);
133 nsAutoString namespaceURI
, prefixStr
, localName
;
135 RefPtr
<nsAtom
> prefix
;
136 for (i
= 0; i
< count
; ++i
) {
137 rv
= aStream
->ReadString(namespaceURI
);
141 rv
= aStream
->ReadBoolean(&prefixIsNull
);
148 rv
= aStream
->ReadString(prefixStr
);
152 prefix
= NS_Atomize(prefixStr
);
154 rv
= aStream
->ReadString(localName
);
159 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
160 // Using UINT16_MAX here as we don't know which nodeinfos will be
161 // used for attributes and which for elements. And that doesn't really
163 rv
= mNodeInfoManager
->GetNodeInfo(localName
, prefix
, namespaceURI
,
164 UINT16_MAX
, getter_AddRefs(nodeInfo
));
168 nodeInfos
.AppendElement(nodeInfo
);
173 while (NS_SUCCEEDED(rv
)) {
174 rv
= aStream
->Read32(&type
);
180 if ((nsXULPrototypeNode::Type
)type
== nsXULPrototypeNode::eType_PI
) {
181 RefPtr
<nsXULPrototypePI
> pi
= new nsXULPrototypePI();
183 rv
= pi
->Deserialize(aStream
, this, mURI
, &nodeInfos
);
187 rv
= AddProcessingInstruction(pi
);
191 } else if ((nsXULPrototypeNode::Type
)type
==
192 nsXULPrototypeNode::eType_Element
) {
193 rv
= mRoot
->Deserialize(aStream
, this, mURI
, &nodeInfos
);
199 MOZ_ASSERT_UNREACHABLE("Unexpected prototype node type");
200 return NS_ERROR_FAILURE
;
204 return NotifyLoadDone();
207 static nsresult
GetNodeInfos(nsXULPrototypeElement
* aPrototype
,
208 nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>& aArray
) {
209 if (aArray
.IndexOf(aPrototype
->mNodeInfo
) == aArray
.NoIndex
) {
210 aArray
.AppendElement(aPrototype
->mNodeInfo
);
215 for (i
= 0; i
< aPrototype
->mAttributes
.Length(); ++i
) {
216 RefPtr
<mozilla::dom::NodeInfo
> ni
;
217 nsAttrName
* name
= &aPrototype
->mAttributes
[i
].mName
;
218 if (name
->IsAtom()) {
219 ni
= aPrototype
->mNodeInfo
->NodeInfoManager()->GetNodeInfo(
220 name
->Atom(), nullptr, kNameSpaceID_None
, nsINode::ATTRIBUTE_NODE
);
222 ni
= name
->NodeInfo();
225 if (aArray
.IndexOf(ni
) == aArray
.NoIndex
) {
226 aArray
.AppendElement(ni
);
231 for (i
= 0; i
< aPrototype
->mChildren
.Length(); ++i
) {
232 nsXULPrototypeNode
* child
= aPrototype
->mChildren
[i
];
233 if (child
->mType
== nsXULPrototypeNode::eType_Element
) {
235 GetNodeInfos(static_cast<nsXULPrototypeElement
*>(child
), aArray
);
236 NS_ENSURE_SUCCESS(rv
, rv
);
244 nsXULPrototypeDocument::Write(nsIObjectOutputStream
* aStream
) {
247 rv
= aStream
->WriteCompoundObject(mURI
, NS_GET_IID(nsIURI
), true);
249 // nsIPrincipal mNodeInfoManager->mPrincipal
251 mozilla::BasePrincipal::Cast(mNodeInfoManager
->DocumentPrincipal())
253 nsresult tmp
= aStream
->WriteStringZ(JSON
.get());
254 if (NS_FAILED(tmp
)) {
259 // XXX Worrisome if we're caching things without system principal.
260 if (!mNodeInfoManager
->DocumentPrincipal()->IsSystemPrincipal()) {
261 NS_WARNING("Serializing document without system principal");
265 tmp
= aStream
->WriteBoolean(mWasL10nCached
);
266 if (NS_FAILED(tmp
)) {
270 // mozilla::dom::NodeInfo table
271 nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>> nodeInfos
;
273 tmp
= GetNodeInfos(mRoot
, nodeInfos
);
274 if (NS_FAILED(tmp
)) {
279 uint32_t nodeInfoCount
= nodeInfos
.Length();
280 tmp
= aStream
->Write32(nodeInfoCount
);
281 if (NS_FAILED(tmp
)) {
285 for (i
= 0; i
< nodeInfoCount
; ++i
) {
286 mozilla::dom::NodeInfo
* nodeInfo
= nodeInfos
[i
];
287 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_FAILURE
);
289 nsAutoString namespaceURI
;
290 nodeInfo
->GetNamespaceURI(namespaceURI
);
291 tmp
= aStream
->WriteWStringZ(namespaceURI
.get());
292 if (NS_FAILED(tmp
)) {
297 nodeInfo
->GetPrefix(prefix
);
298 bool nullPrefix
= DOMStringIsNull(prefix
);
299 tmp
= aStream
->WriteBoolean(nullPrefix
);
300 if (NS_FAILED(tmp
)) {
304 tmp
= aStream
->WriteWStringZ(prefix
.get());
305 if (NS_FAILED(tmp
)) {
310 nsAutoString localName
;
311 nodeInfo
->GetName(localName
);
312 tmp
= aStream
->WriteWStringZ(localName
.get());
313 if (NS_FAILED(tmp
)) {
318 // Now serialize the document contents
319 uint32_t count
= mProcessingInstructions
.Length();
320 for (i
= 0; i
< count
; ++i
) {
321 nsXULPrototypePI
* pi
= mProcessingInstructions
[i
];
322 tmp
= pi
->Serialize(aStream
, this, &nodeInfos
);
323 if (NS_FAILED(tmp
)) {
329 tmp
= mRoot
->Serialize(aStream
, this, &nodeInfos
);
330 if (NS_FAILED(tmp
)) {
338 //----------------------------------------------------------------------
341 nsresult
nsXULPrototypeDocument::InitPrincipal(nsIURI
* aURI
,
342 nsIPrincipal
* aPrincipal
) {
343 NS_ENSURE_ARG_POINTER(aURI
);
346 mNodeInfoManager
->SetDocumentPrincipal(aPrincipal
);
350 nsIURI
* nsXULPrototypeDocument::GetURI() {
351 NS_ASSERTION(mURI
, "null URI");
355 nsXULPrototypeElement
* nsXULPrototypeDocument::GetRootElement() {
359 void nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement
* aElement
) {
363 nsresult
nsXULPrototypeDocument::AddProcessingInstruction(
364 nsXULPrototypePI
* aPI
) {
365 MOZ_ASSERT(aPI
, "null ptr");
366 // XXX(Bug 1631371) Check if this should use a fallible operation as it
367 // pretended earlier, or change the return type to void.
368 mProcessingInstructions
.AppendElement(aPI
);
372 const nsTArray
<RefPtr
<nsXULPrototypePI
>>&
373 nsXULPrototypeDocument::GetProcessingInstructions() const {
374 return mProcessingInstructions
;
377 nsIPrincipal
* nsXULPrototypeDocument::DocumentPrincipal() {
378 MOZ_ASSERT(mNodeInfoManager
, "missing nodeInfoManager");
379 return mNodeInfoManager
->DocumentPrincipal();
382 void nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal
* aPrincipal
) {
383 mNodeInfoManager
->SetDocumentPrincipal(aPrincipal
);
386 void nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration
) {
387 mCCGeneration
= aCCGeneration
;
390 nsNodeInfoManager
* nsXULPrototypeDocument::GetNodeInfoManager() {
391 return mNodeInfoManager
;
394 nsresult
nsXULPrototypeDocument::AwaitLoadDone(Callback
&& aCallback
,
401 // XXX(Bug 1631371) Check if this should use a fallible operation as it
402 // pretended earlier, or change the return type to void.
403 mPrototypeWaiters
.AppendElement(std::move(aCallback
));
409 nsresult
nsXULPrototypeDocument::NotifyLoadDone() {
410 // Call back to each XUL document that raced to start the same
411 // prototype document load, lost the race, but hit the XUL
412 // prototype cache because the winner filled the cache with
413 // the not-yet-loaded prototype object.
417 for (uint32_t i
= mPrototypeWaiters
.Length(); i
> 0;) {
419 mPrototypeWaiters
[i
]();
421 mPrototypeWaiters
.Clear();
426 void nsXULPrototypeDocument::SetIsL10nCached(bool aIsCached
) {
427 mWasL10nCached
= aIsCached
;
430 void nsXULPrototypeDocument::RebuildPrototypeFromElement(
431 nsXULPrototypeElement
* aPrototype
, Element
* aElement
, bool aDeep
) {
432 aPrototype
->mHasIdAttribute
= aElement
->HasID();
433 aPrototype
->mHasClassAttribute
= aElement
->MayHaveClass();
434 aPrototype
->mHasStyleAttribute
= aElement
->MayHaveStyle();
435 NodeInfo
* oldNodeInfo
= aElement
->NodeInfo();
436 RefPtr
<NodeInfo
> newNodeInfo
= mNodeInfoManager
->GetNodeInfo(
437 oldNodeInfo
->NameAtom(), oldNodeInfo
->GetPrefixAtom(),
438 oldNodeInfo
->NamespaceID(), nsINode::ELEMENT_NODE
);
439 aPrototype
->mNodeInfo
= newNodeInfo
;
441 // First replace the prototype attributes with the new ones from this element.
442 aPrototype
->mAttributes
.Clear();
444 uint32_t count
= aElement
->GetAttrCount();
445 nsXULPrototypeAttribute
* protoAttr
=
446 aPrototype
->mAttributes
.AppendElements(count
);
447 for (uint32_t index
= 0; index
< count
; index
++) {
448 BorrowedAttrInfo attr
= aElement
->GetAttrInfoAt(index
);
450 if (attr
.mName
->IsAtom()) {
451 protoAttr
->mName
.SetTo(attr
.mName
->Atom());
453 NodeInfo
* oldNodeInfo
= attr
.mName
->NodeInfo();
454 RefPtr
<NodeInfo
> newNodeInfo
= mNodeInfoManager
->GetNodeInfo(
455 oldNodeInfo
->NameAtom(), oldNodeInfo
->GetPrefixAtom(),
456 oldNodeInfo
->NamespaceID(), nsINode::ATTRIBUTE_NODE
);
457 protoAttr
->mName
.SetTo(newNodeInfo
);
459 protoAttr
->mValue
.SetTo(*attr
.mValue
);
464 // Make sure the mIsAtom is correct in case this prototype element has been
465 // completely rebuilt.
466 CustomElementData
* ceData
= aElement
->GetCustomElementData();
467 nsAtom
* isAtom
= ceData
? ceData
->GetIs(aElement
) : nullptr;
468 aPrototype
->mIsAtom
= isAtom
;
471 // We have to rebuild the prototype children from this element.
472 // First release the tree under this element.
473 aPrototype
->ReleaseSubtree();
475 RefPtr
<nsXULPrototypeNode
>* children
=
476 aPrototype
->mChildren
.AppendElements(aElement
->GetChildCount());
477 for (nsIContent
* child
= aElement
->GetFirstChild(); child
;
478 child
= child
->GetNextSibling()) {
479 if (child
->IsElement()) {
480 Element
* element
= child
->AsElement();
481 RefPtr
<nsXULPrototypeElement
> elemProto
= new nsXULPrototypeElement
;
482 RebuildPrototypeFromElement(elemProto
, element
, true);
483 *children
= elemProto
;
484 } else if (child
->IsText()) {
485 Text
* text
= child
->AsText();
486 RefPtr
<nsXULPrototypeText
> textProto
= new nsXULPrototypeText();
487 text
->AppendTextTo(textProto
->mValue
);
488 *children
= textProto
;
490 MOZ_ASSERT(false, "We handle only elements and text nodes here.");
498 void nsXULPrototypeDocument::RebuildL10nPrototype(Element
* aElement
,
500 if (mWasL10nCached
) {
504 MOZ_ASSERT(aElement
->HasAttr(nsGkAtoms::datal10nid
));
506 Document
* doc
= aElement
->OwnerDoc();
507 if (RefPtr
<nsXULPrototypeElement
> proto
=
508 doc
->mL10nProtoElements
.Get(aElement
)) {
509 RebuildPrototypeFromElement(proto
, aElement
, aDeep
);